source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/tests/test_browser.py @ 12986

Last change on this file since 12986 was 12825, checked in by Henrik Bettermann, 10 years ago

Fix template and add test.

  • Property svn:keywords set to Id
File size: 81.0 KB
Line 
1## $Id: test_browser.py 12825 2015-03-24 12:45:18Z henrik $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Test the customer-related UI components.
20"""
21import shutil
22import tempfile
23import logging
24import base64
25from decimal import Decimal
26from datetime import datetime, timedelta, date
27from StringIO import StringIO
28import os
29import grok
30from zc.async.testing import wait_for_result
31from zope.event import notify
32from zope.component import createObject, queryUtility, getUtility
33from zope.component.hooks import setSite, clearSite
34from zope.schema.interfaces import ConstraintNotSatisfied
35from zope.catalog.interfaces import ICatalog
36from zope.security.interfaces import Unauthorized
37from zope.securitypolicy.interfaces import IPrincipalRoleManager
38from zope.testbrowser.testing import Browser
39from hurry.workflow.interfaces import (
40    IWorkflowInfo, IWorkflowState, InvalidTransitionError)
41from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
42from waeup.ikoba.app import Company
43from waeup.ikoba.interfaces import (
44    IUserAccount, IJobManager, APPROVED, SUBMITTED,
45    IFileStoreNameChooser, NotIdValue)
46from waeup.ikoba.imagestorage import ExtFileStore
47from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
48from waeup.ikoba.interfaces import VERIFIED
49from waeup.ikoba.browser.tests.test_pdf import samples_dir
50from waeup.ikoba.products.productoptions import ProductOption
51from waeup.ikoba.customers.interfaces import CurrencyMismatch
52from waeup.ikoba.payments.payment import Payment
53from waeup.ikoba.payments.interfaces import IPayer, IPayable, STATE_PAID
54
55PH_LEN = 15911  # Length of placeholder file
56
57SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
58SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
59SAMPLE_PDF = os.path.join(os.path.dirname(__file__), 'test_pdf.pdf')
60
61
62def lookup_submit_value(name, value, browser):
63    """Find a button with a certain value."""
64    for num in range(0, 100):
65        try:
66            button = browser.getControl(name=name, index=num)
67            if button.value.endswith(value):
68                return button
69        except IndexError:
70            break
71    return None
72
73
74class CustomersFullSetup(FunctionalTestCase):
75    # A test case that only contains a setup and teardown
76    #
77    # Complete setup for customers handlings is rather complex and
78    # requires lots of things created before we can start. This is a
79    # setup that does all this, creates a company etc.
80    # so that we do not have to bother with that in different
81    # test cases.
82
83    layer = FunctionalLayer
84
85    def setup_customizable_params(self):
86        self._contract_category = u'sample'
87        self._document_factory = 'waeup.CustomerSampleDocument'
88        self._contract_factory = 'waeup.SampleContract'
89        return
90
91    def setUp(self):
92        super(CustomersFullSetup, self).setUp()
93        self.setup_customizable_params()
94        # Setup a sample site for each test
95        app = Company()
96        self.dc_root = tempfile.mkdtemp()
97        app['datacenter'].setStoragePath(self.dc_root)
98
99        # Prepopulate the ZODB...
100        self.getRootFolder()['app'] = app
101        # we add the site immediately after creation to the
102        # ZODB. Catalogs and other local utilities are not setup
103        # before that step.
104        self.app = self.getRootFolder()['app']
105        # Set site here. Some of the following setup code might need
106        # to access grok.getSite() and should get our new app then
107        setSite(app)
108
109        # Add some products
110        self.product = createObject('waeup.Product')
111        self.product.product_id = u'SAM'
112        self.product.title = u'Our Sample Product'
113        self.product.contract_category = self._contract_category
114        self.product.valid_from = date(2014, 12, 4)
115        self.product.tc_dict = {'en': u'Hello world'}
116        prodoption = ProductOption()
117        prodoption.title = u'First option'
118        prodoption.fee = Decimal('99.9')
119        prodoption.currency = 'USD'
120        self.product.options = [prodoption, ]
121        self.app['products'].addProduct(self.product)
122
123        # Add customer with subobjects
124        customer = createObject('waeup.Customer')
125        customer.firstname = u'Anna'
126        customer.lastname = u'Tester'
127        customer.reg_number = u'123'
128        customer.sex = u'm'
129        customer.email = u'aa@aa.ng'
130        customer.phone = u'1234'
131        customer.date_of_birth = date(1981, 2, 4)
132        self.app['customers'].addCustomer(customer)
133        self.customer_id = customer.customer_id
134        self.customer = self.app['customers'][self.customer_id]
135        self.document = createObject(self._document_factory)
136        self.document.title = u'My first document'
137        self.document.document_id = u'DOC1'
138        self.assertRaises(
139            NotIdValue, setattr, self.document, 'document_id',
140            u'id with spaces')
141        self.customer['documents'].addDocument(self.document)
142        self.contract = createObject(self._contract_factory)
143        self.contract.contract_id = u'CON1'
144        self.assertRaises(
145            NotIdValue, setattr, self.contract, 'contract_id',
146            u'id with spaces')
147        self.customer['contracts'].addContract(self.contract)
148
149        # Set password
150        IUserAccount(
151            self.app['customers'][self.customer_id]).setPassword('cpwd')
152
153        self.login_path = 'http://localhost/app/login'
154        self.container_path = 'http://localhost/app/customers'
155        self.manage_container_path = self.container_path + '/@@manage'
156        self.add_customer_path = self.container_path + '/addcustomer'
157        self.customer_path = self.container_path + '/' + self.customer_id
158        self.manage_customer_path = self.customer_path + '/manage_base'
159        self.trigtrans_path = self.customer_path + '/trigtrans'
160        self.history_path = self.customer_path + '/history'
161        self.documents_path = self.customer_path + '/documents'
162        self.contracts_path = self.customer_path + '/contracts'
163
164        # Update the catalog
165        notify(grok.ObjectModifiedEvent(self.customer))
166
167        # Put the prepopulated site into test ZODB and prepare test
168        # browser
169        self.browser = Browser()
170        self.browser.handleErrors = False
171
172    def tearDown(self):
173        super(CustomersFullSetup, self).tearDown()
174        clearSite()
175        shutil.rmtree(self.dc_root)
176
177
178class CustomersContainerUITests(CustomersFullSetup):
179    # Tests for CustomersContainer class views and pages
180
181    layer = FunctionalLayer
182
183    def test_anonymous_access(self):
184        # Anonymous users can't access customers containers
185        self.assertRaises(
186            Unauthorized, self.browser.open, self.container_path)
187        self.assertRaises(
188            Unauthorized, self.browser.open, self.manage_container_path)
189        return
190
191    def test_manage_access(self):
192        # Managers can access the view page of customers
193        # containers and can perform actions
194        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
195        self.browser.open(self.container_path)
196        self.assertEqual(self.browser.headers['Status'], '200 Ok')
197        self.assertEqual(self.browser.url, self.container_path)
198        self.browser.getLink("Manage customer section").click()
199        self.assertEqual(self.browser.headers['Status'], '200 Ok')
200        self.assertEqual(self.browser.url, self.manage_container_path)
201        return
202
203    def test_add_search_delete_customers(self):
204        # Managers can add search and remove customers
205        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
206        self.browser.open(self.manage_container_path)
207        self.browser.getLink("Add customer").click()
208        self.assertEqual(self.browser.headers['Status'], '200 Ok')
209        self.assertEqual(self.browser.url, self.add_customer_path)
210        self.browser.getControl(name="form.firstname").value = 'Bob'
211        self.browser.getControl(name="form.lastname").value = 'Tester'
212        self.browser.getControl(name="form.reg_number").value = '123'
213        self.browser.getControl("Create customer record").click()
214        self.assertTrue('Registration number in use.'
215            in self.browser.contents)
216        self.browser.getControl(name="form.reg_number").value = '1234'
217        self.browser.getControl("Create customer").click()
218        self.assertTrue('Customer created' in self.browser.contents)
219
220        # Registration must be unique
221        self.browser.getLink("Manage").click()
222        self.browser.getControl(name="form.reg_number").value = '123'
223        self.browser.getControl("Save").click()
224        self.assertMatches('...Registration number in use...',
225                           self.browser.contents)
226
227        # We can find a customer with a certain customer_id
228        self.browser.open(self.container_path)
229        self.browser.getControl("Find customer(s)").click()
230        self.assertTrue('Empty search string' in self.browser.contents)
231        self.browser.getControl(name="searchtype").value = ['customer_id']
232        self.browser.getControl(name="searchterm").value = self.customer_id
233        self.browser.getControl("Find customer(s)").click()
234        self.assertTrue('Anna Tester' in self.browser.contents)
235
236        # We can find a customer by searching for all kind of name parts
237        self.browser.open(self.manage_container_path)
238        self.browser.getControl("Find customer(s)").click()
239        self.assertTrue('Empty search string' in self.browser.contents)
240        self.browser.getControl(name="searchtype").value = ['fullname']
241        self.browser.getControl(name="searchterm").value = 'Anna Tester'
242        self.browser.getControl("Find customer(s)").click()
243        self.assertTrue('Anna Tester' in self.browser.contents)
244        self.browser.open(self.manage_container_path)
245        self.browser.getControl(name="searchtype").value = ['fullname']
246        self.browser.getControl(name="searchterm").value = 'Anna'
247        self.browser.getControl("Find customer(s)").click()
248        self.assertTrue('Anna Tester' in self.browser.contents)
249        self.browser.open(self.manage_container_path)
250        self.browser.getControl(name="searchtype").value = ['fullname']
251        self.browser.getControl(name="searchterm").value = 'Tester'
252        self.browser.getControl("Find customer(s)").click()
253        self.assertTrue('Anna Tester' in self.browser.contents)
254        self.browser.open(self.manage_container_path)
255        self.browser.getControl(name="searchtype").value = ['fullname']
256        self.browser.getControl(name="searchterm").value = 'An'
257        self.browser.getControl("Find customer(s)").click()
258        self.assertFalse('Anna Tester' in self.browser.contents)
259        self.browser.open(self.manage_container_path)
260        self.browser.getControl(name="searchtype").value = ['fullname']
261        self.browser.getControl(name="searchterm").value = 'An*'
262        self.browser.getControl("Find customer(s)").click()
263        self.assertTrue('Anna Tester' in self.browser.contents)
264        self.browser.open(self.manage_container_path)
265        self.browser.getControl(name="searchtype").value = ['fullname']
266        self.browser.getControl(name="searchterm").value = 'tester'
267        self.browser.getControl("Find customer(s)").click()
268        self.assertTrue('Anna Tester' in self.browser.contents)
269        self.browser.open(self.manage_container_path)
270        self.browser.getControl(name="searchtype").value = ['fullname']
271        self.browser.getControl(name="searchterm").value = 'Tester Ana'
272        self.browser.getControl("Find customer(s)").click()
273        self.assertFalse('Anna Tester' in self.browser.contents)
274        self.browser.open(self.manage_container_path)
275        self.browser.getControl(name="searchtype").value = ['fullname']
276        self.browser.getControl(name="searchterm").value = 'Tester Anna'
277        self.browser.getControl("Find customer(s)").click()
278        self.assertTrue('Anna Tester' in self.browser.contents)
279        # The old searchterm will be used again
280        self.browser.getControl("Find customer(s)").click()
281        self.assertTrue('Anna Tester' in self.browser.contents)
282
283        # We can find suspended customers
284        self.customer.suspended = True
285        notify(grok.ObjectModifiedEvent(self.customer))
286        self.browser.open(self.manage_container_path)
287        self.browser.getControl(name="searchtype").value = ['suspended']
288        self.browser.getControl("Find customer(s)").click()
289        self.assertTrue('Anna Tester' in self.browser.contents)
290
291        # Removed customers won't be found
292        ctrl = self.browser.getControl(name='entries')
293        ctrl.getControl(value=self.customer_id).selected = True
294        self.browser.getControl("Remove selected", index=0).click()
295        self.assertTrue('Successfully removed' in self.browser.contents)
296        self.browser.getControl(name="searchtype").value = ['customer_id']
297        self.browser.getControl(name="searchterm").value = self.customer_id
298        self.browser.getControl("Find customer(s)").click()
299        self.assertTrue('No customer found' in self.browser.contents)
300        return
301
302
303class OfficerUITests(CustomersFullSetup):
304    # Tests for Customer class views and pages
305
306    def setup_logging(self):
307        # setup a log-handler that catches all fake mailer output
308        self.stream = StringIO()
309        handler = logging.StreamHandler(self.stream)
310        logger = logging.getLogger('test.smtp')
311        logger.addHandler(handler)
312        logger.setLevel(logging.INFO)
313        return
314
315    def get_fake_smtp_output(self):
316        # get output generated by fake mailer
317        self.stream.flush()
318        self.stream.seek(0)
319        return self.stream.read()
320
321    def teardown_logging(self):
322        # remove the log handler for fake mailer output
323        logger = logging.getLogger('test.smtp')
324        handlers = [x for x in logger.handlers]
325        for handler in handlers:
326            logger.removeHandler(handler)
327        return
328
329    def test_basic_auth(self):
330        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
331        self.browser.open('http://localhost/app')
332        self.browser.getLink("Logout").click()
333        self.assertTrue('You have been logged out' in self.browser.contents)
334        # But we are still logged in since we've used basic
335        # authentication here.  Wikipedia says: Existing browsers
336        # retain authentication information until the tab or browser
337        # is closed or the user clears the history.  HTTP does not
338        # provide a method for a server to direct clients to discard
339        # these cached credentials. This means that there is no
340        # effective way for a server to "log out" the user without
341        # closing the browser. This is a significant defect that
342        # requires browser manufacturers to support a "logout" user
343        # interface element ...
344        self.assertTrue('Manager' in self.browser.contents)
345
346    def test_basic_auth_base64(self):
347        auth_token = base64.b64encode('mgr:mgrpw')
348        self.browser.addHeader('Authorization', 'Basic %s' % auth_token)
349        self.browser.open(self.manage_container_path)
350        self.assertEqual(self.browser.headers['Status'], '200 Ok')
351
352    def test_manage_access(self):
353        # Managers can access the pages of customers
354        # and can perform actions
355        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
356        self.browser.open(self.customer_path)
357        self.assertEqual(self.browser.headers['Status'], '200 Ok')
358        self.assertEqual(self.browser.url, self.customer_path)
359        self.browser.getLink("Transition").click()
360        self.assertEqual(self.browser.headers['Status'], '200 Ok')
361        # Managers can trigger transitions
362        self.browser.getControl(name="transition").value = ['start']
363        self.browser.getControl("Apply").click()
364        # Managers can edit base
365        self.browser.open(self.customer_path)
366        self.browser.getLink("Manage").click()
367        self.assertEqual(self.browser.url, self.manage_customer_path)
368        self.assertEqual(self.browser.headers['Status'], '200 Ok')
369        self.browser.getControl(name="form.firstname").value = 'John'
370        self.browser.getControl(name="form.lastname").value = 'Tester'
371        self.browser.getControl(name="form.reg_number").value = '345'
372        self.browser.getControl(name="password").value = 'secret'
373        self.browser.getControl(name="control_password").value = 'secret'
374        self.browser.getControl("Save").click()
375        self.assertMatches('...Form has been saved...',
376                           self.browser.contents)
377
378    def test_manage_contact_customer(self):
379        # Managers can contact customer
380        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
381        self.browser.open(self.customer_path)
382        self.browser.getLink("Send email").click()
383        self.browser.getControl(
384            name="form.subject").value = 'Important subject'
385        self.browser.getControl(name="form.body").value = 'Hello!'
386        self.browser.getControl("Send message now").click()
387        self.assertTrue('Your message has been sent' in self.browser.contents)
388        return
389
390    def test_manage_upload_passport(self):
391        # Managers can upload a file via the CustomerBaseManageFormPage
392        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
393        self.browser.open(self.manage_customer_path)
394        image = open(SAMPLE_IMAGE_BMP, 'rb')
395        ctrl = self.browser.getControl(name='passportmanageupload')
396        file_ctrl = ctrl.mech_control
397        file_ctrl.add_file(image, filename='my_photo.bmp')
398        self.browser.getControl(
399            name='upload_passportmanageupload').click()
400        self.assertTrue('jpg file format expected'
401            in self.browser.contents)
402        ctrl = self.browser.getControl(name='passportmanageupload')
403        file_ctrl = ctrl.mech_control
404        image = open(SAMPLE_IMAGE, 'rb')
405        file_ctrl.add_file(image, filename='my_photo.jpg')
406        self.browser.getControl(
407            name='upload_passportmanageupload').click()
408        self.assertTrue(
409            'src="http://localhost/app/customers/K1000000/passport.jpg"'
410            in self.browser.contents)
411        # We remove the passport file again
412        self.browser.open(self.manage_customer_path)
413        self.browser.getControl('Delete').click()
414        self.assertTrue('passport.jpg deleted' in self.browser.contents)
415
416    def test_manage_workflow_send_transition_information(self):
417        # Managers can pass through the whole workflow
418        config = grok.getSite()['configuration']
419        config.email_notification = True
420        self.setup_logging()
421        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
422        self.customer = self.app['customers'][self.customer_id]
423        self.browser.open(self.trigtrans_path)
424        self.browser.getControl(name="transition").value = ['start']
425        self.browser.getControl("Apply").click()
426        self.browser.open(self.trigtrans_path)
427        self.browser.getControl(name="transition").value = ['request']
428        self.browser.getControl("Apply").click()
429        self.browser.open(self.trigtrans_path)
430        self.browser.getControl(name="transition").value = ['reject']
431        self.browser.getControl("Apply").click()
432        # The customer is being notified by email after rejection
433        expected = [
434            u'Sending email from no-reply@waeup.org to aa@aa.ng:',
435            u'Message:', u'msg: MIME-Version: 1.0',
436            u'msg: Content-Type: text/plain; charset="us-ascii"',
437            u'msg: Content-Transfer-Encoding: 7bit',
438            u'msg: From: Administrator <no-reply@waeup.org>',
439            u'msg: To: Anna Tester <aa@aa.ng>',
440            u'msg: Reply-To: Administrator <contact@waeup.org>',
441            u'msg: Subject: Ikoba status change information',
442            u'msg: ',
443            u'msg: Dear Anna Tester,',
444            u'msg: ',
445            u'msg: The status of the following object has been changed:',
446            u'msg: ', u'msg: Object Id: K1000000',
447            u'msg: Title: Anna Tester',
448            u'msg: Transition: customer registration rejected',
449            u'msg: New state: started',
450            u'msg: ',
451            u'msg: Regards,',
452            u'msg: ',
453            u'msg: Manager',
454            u'msg: ',
455            u'msg: --',
456            u'msg: Sample Company',
457            u'msg: ',
458            u'']
459        self.assertEqual(expected, self.get_fake_smtp_output().split('\n'))
460        self.browser.open(self.trigtrans_path)
461        self.browser.getControl(name="transition").value = ['request']
462        self.browser.getControl("Apply").click()
463        self.browser.open(self.trigtrans_path)
464        self.browser.getControl(name="transition").value = [
465            'approve_provisionally']
466        self.browser.getControl("Apply").click()
467        self.browser.open(self.trigtrans_path)
468        self.browser.getControl(name="transition").value = ['approve_finally']
469        self.browser.getControl("Apply").click()
470        self.browser.open(self.trigtrans_path)
471        self.browser.getControl(name="transition").value = ['reset1']
472        self.browser.getControl("Apply").click()
473        self.teardown_logging()
474        return
475
476    def test_manage_import(self):
477        # Managers can import customer data files
478        datacenter_path = 'http://localhost/app/datacenter'
479        # Prepare a csv file for customers
480        open('customers.csv', 'wb').write(
481"""firstname,lastname,reg_number,date_of_birth,email,phone,sex,password
482Aaren,Pieri,1,1990-01-02,bb@aa.ng,1234,m,mypwd1
483Claus,Finau,2,1990-01-03,cc@aa.ng,1234,m,mypwd1
484Brit,Berson,2,1990-01-04,dd@aa.ng,1234,m,mypwd1
485""")
486        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
487        self.browser.open(datacenter_path)
488        self.browser.getLink('Upload data').click()
489        filecontents = StringIO(open('customers.csv', 'rb').read())
490        filewidget = self.browser.getControl(name='uploadfile:file')
491        filewidget.add_file(filecontents, 'text/plain', 'customers.csv')
492        self.browser.getControl(name='SUBMIT').click()
493        self.browser.getLink('Process data').click()
494        button = lookup_submit_value(
495            'select', 'customers_zope.mgr.csv', self.browser)
496        button.click()
497        importerselect = self.browser.getControl(name='importer')
498        modeselect = self.browser.getControl(name='mode')
499        importerselect.getControl('Customer Processor').selected = True
500        modeselect.getControl(value='create').selected = True
501        self.browser.getControl('Proceed to step 3').click()
502        self.assertTrue('Header fields OK' in self.browser.contents)
503        self.browser.getControl('Perform import').click()
504        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
505        self.assertTrue(
506            'Successfully processed 2 rows' in self.browser.contents)
507        self.assertTrue('Batch processing finished' in self.browser.contents)
508
509        # The customers are properly indexed and we can
510        # thus find a customer in  the department
511        self.browser.open(self.manage_container_path)
512        # We can search for a new customer by name ...
513        self.browser.getControl(name="searchtype").value = ['fullname']
514        self.browser.getControl(name="searchterm").value = 'Claus'
515        self.browser.getControl("Find customer(s)").click()
516        self.assertTrue('Claus Finau' in self.browser.contents)
517        # ... and check if the imported password has been properly set
518        ctrl = self.browser.getControl(name='entries')
519        value = ctrl.options[0]
520        claus = self.app['customers'][value]
521        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
522        return
523
524    def test_activate_deactivate_buttons(self):
525        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
526        self.browser.open(self.customer_path)
527        self.browser.getLink("Deactivate").click()
528        self.assertTrue(
529            'Customer account has been deactivated.' in self.browser.contents)
530        self.assertTrue(
531            'Base Data (account deactivated)' in self.browser.contents)
532        self.assertTrue(self.customer.suspended)
533        self.browser.getLink("Activate").click()
534        self.assertTrue(
535            'Customer account has been activated.' in self.browser.contents)
536        self.assertFalse(
537            'Base Data (account deactivated)' in self.browser.contents)
538        self.assertFalse(self.customer.suspended)
539        # History messages have been added ...
540        self.browser.getLink("History").click()
541        self.assertTrue(
542            'Customer account deactivated by Manager<br />'
543            in self.browser.contents)
544        self.assertTrue(
545            'Customer account activated by Manager<br />'
546            in self.browser.contents)
547        # ... and actions have been logged.
548        logfile = os.path.join(
549            self.app['datacenter'].storage, 'logs', 'customers.log')
550        logcontent = open(logfile).read()
551        self.assertTrue(
552            'zope.mgr - customers.browser.CustomerDeactivatePage - '
553            'K1000000 - account deactivated' in logcontent)
554        self.assertTrue(
555            'zope.mgr - customers.browser.CustomerActivatePage - '
556            'K1000000 - account activated' in logcontent)
557
558    def test_login_as_customer(self):
559        # CustomerImpersonators can login as customer
560        self.app['users'].addUser('mrofficer', 'mrofficersecret')
561        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
562        self.app['users']['mrofficer'].title = 'Harry Actor'
563        prmglobal = IPrincipalRoleManager(self.app)
564        prmglobal.assignRoleToPrincipal(
565            'waeup.CustomerImpersonator', 'mrofficer')
566        prmglobal.assignRoleToPrincipal('waeup.CustomersManager', 'mrofficer')
567        self.assertEqual(self.customer.state, 'created')
568        # Login as customer impersonator
569        self.browser.open(self.login_path)
570        self.browser.getControl(name="form.login").value = 'mrofficer'
571        self.browser.getControl(name="form.password").value = 'mrofficersecret'
572        self.browser.getControl("Login").click()
573        self.assertMatches('...You logged in...', self.browser.contents)
574        self.browser.open(self.customer_path)
575        self.browser.getLink("Login as").click()
576        self.browser.getControl("Set password now").click()
577        temp_password = self.browser.getControl(name='form.password').value
578        self.browser.getControl("Login now").click()
579        self.assertMatches(
580            '...You successfully logged in as...', self.browser.contents)
581        # Status has changed
582        self.assertEqual(self.customer.state, 'started')
583        # We are logged in as customer and can see the 'My Data' tab
584        self.assertMatches(
585            '...<a href="#" class="dropdown-toggle"'
586            ' data-toggle="dropdown">...',
587            self.browser.contents)
588        self.assertMatches(
589            '...My Data...',
590            self.browser.contents)
591        self.browser.getLink("Logout").click()
592        # The customer can't login with the original password ...
593        self.browser.open(self.login_path)
594        self.browser.getControl(name="form.login").value = self.customer_id
595        self.browser.getControl(name="form.password").value = 'cpwd'
596        self.browser.getControl("Login").click()
597        self.assertMatches(
598            '...Your account has been temporarily deactivated...',
599            self.browser.contents)
600        # ... but with the temporary password
601        self.browser.open(self.login_path)
602        self.browser.getControl(name="form.login").value = self.customer_id
603        self.browser.getControl(name="form.password").value = temp_password
604        self.browser.getControl("Login").click()
605        self.assertMatches('...You logged in...', self.browser.contents)
606        # Creation of temp_password is properly logged
607        logfile = os.path.join(
608            self.app['datacenter'].storage, 'logs', 'customers.log')
609        logcontent = open(logfile).read()
610        self.assertTrue(
611            'mrofficer - customers.browser.LoginAsCustomerStep1 - K1000000 - '
612            'temp_password generated: %s' % temp_password in logcontent)
613
614
615class CustomerUITests(CustomersFullSetup):
616    # Tests for Customer class views and pages
617
618    def test_customer_login_with_email(self):
619        self.assertEqual(self.customer.state, 'created')
620        self.browser.open(self.login_path)
621        self.browser.getControl(name="form.login").value = self.customer.email
622        self.browser.getControl(name="form.password").value = 'cpwd'
623        self.browser.getControl("Login").click()
624        self.assertEqual(self.browser.url, self.customer_path)
625        self.assertTrue('You logged in' in self.browser.contents)
626        # Status has changed
627        self.assertEqual(self.customer.state, 'started')
628        return
629
630    def test_customer_change_password(self):
631        # Customers can change the password
632        self.assertEqual(self.customer.state, 'created')
633        self.customer.personal_updated = datetime.utcnow()
634        self.browser.open(self.login_path)
635        self.browser.getControl(name="form.login").value = self.customer_id
636        self.browser.getControl(name="form.password").value = 'cpwd'
637        self.browser.getControl("Login").click()
638        self.assertEqual(self.browser.url, self.customer_path)
639        self.assertTrue('You logged in' in self.browser.contents)
640        # Status has changed
641        self.assertEqual(self.customer.state, 'started')
642        # Change password
643        self.browser.getLink("Change password").click()
644        self.browser.getControl(name="change_password").value = 'pw'
645        self.browser.getControl(
646            name="change_password_repeat").value = 'pw'
647        self.browser.getControl("Save").click()
648        self.assertTrue('Password must have at least' in self.browser.contents)
649        self.browser.getControl(name="change_password").value = 'new_password'
650        self.browser.getControl(
651            name="change_password_repeat").value = 'new_passssword'
652        self.browser.getControl("Save").click()
653        self.assertTrue('Passwords do not match' in self.browser.contents)
654        self.browser.getControl(name="change_password").value = 'new_password'
655        self.browser.getControl(
656            name="change_password_repeat").value = 'new_password'
657        self.browser.getControl("Save").click()
658        self.assertTrue('Password changed' in self.browser.contents)
659        # We are still logged in. Changing the password hasn't thrown us out.
660        self.browser.getLink("Base Data").click()
661        self.assertEqual(self.browser.url, self.customer_path)
662        # We can logout
663        self.browser.getLink("Logout").click()
664        self.assertTrue('You have been logged out' in self.browser.contents)
665        self.assertEqual(self.browser.url, 'http://localhost/app/index')
666        # We can login again with the new password
667        self.browser.getLink("Login").click()
668        self.browser.open(self.login_path)
669        self.browser.getControl(name="form.login").value = self.customer_id
670        self.browser.getControl(name="form.password").value = 'new_password'
671        self.browser.getControl("Login").click()
672        self.assertEqual(self.browser.url, self.customer_path)
673        self.assertTrue('You logged in' in self.browser.contents)
674        return
675
676    def test_customer_edit_upload_upload_and_request(self):
677        # Customer cant login if their password is not set
678        self.browser.open(self.login_path)
679        self.browser.getControl(name="form.login").value = self.customer_id
680        self.browser.getControl(name="form.password").value = 'cpwd'
681        self.browser.getControl("Login").click()
682        self.assertMatches(
683            '...You logged in...', self.browser.contents)
684        self.browser.getLink("Edit").click()
685        self.browser.getControl(name="form.email").value = 'new_email@aa.ng'
686        self.browser.getControl("Save", index=0).click()
687        self.assertMatches('...Form has been saved...',
688                           self.browser.contents)
689        self.browser.getControl("Save and request registration").click()
690        self.assertMatches('...Passport picture is missing...',
691                           self.browser.contents)
692        self.assertEqual(self.customer.state, 'started')
693        # Customer must upload a passport picture. We are already on
694        # the upload page.
695        ctrl = self.browser.getControl(name='passporteditupload')
696        file_obj = open(SAMPLE_IMAGE, 'rb')
697        file_ctrl = ctrl.mech_control
698        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
699        self.browser.getControl(
700            name='upload_passporteditupload').click()
701        self.assertTrue(
702            'src="http://localhost/app/customers/K1000000/passport.jpg"'
703            in self.browser.contents)
704        self.browser.getControl(name="CANCEL").click()
705        self.assertEqual(self.browser.url, self.customer_path)
706        self.browser.getLink("Edit").click()
707        self.browser.getControl("Save and request registration").click()
708        self.assertMatches('...Registration form has been submitted...',
709                           self.browser.contents)
710        self.assertEqual(self.customer.state, 'requested')
711        # Customer can view history
712        self.browser.getLink("History").click()
713        self.assertMatches('...Customer created by system...',
714            self.browser.contents)
715
716    def test_customer_login(self):
717        # Customer cant login if their password is not set
718        self.customer.password = None
719        self.browser.open(self.login_path)
720        self.browser.getControl(name="form.login").value = self.customer_id
721        self.browser.getControl(name="form.password").value = 'cpwd'
722        self.browser.getControl("Login").click()
723        self.assertTrue(
724            'You entered invalid credentials.' in self.browser.contents)
725        # We set the password again
726        IUserAccount(
727            self.app['customers'][self.customer_id]).setPassword('cpwd')
728        # Customers can't login if their account is suspended/deactivated
729        self.customer.suspended = True
730        self.browser.open(self.login_path)
731        self.browser.getControl(name="form.login").value = self.customer_id
732        self.browser.getControl(name="form.password").value = 'cpwd'
733        self.browser.getControl("Login").click()
734        self.assertMatches(
735            '...<div class="alert alert-warning">'
736            'Your account has been deactivated.</div>...',
737            self.browser.contents)
738        # If suspended_comment is set this message will be flashed instead
739        self.customer.suspended_comment = u'Aetsch baetsch!'
740        self.browser.getControl(name="form.login").value = self.customer_id
741        self.browser.getControl(name="form.password").value = 'cpwd'
742        self.browser.getControl("Login").click()
743        self.assertMatches(
744            '...<div class="alert alert-warning">Aetsch baetsch!</div>...',
745            self.browser.contents)
746        self.customer.suspended = False
747        # Customers can't login if a temporary password has been set and
748        # is not expired
749        self.app['customers'][self.customer_id].setTempPassword(
750            'anybody', 'temp_cpwd')
751        self.browser.open(self.login_path)
752        self.browser.getControl(name="form.login").value = self.customer_id
753        self.browser.getControl(name="form.password").value = 'cpwd'
754        self.browser.getControl("Login").click()
755        self.assertMatches(
756            '...Your account has been temporarily deactivated...',
757            self.browser.contents)
758        # The customer can login with the temporary password
759        self.browser.open(self.login_path)
760        self.browser.getControl(name="form.login").value = self.customer_id
761        self.browser.getControl(name="form.password").value = 'temp_cpwd'
762        self.browser.getControl("Login").click()
763        self.assertMatches(
764            '...You logged in...', self.browser.contents)
765        # Customer can view the base data
766        self.browser.open(self.customer_path)
767        self.assertEqual(self.browser.headers['Status'], '200 Ok')
768        self.assertEqual(self.browser.url, self.customer_path)
769        # When the password expires ...
770        delta = timedelta(minutes=11)
771        self.app['customers'][self.customer_id].temp_password[
772            'timestamp'] = datetime.utcnow() - delta
773        self.app['customers'][self.customer_id]._p_changed = True
774        # ... the customer will be automatically logged out
775        self.assertRaises(
776            Unauthorized, self.browser.open, self.customer_path)
777        # Then the customer can login with the original password
778        self.browser.open(self.login_path)
779        self.browser.getControl(name="form.login").value = self.customer_id
780        self.browser.getControl(name="form.password").value = 'cpwd'
781        self.browser.getControl("Login").click()
782        self.assertMatches(
783            '...You logged in...', self.browser.contents)
784
785    def test_change_password_request(self):
786        self.browser.open('http://localhost/app/changepw')
787        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
788        self.browser.getControl("Send login credentials").click()
789        self.assertTrue('An email with' in self.browser.contents)
790
791
792class CustomerRegistrationTests(CustomersFullSetup):
793    # Tests for customer registration
794
795    layer = FunctionalLayer
796
797    def test_request_pw(self):
798        # Customer with wrong number can't be found.
799        self.browser.open('http://localhost/app/requestpw')
800        self.browser.getControl(name="form.firstname").value = 'Anna'
801        self.browser.getControl(name="form.number").value = 'anynumber'
802        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
803        self.browser.getControl("Send login credentials").click()
804        self.assertTrue('No customer found.'
805            in self.browser.contents)
806        # Anonymous is not informed that firstname verification failed.
807        # It seems that the record doesn't exist.
808        self.browser.open('http://localhost/app/requestpw')
809        self.browser.getControl(name="form.firstname").value = 'Johnny'
810        self.browser.getControl(name="form.number").value = '123'
811        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
812        self.browser.getControl("Send login credentials").click()
813        self.assertTrue('No customer found.'
814            in self.browser.contents)
815        # Even with the correct firstname we can't register if a
816        # password has been set and used.
817        self.browser.getControl(name="form.firstname").value = 'Anna'
818        self.browser.getControl(name="form.number").value = '123'
819        self.browser.getControl("Send login credentials").click()
820        self.assertTrue('Your password has already been set and used.'
821            in self.browser.contents)
822        self.browser.open('http://localhost/app/requestpw')
823        self.app['customers'][self.customer_id].password = None
824        # The firstname field, used for verification, is not case-sensitive.
825        self.browser.getControl(name="form.firstname").value = 'aNNa'
826        self.browser.getControl(name="form.number").value = '123'
827        self.browser.getControl(name="form.email").value = 'new@yy.zz'
828        self.browser.getControl("Send login credentials").click()
829        # Yeah, we succeded ...
830        self.assertTrue('Your request was successful.'
831            in self.browser.contents)
832        # ... and  customer can be found in the catalog via the email address
833        cat = queryUtility(ICatalog, name='customers_catalog')
834        results = list(
835            cat.searchResults(
836            email=('new@yy.zz', 'new@yy.zz')))
837        self.assertEqual(self.customer, results[0])
838        logfile = os.path.join(
839            self.app['datacenter'].storage, 'logs', 'main.log')
840        logcontent = open(logfile).read()
841        self.assertTrue(
842            'zope.anybody - customers.browser.CustomerRequestPasswordPage - '
843            '123 (K1000000) - new@yy.zz' in logcontent)
844        return
845
846    def test_create_account(self):
847        self.browser.open('http://localhost/app/createaccount')
848        self.browser.getControl(name="form.firstname").value = 'Ruben'
849        self.browser.getControl(name="form.lastname").value = 'Gonzales'
850        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
851        self.browser.getControl("Send login credentials").click()
852        # Email address exists.
853        self.assertTrue('Email address in use.' in self.browser.contents)
854        self.browser.getControl(name="form.email").value = 'newcustomer@xx.zz'
855        self.browser.getControl("Send login credentials").click()
856        self.assertTrue(
857            'Your request was successful.' in self.browser.contents)
858        # Customer can be found in the catalog via the email address
859        cat = queryUtility(ICatalog, name='customers_catalog')
860        results = list(
861            cat.searchResults(
862            email=('newcustomer@xx.zz', 'newcustomer@xx.zz')))
863        self.assertEqual(self.app['customers']['K1000001'], results[0])
864        self.assertEqual(self.app['customers']['K1000001'].firstname, 'Ruben')
865        self.assertEqual(
866            self.app['customers']['K1000001'].lastname, 'Gonzales')
867        logfile = os.path.join(
868            self.app['datacenter'].storage, 'logs', 'main.log')
869        logcontent = open(logfile).read()
870        self.assertTrue(
871            'zope.anybody - customers.browser.CustomerCreateAccountPage - '
872            'K1000001 - newcustomer@xx.zz' in logcontent)
873        return
874
875
876class CustomerDataExportTests(CustomersFullSetup, FunctionalAsyncTestCase):
877    # Tests for CustomersContainer class views and pages
878
879    layer = FunctionalLayer
880
881    def wait_for_export_job_completed(self):
882        # helper function waiting until the current export job is completed
883        manager = getUtility(IJobManager)
884        job_id = self.app['datacenter'].running_exports[0][0]
885        job = manager.get(job_id)
886        wait_for_result(job)
887        return job_id
888
889    def test_datacenter_export(self):
890        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
891        self.browser.open('http://localhost/app/datacenter/@@export')
892        self.browser.getControl(name="exporter").value = ['customers']
893        self.browser.getControl("Create CSV file").click()
894
895        # When the job is finished and we reload the page...
896        job_id = self.wait_for_export_job_completed()
897        # ... the csv file can be downloaded ...
898        self.browser.open('http://localhost/app/datacenter/@@export')
899        self.browser.getLink("Download").click()
900        self.assertEqual(self.browser.headers['content-type'],
901            'text/csv; charset=UTF-8')
902        self.assertTrue(
903            'filename="WAeUP.Ikoba_customers_%s.csv' % job_id in
904            self.browser.headers['content-disposition'])
905        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
906        job_id = self.app['datacenter'].running_exports[0][0]
907        # ... and discarded
908        self.browser.open('http://localhost/app/datacenter/@@export')
909        self.browser.getControl("Discard").click()
910        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
911        # Creation, downloading and discarding is logged
912        logfile = os.path.join(
913            self.app['datacenter'].storage, 'logs', 'datacenter.log')
914        logcontent = open(logfile).read()
915        self.assertTrue(
916            'zope.mgr - browser.pages.ExportCSVPage - exported: '
917            'customers, job_id=%s'
918            % job_id in logcontent
919            )
920        self.assertTrue(
921            'zope.mgr - browser.pages.ExportCSVView - downloaded: '
922            'WAeUP.Ikoba_customers_%s.csv, job_id=%s'
923            % (job_id, job_id) in logcontent
924            )
925        self.assertTrue(
926            'zope.mgr - browser.pages.ExportCSVPage - discarded: '
927            'job_id=%s' % job_id in logcontent
928            )
929
930
931class DocumentUITests(CustomersFullSetup):
932    # Tests for customer document related views and pages
933
934    def test_manage_document(self):
935        # Managers can access the pages of customer documentsconter
936        # and can perform actions
937        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
938        self.browser.open(self.customer_path)
939        self.assertEqual(self.browser.headers['Status'], '200 Ok')
940        self.assertEqual(self.browser.url, self.customer_path)
941        self.browser.open(self.customer_path)
942        self.browser.getLink("Documents", index=1).click()
943        self.browser.getControl("Add document").click()
944        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
945        self.browser.getControl(name="form.title").value = 'My PDF Document'
946        self.browser.getControl("Add document").click()
947        self.assertTrue('PDF Document added.' in self.browser.contents)
948        docid = [i for i in self.customer['documents'].keys()
949                 if len(i) > 10][0]
950        document = self.customer['documents'][docid]
951
952        # Document can be edited
953        self.browser.getLink(docid[:9]).click()
954        self.browser.getLink("Manage").click()
955        self.browser.getControl(name="form.title").value = 'My second doc'
956        self.browser.getControl("Save").click()
957        self.assertTrue('Form has been saved.' in self.browser.contents)
958        self.browser.getLink("View").click()
959        self.assertEqual(self.browser.url,
960            self.documents_path + '/' + docid + '/index')
961
962        # Transitions can be performed
963        self.browser.getLink("Transition").click()
964        self.browser.getControl(name="transition").value = ['submit']
965        self.browser.getControl("Apply").click()
966        self.browser.getLink("Transition").click()
967        # Document can only be verified if customer is approved
968        self.browser.getControl(name="transition").value = ['verify']
969        self.browser.getControl("Apply").click()
970        self.assertTrue(
971            'Customer has not yet been approved' in self.browser.contents)
972        IWorkflowState(self.customer).setState(APPROVED)
973        # Document can only be verified if files have been uploaded before
974        self.browser.getLink("Transition").click()
975        self.browser.getControl(name="transition").value = ['verify']
976        self.browser.getControl("Apply").click()
977        self.assertTrue('No file uploaded' in self.browser.contents)
978        self.assertEqual(document.state, 'submitted')
979        # We set state here manually (verification is tested in
980        # test_verify_document)
981        IWorkflowState(document).setState(VERIFIED)
982
983        # Manage button and form is no longer available
984        self.browser.open(self.documents_path + '/' + docid + '/index')
985        self.assertFalse(
986            'href="http://localhost/app/customers/K1000000/'
987            'documents/%s/manage"'
988            % docid in self.browser.contents)
989        self.browser.open(self.documents_path + '/' + docid + '/manage')
990        self.assertTrue(
991            'The requested form is locked (read-only)'
992            in self.browser.contents)
993
994        # Documents can be removed
995        self.browser.getLink("Documents", index=1).click()
996        ctrl = self.browser.getControl(name='val_id')
997        ctrl.getControl(value=document.document_id).selected = True
998        self.browser.getControl("Remove selected", index=0).click()
999        self.assertTrue('Successfully removed' in self.browser.contents)
1000
1001        # All actions are being logged
1002        logfile = os.path.join(
1003            self.app['datacenter'].storage, 'logs', 'customers.log')
1004        logcontent = open(logfile).read()
1005
1006        self.assertTrue(
1007            'INFO - system - K1000000 - DOC1 - Document created'
1008            in logcontent)
1009        self.assertTrue(
1010            'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
1011            '- K1000000 - added: PDF Document %s'
1012            % document.document_id in logcontent)
1013        self.assertTrue(
1014            'INFO - zope.mgr - customers.browser.DocumentManageFormPage '
1015            '- K1000000 - %s - saved: title' % docid
1016            in logcontent)
1017        self.assertTrue(
1018            'INFO - zope.mgr - K1000000 - %s - Submitted for verification'
1019            % docid in logcontent)
1020        self.assertTrue(
1021            'INFO - zope.mgr - customers.browser.DocumentsManageFormPage '
1022            '- K1000000 - removed: %s' % docid
1023            in logcontent)
1024
1025    def test_edit_sample_document(self):
1026        # Customers can manage documents under certain conditions
1027        self.browser.open(self.login_path)
1028        self.browser.getControl(name="form.login").value = self.customer_id
1029        self.browser.getControl(name="form.password").value = 'cpwd'
1030        self.browser.getControl("Login").click()
1031        self.assertMatches(
1032            '...You logged in...', self.browser.contents)
1033        self.browser.getLink("Documents").click()
1034        self.browser.getControl("Add document").click()
1035        self.assertTrue(
1036            'The requested form is locked' in self.browser.contents)
1037        # Customer is in wrong state
1038        IWorkflowState(self.customer).setState(APPROVED)
1039        self.browser.getControl("Add document").click()
1040        self.browser.getControl(name="doctype").value = [
1041            'CustomerSampleDocument']
1042        self.browser.getControl(name="form.title").value = 'My Sample Document'
1043        self.browser.getControl("Add document").click()
1044        self.assertTrue('Sample Document added.' in self.browser.contents)
1045        docid = [i for i in self.customer['documents'].keys()
1046                 if len(i) > 10][0]
1047        document = self.customer['documents'][docid]
1048        self.browser.getControl(name="form.title").value = 'My second doc'
1049        self.browser.getControl("Save").click()
1050        self.assertEqual(document.title, 'My second doc')
1051        self.assertTrue('Form has been saved.' in self.browser.contents)
1052        self.browser.getLink("View").click()
1053        self.assertEqual(
1054            self.browser.url, self.documents_path + '/%s/index' % docid)
1055        # Customer can upload a document.
1056        self.browser.getLink("Edit").click()
1057        ctrl = self.browser.getControl(name='samplescaneditupload')
1058        file_obj = open(SAMPLE_IMAGE, 'rb')
1059        file_ctrl = ctrl.mech_control
1060        file_ctrl.add_file(file_obj, filename='my_document.jpg')
1061        self.browser.getControl(
1062            name='upload_samplescaneditupload').click()
1063        self.assertTrue(
1064            'href="http://localhost/app/customers/K1000000/'
1065            'documents/%s/sample"'
1066            % docid in self.browser.contents)
1067        # Customer can submit the form. The form is also saved.
1068        self.browser.getControl(name="form.title").value = 'My third doc'
1069        self.browser.getControl("Final Submit").click()
1070        self.assertEqual(document.title, 'My third doc')
1071        self.assertEqual(document.state, 'submitted')
1072        self.assertTrue(
1073            'Document State: submitted for verification'
1074            in self.browser.contents)
1075        # Customer can't edit the document once it has been submitted
1076        self.browser.open(self.documents_path + '/%s/edit' % docid)
1077        self.assertTrue(
1078            'The requested form is locked' in self.browser.contents)
1079
1080    def test_manage_upload_sample_file(self):
1081        # Managers can upload a file via the DocumentManageFormPage
1082        # The image is stored even if form has errors
1083        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1084        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1085        # Create a pseudo image file and select it to be uploaded
1086        image = open(SAMPLE_IMAGE, 'rb')
1087        ctrl = self.browser.getControl(name='samplescanmanageupload')
1088        file_ctrl = ctrl.mech_control
1089        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1090        # The Save action does not upload files
1091        self.browser.getControl("Save").click()  # submit form
1092        self.assertFalse(
1093            'href="http://localhost/app/customers/K1000000/'
1094            'documents/DOC1/sample"'
1095            in self.browser.contents)
1096        # ... but the correct upload submit button does
1097        image = open(SAMPLE_IMAGE)
1098        ctrl = self.browser.getControl(name='samplescanmanageupload')
1099        file_ctrl = ctrl.mech_control
1100        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1101        self.browser.getControl(
1102            name='upload_samplescanmanageupload').click()
1103        self.assertTrue(
1104            'href="http://localhost/app/customers/K1000000/'
1105            'documents/DOC1/sample"'
1106            in self.browser.contents)
1107        # Browsing the link shows a real image
1108        self.browser.open('sample')
1109        self.assertEqual(
1110            self.browser.headers['content-type'], 'image/jpeg')
1111        self.assertEqual(len(self.browser.contents), 2787)
1112        # We can't reupload a file. The existing file must be deleted first.
1113        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1114        self.assertFalse(
1115            'upload_samplescanmanageupload' in self.browser.contents)
1116        # File must be deleted first
1117        self.browser.getControl(name='delete_samplescanmanageupload').click()
1118        self.assertTrue(
1119            'sample deleted' in self.browser.contents)
1120        # Uploading a file which is bigger than 150k will raise an error
1121        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
1122        ctrl = self.browser.getControl(name='samplescanmanageupload')
1123        file_ctrl = ctrl.mech_control
1124        file_ctrl.add_file(big_image, filename='my_sample_scan.jpg')
1125        self.browser.getControl(
1126            name='upload_samplescanmanageupload').click()
1127        self.assertTrue(
1128            'Uploaded file is too big' in self.browser.contents)
1129        # We do not rely on filename extensions given by uploaders
1130        image = open(SAMPLE_IMAGE, 'rb')  # a jpg-file
1131        ctrl = self.browser.getControl(name='samplescanmanageupload')
1132        file_ctrl = ctrl.mech_control
1133        # Tell uploaded file is bmp
1134        file_ctrl.add_file(image, filename='my_sample_scan.bmp')
1135        self.browser.getControl(
1136            name='upload_samplescanmanageupload').click()
1137        self.assertTrue(
1138            # jpg file was recognized
1139            'File sample.jpg uploaded.' in self.browser.contents)
1140        # Delete file again
1141        self.browser.getControl(name='delete_samplescanmanageupload').click()
1142        self.assertTrue(
1143            'sample deleted' in self.browser.contents)
1144        # File names must meet several conditions
1145        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
1146        ctrl = self.browser.getControl(name='samplescanmanageupload')
1147        file_ctrl = ctrl.mech_control
1148        file_ctrl.add_file(bmp_image, filename='my_sample_scan.bmp')
1149        self.browser.getControl(
1150            name='upload_samplescanmanageupload').click()
1151        self.assertTrue('Only the following extensions are allowed'
1152            in self.browser.contents)
1153
1154    def test_verify_document(self):
1155        IWorkflowState(self.customer).setState('approved')
1156        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1157        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1158        image = open(SAMPLE_IMAGE, 'rb')
1159        ctrl = self.browser.getControl(name='samplescanmanageupload')
1160        file_ctrl = ctrl.mech_control
1161        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1162        self.browser.getControl(
1163            name='upload_samplescanmanageupload').click()
1164        IWorkflowState(self.document).setState(SUBMITTED)
1165        # Only after verifying the document, sample_md5 is set
1166        self.assertEqual(
1167            getattr(self.document, 'sample_md5', None), None)
1168        self.browser.open(self.documents_path + '/DOC1/trigtrans')
1169        self.browser.getControl(name="transition").value = ['verify']
1170        self.browser.getControl("Apply").click()
1171        self.assertEqual(
1172            getattr(self.document, 'sample_md5', None),
1173                    '1d1ab893e87c240afb2104d61ddfe180')
1174
1175    def test_manage_upload_pdf_file(self):
1176        # Managers can upload a file via the DocumentManageFormPage
1177        # The image is stored even if form has errors
1178        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1179        self.browser.open(self.customer_path + '/documents')
1180        self.browser.getControl("Add document").click()
1181        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
1182        self.browser.getControl(name="form.title").value = 'My PDF Document'
1183        self.browser.getControl("Add document").click()
1184        docid = [
1185            i for i in self.customer['documents'].keys() if len(i) > 10][0]
1186        self.browser.open(self.documents_path + '/%s/manage' % docid)
1187        # Create a pseudo image file and select it to be uploaded
1188        image = open(SAMPLE_IMAGE, 'rb')
1189        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1190        file_ctrl = ctrl.mech_control
1191        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1192        self.browser.getControl(
1193            name='upload_pdfscanmanageupload').click()
1194        self.assertTrue(
1195            'pdf file format expected' in self.browser.contents)
1196        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1197        file_ctrl = ctrl.mech_control
1198        file_ctrl.add_file(image, filename='my_sample_scan.pdf')
1199        self.browser.getControl(
1200            name='upload_pdfscanmanageupload').click()
1201        self.assertTrue(
1202            'Could not determine file type' in self.browser.contents)
1203        pdf = open(SAMPLE_PDF, 'rb')
1204        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1205        file_ctrl = ctrl.mech_control
1206        file_ctrl.add_file(pdf, filename='my_sample_scan.pdf')
1207        self.browser.getControl(
1208            name='upload_pdfscanmanageupload').click()
1209        self.assertTrue(
1210            'href="http://localhost/app/customers/K1000000/'
1211            'documents/%s/sample.pdf">%s.pdf</a>'
1212            % (docid, docid[:9]) in self.browser.contents)
1213        # Browsing the link shows a real pdf
1214        self.browser.open('sample.pdf')
1215        self.assertEqual(
1216            self.browser.headers['content-type'], 'application/pdf')
1217        # The name of the downloaded file will be different
1218        self.assertEqual(
1219            self.browser.headers['Content-Disposition'],
1220            'attachment; filename="%s.pdf' % docid[:9])
1221
1222    def test_view_slips(self):
1223        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1224        # Officers can open the document overview
1225        self.browser.open(self.customer_path + '/documents')
1226        self.browser.getLink("Download documents overview").click()
1227        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1228        self.assertEqual(
1229            self.browser.headers['Content-Type'], 'application/pdf')
1230        path = os.path.join(samples_dir(), 'documents_overview_slip.pdf')
1231        open(path, 'wb').write(self.browser.contents)
1232        print "Sample PDF overview_slip.pdf written to %s" % path
1233        # Officers can open document slips which shows a thumbnail of
1234        # the jpeg file attached.
1235        file_id = IFileStoreNameChooser(self.document).chooseName(
1236            attr='sample.jpg')
1237        fs = ExtFileStore(root=self.dc_root)
1238        jpegfile = open(SAMPLE_IMAGE, 'rb')
1239        fs.createFile(file_id, jpegfile)
1240        self.browser.open(self.customer_path + '/documents/DOC1')
1241        self.browser.getLink("Download document slip").click()
1242        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1243        self.assertEqual(
1244            self.browser.headers['Content-Type'], 'application/pdf')
1245        path = os.path.join(samples_dir(), 'document_slip.pdf')
1246        open(path, 'wb').write(self.browser.contents)
1247        print "Sample document_slip.pdf written to %s" % path
1248        # Officers can open merged pdf document slips
1249        pdfdocument = createObject('waeup.CustomerPDFDocument')
1250        pdfdocument.title = u'My first document'
1251        self.customer['documents'].addDocument(pdfdocument)
1252        # Add pdf file
1253        file_id = IFileStoreNameChooser(pdfdocument).chooseName(
1254            attr='sample.pdf')
1255        fs = ExtFileStore(root=self.dc_root)
1256        pdffile = open(SAMPLE_PDF, 'rb')
1257        fs.createFile(file_id, pdffile)
1258        docid = [i for i in self.customer['documents'].keys()
1259                 if len(i) > 10][0]
1260        self.browser.open(self.customer_path + '/documents/' + docid)
1261        self.browser.getLink("Download document slip").click()
1262        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1263        self.assertEqual(
1264            self.browser.headers['Content-Type'], 'application/pdf')
1265        path = os.path.join(samples_dir(), 'pdfdocument_slip.pdf')
1266        open(path, 'wb').write(self.browser.contents)
1267        print "Sample pdfdocument_slip.pdf written to %s" % path
1268
1269    def test_get_setmd5_file(self):
1270        # A proper file name chooser is registered for customer documents.
1271        # This is not a UI test. It's just a functional test.
1272        file_id = IFileStoreNameChooser(self.document).chooseName(
1273            attr='sample')
1274        fs = ExtFileStore(root=self.dc_root)
1275        fs.createFile(file_id, StringIO('my sample 1'))
1276        result = fs.getFileByContext(self.document, attr='sample')
1277        self.assertEqual(
1278            file_id, '__file-customerdocument__01000/'
1279            'K1000000/sample_DOC1_K1000000')
1280        self.assertEqual(result.read(), 'my sample 1')
1281        self.assertEqual(
1282            self.document.connected_files[0][1].read(), 'my sample 1')
1283        self.document.setMD5()
1284        self.assertEqual(
1285            self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
1286        return
1287
1288
1289class ContractUITests(CustomersFullSetup):
1290    # Tests for contract related views and pages
1291
1292    never_ending_button_text = (
1293        'Select payment method (final submission)')
1294
1295    def setup_payment(self):
1296        payer = IPayer(self.customer)
1297        payable = IPayable(self.contract)
1298        self.payment = Payment(payer, payable)
1299        self.payment.gateway_service = 'demo_creditcard'
1300        self.payment.state = STATE_PAID
1301        self.payment.title = u'My payment'
1302        self.app['payments'][self.payment.payment_id] = self.payment
1303
1304    def add_product_option(self, contract):
1305        prodoption = ProductOption()
1306        prodoption.title = u'Any product option'
1307        prodoption.fee = Decimal('88.8')
1308        prodoption.currency = 'EUR'
1309        contract.product_options = [prodoption, ]
1310
1311    def test_multiple_currencies(self):
1312        prodoption1 = ProductOption()
1313        prodoption1.title = u'Any product option in Euros'
1314        prodoption1.fee = Decimal('88.8')
1315        prodoption1.currency = 'EUR'
1316        prodoption2 = ProductOption()
1317        prodoption2.title = u'Any product option in Dollars'
1318        prodoption2.fee = Decimal('99.9')
1319        prodoption2.currency = 'USD'
1320        self.assertRaises(CurrencyMismatch,
1321                          setattr, self.contract,
1322                          'product_options',
1323                          [prodoption1, prodoption2]
1324                          )
1325
1326    def prepare_payment_select(self):
1327        IWorkflowState(self.customer).setState('approved')
1328        IWorkflowState(self.document).setState('verified')
1329        self.contract.document_object = self.document
1330        self.add_product_option(self.contract)
1331        IWorkflowState(self.contract).setState('created')
1332        # login as customer
1333        self.browser.open(self.login_path)
1334        self.browser.getControl(name="form.login").value = self.customer_id
1335        self.browser.getControl(name="form.password").value = 'cpwd'
1336        self.browser.getControl("Login").click()
1337
1338    def test_manage_contract(self):
1339        # Managers can access the pages of customer contractsconter
1340        # and can perform actions
1341        IWorkflowState(self.customer).setState(APPROVED)
1342        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1343        self.browser.open(self.customer_path)
1344        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1345        self.assertEqual(self.browser.url, self.customer_path)
1346        self.browser.open(self.customer_path)
1347        self.browser.getLink("Contracts").click()
1348        self.browser.getControl("Add contract").click()
1349        self.browser.getControl(name="contype").value = ['SampleContract']
1350        self.browser.getControl("Add contract").click()
1351        self.assertTrue('Sample Contract added.' in self.browser.contents)
1352        conid = [i for i in self.customer['contracts'].keys()
1353                 if len(i) > 10][0]
1354        contract = self.customer['contracts'][conid]
1355        self.assertEqual(
1356            self.browser.url,
1357            self.contracts_path + '/%s/selectproduct' % conid)
1358        # SAM is in the correct contract_category
1359        self.assertTrue('<option value="SAM">' in self.browser.contents)
1360        # So far last_product_id is None.
1361        self.assertTrue(
1362            self.customer['contracts'][conid].last_product_id is None)
1363        self.browser.getControl(name="form.product_object").value = ['SAM']
1364        self.browser.getControl("Save and proceed").click()
1365        self.assertEqual(
1366            self.browser.url, self.contracts_path + '/%s/manage' % conid)
1367        self.browser.getLink("View").click()
1368        self.assertEqual(
1369            self.browser.url, self.contracts_path + '/%s/index' % conid)
1370        self.assertEqual(contract.tc_dict, {'en': u'Hello world'})
1371
1372        # Transitions can be performed
1373        self.browser.getLink("Transition").click()
1374        self.browser.getControl(name="transition").value = ['submit']
1375        self.browser.getControl("Apply").click()
1376        self.browser.getLink("Transition").click()
1377        self.browser.getControl(name="transition").value = ['approve']
1378        self.browser.getControl("Apply").click()
1379        self.assertEqual(contract.state, 'approved')
1380
1381        # Even in state approved the official use data can be edited
1382        self.browser.open(self.contracts_path + '/%s/index' % conid)
1383        self.browser.getLink("Manage official data").click()
1384        self.browser.getControl(name="form.comment").value = u'Nice place'
1385        self.browser.getControl("Save").click()
1386        self.assertEqual(contract.comment, 'Nice place')
1387        self.assertTrue('Form has been saved.' in self.browser.contents)
1388
1389        # Contracts can be removed
1390        self.browser.getLink("Contracts").click()
1391        ctrl = self.browser.getControl(name='val_id')
1392        ctrl.getControl(value=contract.contract_id).selected = True
1393        self.browser.getControl("Remove selected", index=0).click()
1394        self.assertTrue('Successfully removed' in self.browser.contents)
1395
1396        # All actions are being logged
1397        logfile = os.path.join(
1398            self.app['datacenter'].storage, 'logs', 'customers.log')
1399        logcontent = open(logfile).read()
1400        self.assertTrue(
1401            'INFO - zope.mgr - K1000000 - %s - Contract created' % conid
1402            in logcontent)
1403        self.assertTrue(
1404            'INFO - zope.mgr - customers.browser.ContractAddFormPage '
1405            '- K1000000 - added: Sample Contract %s'
1406            % contract.contract_id in logcontent)
1407        self.assertTrue(
1408            'INFO - zope.mgr - K1000000 - %s - Contract submitted' % conid
1409            in logcontent)
1410        self.assertTrue(
1411            'INFO - zope.mgr - K1000000 - %s - Contract approved' % conid
1412            in logcontent)
1413        self.assertTrue(
1414            'INFO - zope.mgr - customers.browser.ContractsFormPage '
1415            '- K1000000 - removed: %s' % conid
1416            in logcontent)
1417
1418    def test_edit_sample_contract(self):
1419        # We add a second product.
1420        product = createObject('waeup.Product')
1421        product.product_id = u'LIC'
1422        product.title = u'Our License Product'
1423        product.contract_category = u'license'
1424        product.valid_from = date(2014, 12, 4)
1425        self.app['products'].addProduct(product)
1426        # Customers can manage contracts under certain conditions
1427        self.browser.open(self.login_path)
1428        self.browser.getControl(name="form.login").value = self.customer_id
1429        self.browser.getControl(name="form.password").value = 'cpwd'
1430        self.browser.getControl("Login").click()
1431        self.assertMatches(
1432            '...You logged in...', self.browser.contents)
1433        self.browser.getLink("Contracts").click()
1434        # Customer is in wrong state
1435        self.assertFalse('Add contract' in self.browser.contents)
1436        self.browser.open(self.contracts_path + '/addcontract')
1437        self.assertTrue(
1438            'The requested form is locked' in self.browser.contents)
1439        IWorkflowState(self.customer).setState(APPROVED)
1440        self.browser.open(self.contracts_path)
1441        # Now customer can add a contract
1442        self.browser.getControl("Add contract").click()
1443        self.browser.getControl(name="contype").value = ['SampleContract']
1444        self.browser.getControl("Add contract").click()
1445        self.assertTrue('Sample Contract added.' in self.browser.contents)
1446        conid = [i for i in self.customer['contracts'].keys()
1447                 if len(i) > 10][0]
1448        contract = self.customer['contracts'][conid]
1449        self.assertEqual(
1450            self.browser.url,
1451            self.contracts_path + '/%s/selectproduct' % conid)
1452        # SAM is in the correct contract_category ...
1453        self.assertTrue('<option value="SAM">' in self.browser.contents)
1454        # ... but LIC not.
1455        self.assertFalse('<option value="LIC">' in self.browser.contents)
1456        # So far last_product_id is None.
1457        self.assertTrue(
1458            self.customer['contracts'][conid].last_product_id is None)
1459        self.browser.getControl(name="form.product_object").value = ['SAM']
1460        self.browser.getControl("Save and proceed").click()
1461        self.assertEqual(
1462            self.browser.url, self.contracts_path + '/%s/edit' % conid)
1463        # Document is a required field on edit form page.
1464        self.browser.getControl("Save").click()
1465        self.assertTrue(
1466            'Document: <span class="error">Required input is missing.</span>'
1467            in self.browser.contents)
1468        # But our document can't be selected because it's not submitted
1469        self.assertFalse('My first document' in self.browser.contents)
1470        IWorkflowState(self.document).setState(SUBMITTED)
1471        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1472        self.browser.getControl(name="form.document_object").value = ['DOC1']
1473        self.browser.getControl("Save").click()
1474        # After saving the form, last_product_id and other attributes are set
1475        self.assertTrue('Form has been saved.' in self.browser.contents)
1476        self.assertEqual(
1477            self.customer['contracts'][conid].last_product_id, 'SAM')
1478        self.assertEqual(contract.title, 'Our Sample Product')
1479        self.assertEqual(contract.product_object, self.product)
1480        self.assertEqual(contract.document_object, self.document)
1481        # Saving the form again does not unset last_product_id
1482        self.browser.getControl("Save").click()
1483        self.assertEqual(
1484            self.customer['contracts'][conid].last_product_id, 'SAM')
1485        self.assertTrue('Form has been saved.' in self.browser.contents)
1486        # So far we have not yet set product options.
1487        # Unfortunately, we can't set them in test browser
1488        prodoption = ProductOption()
1489        prodoption.title = u'Any product option'
1490        prodoption.fee = Decimal('88.8')
1491        prodoption.currency = 'EUR'
1492        contract.product_options = [prodoption, ]
1493        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1494        # We can see both the stored and the recent product options
1495        # from the chosen product.
1496        self.assertTrue(
1497            '<option selected="selected" value="Any product option_88.8_EUR">'
1498            'Any product option @ 88.8 Euro</option>'
1499            in self.browser.contents)
1500        self.assertTrue('<option value="First option_99.9_USD">First option '
1501                        '@ 99.9 US Dollar</option>' in self.browser.contents)
1502        # In test browser we can at least replace the option
1503        self.browser.getControl(
1504            name="form.product_options.0.").value = ['First option_99.9_USD']
1505        self.assertEqual(
1506            contract.product_options[0].title, 'Any product option')
1507        self.browser.getControl("Save").click()
1508        self.assertEqual(contract.product_options[0].title, 'First option')
1509        self.browser.getLink("View").click()
1510        self.assertTrue(
1511            '<span>First option @ 99.9 US Dollar</span>'
1512            in self.browser.contents)
1513        self.assertEqual(
1514            self.browser.url, self.contracts_path + '/%s/index' % conid)
1515        # An href attribute is referring to the document and product objects
1516        self.assertTrue('<a href="http://localhost/app/products/SAM">SAM -'
1517            in self.browser.contents)
1518        self.assertTrue(
1519            '<a href="http://localhost/app/customers/K1000000/'
1520            'documents/DOC1">DOC1 -'
1521            in self.browser.contents)
1522        # Customer can submit the form if confirmation box is ticket.
1523        # The form is also saved.
1524        self.browser.getLink("Edit").click()
1525        self.browser.getControl("Proceed to checkout").click()
1526        self.assertTrue(
1527            'confirm your acceptance of these by ticking'
1528            in self.browser.contents)
1529        self.assertEqual(contract.state, 'created')
1530        self.browser.getControl(name="confirm_tc").value = True
1531        self.browser.getControl("Proceed to checkout").click()
1532        self.assertEqual(contract.state, 'created')
1533        radio_ctrl = self.browser.getControl(name='gw')
1534        radio_ctrl.value = [radio_ctrl.options[0]]  # pick first payment opt
1535        self.browser.getControl("Select payment method").click()
1536        # Select demo payment
1537        self.browser.getControl(name="cc_number").value = '123456789012345'
1538        self.browser.getControl(name="csc").value = '1234'
1539        self.browser.getControl("Authorize Payment").click()
1540        self.assertTrue(
1541            'Your payment will now be verified'
1542            in self.browser.contents)
1543        self.assertEqual(contract.state, 'awaiting')
1544        # Customer can't edit the contract once it has been submitted
1545        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1546        self.assertTrue(
1547            'The requested form is locked' in self.browser.contents)
1548
1549    def test_view_slips(self):
1550        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1551        # Officers can open the contract overview
1552        self.browser.open(self.customer_path + '/contracts')
1553        self.browser.getLink("Download contracts overview").click()
1554        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1555        self.assertEqual(
1556            self.browser.headers['Content-Type'], 'application/pdf')
1557        path = os.path.join(samples_dir(), 'contracts_overview_slip.pdf')
1558        open(path, 'wb').write(self.browser.contents)
1559        print "Sample PDF overview_slip.pdf written to %s" % path
1560        # Officers can open contract slips.
1561        # First we add a submitted document and a product.
1562        IWorkflowState(self.document).setState(SUBMITTED)
1563        self.contract.document_object = self.document
1564        self.contract.product_object = self.product
1565        self.contract.tc_dict = {'en': u'<strong>Hello world</strong>'}
1566        self.contract.valid_from = date(2015, 12, 4)
1567        self.contract.valid_to = 'anything'
1568        self.contract.title = u'Contract Title'
1569        self.browser.open(self.customer_path + '/contracts/CON1')
1570        self.browser.getLink("Download contract slip").click()
1571        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1572        self.assertEqual(
1573            self.browser.headers['Content-Type'], 'application/pdf')
1574        path = os.path.join(samples_dir(), 'contract_slip.pdf')
1575        open(path, 'wb').write(self.browser.contents)
1576        print "Sample contract_slip.pdf written to %s" % path
1577
1578    def test_view_receipt(self):
1579        self.add_product_option(self.contract)
1580        self.setup_payment()
1581        IWorkflowState(self.document).setState(SUBMITTED)
1582        self.contract.document_object = self.document
1583        self.contract.product_object = self.product
1584        self.contract.title = u'Contract Title'
1585        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1586        # Officers can open the receipt
1587        self.browser.open(self.customer_path + '/contracts/CON1')
1588        self.browser.getLink("Download payment receipt").click()
1589        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1590        self.assertEqual(
1591            self.browser.headers['Content-Type'], 'application/pdf')
1592        path = os.path.join(samples_dir(), 'contract_payment_receipt.pdf')
1593        open(path, 'wb').write(self.browser.contents)
1594        print "Sample contract_payment_receipt.pdf written to %s" % path
1595
1596    def test_contract_approval(self):
1597        # This is not a UI test. It's just a functional test.
1598        self.assertRaises(ConstraintNotSatisfied,
1599            self.contract.document_object, self.document)
1600        # Document must be at least submitted
1601        IWorkflowState(self.document).setState('submitted')
1602        self.contract.document_object = self.document
1603        IWorkflowState(self.contract).setState('submitted')
1604        self.assertRaises(InvalidTransitionError,
1605            IWorkflowInfo(self.contract).fireTransition, 'approve')
1606        # Customer must be approved and
1607        # document must be verified for the approval of contracts
1608        IWorkflowState(self.document).setState('verified')
1609        IWorkflowState(self.customer).setState('approved')
1610        IWorkflowInfo(self.contract).fireTransition('approve')
1611        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
1612
1613    def test_contract_approval_in_UI(self):
1614        # Now let's see what the UI says when trying to approve a contract
1615        # with unverified documents.
1616        IWorkflowState(self.customer).setState('approved')
1617        IWorkflowState(self.document).setState('submitted')
1618        self.contract.document_object = self.document
1619        IWorkflowState(self.contract).setState('submitted')
1620        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1621        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1622        self.browser.getControl(name="transition").value = ['approve']
1623        self.browser.getControl("Apply").click()
1624        # InvalidTransitionError is catched
1625        self.assertTrue(
1626            '<div class="alert alert-warning">Attached documents '
1627            'must be verified first.</div>'
1628            in self.browser.contents)
1629        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1630        IWorkflowState(self.document).setState('verified')
1631        self.browser.getControl(name="transition").value = ['approve']
1632        self.browser.getControl("Apply").click()
1633        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
1634
1635    def test_select_payment(self):
1636        # select payment
1637        self.prepare_payment_select()
1638        self.browser.open('%s/CON1/edit' % self.contracts_path)
1639        self.browser.getControl("Proceed to checkout").click()
1640        self.assertTrue(
1641            "Select payment method" in self.browser.contents)
1642        self.assertTrue(
1643            'Credit Card (Demo Payments)' in self.browser.contents)
1644
1645    def test_select_payment_no_choice(self):
1646        # we get warned if no payment was selected
1647        self.prepare_payment_select()
1648        self.browser.open(
1649            '%s/CON1/select_payment_method' % self.contracts_path)
1650        self.browser.getControl(self.never_ending_button_text).click()
1651        self.assertTrue(
1652            'Please pick a payment method' in self.browser.contents)
1653
1654    def test_select_payment_demo_provider(self):
1655        # we can proceed with payments if we select a payment method
1656        self.prepare_payment_select()
1657        self.browser.open(
1658            '%s/CON1/select_payment_method' % self.contracts_path)
1659        radio_ctrl = self.browser.getControl(name='gw')
1660        radio_ctrl.displayValue = ['Credit Card (Demo Payments)']
1661        self.browser.getControl(self.never_ending_button_text).click()
1662
1663
1664class PaymentsUITests(CustomersFullSetup):
1665    # Tests for contract related views and pages
1666
1667    def setup_payment(self):
1668        payer = IPayer(self.customer)
1669        payable = IPayable(self.contract)
1670        self.payment = Payment(payer, payable)
1671        self.payment.gateway_service = 'demo_creditcard'
1672        self.payment.state = STATE_PAID
1673        self.payment.title = u'My payment'
1674        self.app['payments'][self.payment.payment_id] = self.payment
1675
1676    def add_product_option(self, contract):
1677        prodoption = ProductOption()
1678        prodoption.title = u'Any product option'
1679        prodoption.fee = Decimal('88.8')
1680        prodoption.currency = 'EUR'
1681        contract.product_options = [prodoption, ]
1682
1683    def test_view_payments(self):
1684        self.add_product_option(self.contract)
1685        self.setup_payment()
1686        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1687        self.browser.open(self.customer_path)
1688        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1689        self.assertEqual(self.browser.url, self.customer_path)
1690        self.browser.open(self.customer_path)
1691        self.browser.getLink("Payments", index=1).click()
1692        self.assertTrue(
1693            '<td>Credit Card (Demo Payments)</td>' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.