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

Last change on this file since 14202 was 13806, checked in by Henrik Bettermann, 8 years ago

Add portal maintenance mode.

See r13394, r13396, r13468.

  • Property svn:keywords set to Id
File size: 81.1 KB
Line 
1## $Id: test_browser.py 13806 2016-04-06 10:27:11Z 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        self.browser.getLink("Switch maintenance mode").click()
495        button = lookup_submit_value(
496            'select', 'customers_zope.mgr.csv', self.browser)
497        button.click()
498        importerselect = self.browser.getControl(name='importer')
499        modeselect = self.browser.getControl(name='mode')
500        importerselect.getControl('Customer Processor').selected = True
501        modeselect.getControl(value='create').selected = True
502        self.browser.getControl('Proceed to step 3').click()
503        self.assertTrue('Header fields OK' in self.browser.contents)
504        self.browser.getControl('Perform import').click()
505        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
506        self.assertTrue(
507            'Successfully processed 2 rows' in self.browser.contents)
508        self.assertTrue('Batch processing finished' in self.browser.contents)
509
510        # The customers are properly indexed and we can
511        # thus find a customer in  the department
512        self.browser.open(self.manage_container_path)
513        # We can search for a new customer by name ...
514        self.browser.getControl(name="searchtype").value = ['fullname']
515        self.browser.getControl(name="searchterm").value = 'Claus'
516        self.browser.getControl("Find customer(s)").click()
517        self.assertTrue('Claus Finau' in self.browser.contents)
518        # ... and check if the imported password has been properly set
519        ctrl = self.browser.getControl(name='entries')
520        value = ctrl.options[0]
521        claus = self.app['customers'][value]
522        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
523        return
524
525    def test_activate_deactivate_buttons(self):
526        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
527        self.browser.open(self.customer_path)
528        self.browser.getLink("Deactivate").click()
529        self.assertTrue(
530            'Customer account has been deactivated.' in self.browser.contents)
531        self.assertTrue(
532            'Base Data (account deactivated)' in self.browser.contents)
533        self.assertTrue(self.customer.suspended)
534        self.browser.getLink("Activate").click()
535        self.assertTrue(
536            'Customer account has been activated.' in self.browser.contents)
537        self.assertFalse(
538            'Base Data (account deactivated)' in self.browser.contents)
539        self.assertFalse(self.customer.suspended)
540        # History messages have been added ...
541        self.browser.getLink("History").click()
542        self.assertTrue(
543            'Customer account deactivated by Manager<br />'
544            in self.browser.contents)
545        self.assertTrue(
546            'Customer account activated by Manager<br />'
547            in self.browser.contents)
548        # ... and actions have been logged.
549        logfile = os.path.join(
550            self.app['datacenter'].storage, 'logs', 'customers.log')
551        logcontent = open(logfile).read()
552        self.assertTrue(
553            'zope.mgr - customers.browser.CustomerDeactivateView - '
554            'K1000000 - account deactivated' in logcontent)
555        self.assertTrue(
556            'zope.mgr - customers.browser.CustomerActivateView - '
557            'K1000000 - account activated' in logcontent)
558
559    def test_login_as_customer(self):
560        # CustomerImpersonators can login as customer
561        self.app['users'].addUser('mrofficer', 'mrofficersecret')
562        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
563        self.app['users']['mrofficer'].title = 'Harry Actor'
564        prmglobal = IPrincipalRoleManager(self.app)
565        prmglobal.assignRoleToPrincipal(
566            'waeup.CustomerImpersonator', 'mrofficer')
567        prmglobal.assignRoleToPrincipal('waeup.CustomersManager', 'mrofficer')
568        self.assertEqual(self.customer.state, 'created')
569        # Login as customer impersonator
570        self.browser.open(self.login_path)
571        self.browser.getControl(name="form.login").value = 'mrofficer'
572        self.browser.getControl(name="form.password").value = 'mrofficersecret'
573        self.browser.getControl("Login").click()
574        self.assertMatches('...You logged in...', self.browser.contents)
575        self.browser.open(self.customer_path)
576        self.browser.getLink("Login as").click()
577        self.browser.getControl("Set password now").click()
578        temp_password = self.browser.getControl(name='form.password').value
579        self.browser.getControl("Login now").click()
580        self.assertMatches(
581            '...You successfully logged in as...', self.browser.contents)
582        # Status has changed
583        self.assertEqual(self.customer.state, 'started')
584        # We are logged in as customer and can see the 'My Data' tab
585        self.assertMatches(
586            '...<a href="#" class="dropdown-toggle"'
587            ' data-toggle="dropdown">...',
588            self.browser.contents)
589        self.assertMatches(
590            '...My Data...',
591            self.browser.contents)
592        self.browser.getLink("Logout").click()
593        # The customer can't login with the original password ...
594        self.browser.open(self.login_path)
595        self.browser.getControl(name="form.login").value = self.customer_id
596        self.browser.getControl(name="form.password").value = 'cpwd'
597        self.browser.getControl("Login").click()
598        self.assertMatches(
599            '...Your account has been temporarily deactivated...',
600            self.browser.contents)
601        # ... but with the temporary password
602        self.browser.open(self.login_path)
603        self.browser.getControl(name="form.login").value = self.customer_id
604        self.browser.getControl(name="form.password").value = temp_password
605        self.browser.getControl("Login").click()
606        self.assertMatches('...You logged in...', self.browser.contents)
607        # Creation of temp_password is properly logged
608        logfile = os.path.join(
609            self.app['datacenter'].storage, 'logs', 'customers.log')
610        logcontent = open(logfile).read()
611        self.assertTrue(
612            'mrofficer - customers.browser.LoginAsCustomerStep1 - K1000000 - '
613            'temp_password generated: %s' % temp_password in logcontent)
614
615
616class CustomerUITests(CustomersFullSetup):
617    # Tests for Customer class views and pages
618
619    def test_customer_login_with_email(self):
620        self.assertEqual(self.customer.state, 'created')
621        self.browser.open(self.login_path)
622        self.browser.getControl(name="form.login").value = self.customer.email
623        self.browser.getControl(name="form.password").value = 'cpwd'
624        self.browser.getControl("Login").click()
625        self.assertEqual(self.browser.url, self.customer_path)
626        self.assertTrue('You logged in' in self.browser.contents)
627        # Status has changed
628        self.assertEqual(self.customer.state, 'started')
629        return
630
631    def test_customer_change_password(self):
632        # Customers can change the password
633        self.assertEqual(self.customer.state, 'created')
634        self.customer.personal_updated = datetime.utcnow()
635        self.browser.open(self.login_path)
636        self.browser.getControl(name="form.login").value = self.customer_id
637        self.browser.getControl(name="form.password").value = 'cpwd'
638        self.browser.getControl("Login").click()
639        self.assertEqual(self.browser.url, self.customer_path)
640        self.assertTrue('You logged in' in self.browser.contents)
641        # Status has changed
642        self.assertEqual(self.customer.state, 'started')
643        # Change password
644        self.browser.getLink("Change password").click()
645        self.browser.getControl(name="change_password").value = 'pw'
646        self.browser.getControl(
647            name="change_password_repeat").value = 'pw'
648        self.browser.getControl("Save").click()
649        self.assertTrue('Password must have at least' in self.browser.contents)
650        self.browser.getControl(name="change_password").value = 'new_password'
651        self.browser.getControl(
652            name="change_password_repeat").value = 'new_passssword'
653        self.browser.getControl("Save").click()
654        self.assertTrue('Passwords do not match' in self.browser.contents)
655        self.browser.getControl(name="change_password").value = 'new_password'
656        self.browser.getControl(
657            name="change_password_repeat").value = 'new_password'
658        self.browser.getControl("Save").click()
659        self.assertTrue('Password changed' in self.browser.contents)
660        # We are still logged in. Changing the password hasn't thrown us out.
661        self.browser.getLink("Base Data").click()
662        self.assertEqual(self.browser.url, self.customer_path)
663        # We can logout
664        self.browser.getLink("Logout").click()
665        self.assertTrue('You have been logged out' in self.browser.contents)
666        self.assertEqual(self.browser.url, 'http://localhost/app/index')
667        # We can login again with the new password
668        self.browser.getLink("Login").click()
669        self.browser.open(self.login_path)
670        self.browser.getControl(name="form.login").value = self.customer_id
671        self.browser.getControl(name="form.password").value = 'new_password'
672        self.browser.getControl("Login").click()
673        self.assertEqual(self.browser.url, self.customer_path)
674        self.assertTrue('You logged in' in self.browser.contents)
675        return
676
677    def test_customer_edit_upload_upload_and_request(self):
678        # Customer cant login if their password is not set
679        self.browser.open(self.login_path)
680        self.browser.getControl(name="form.login").value = self.customer_id
681        self.browser.getControl(name="form.password").value = 'cpwd'
682        self.browser.getControl("Login").click()
683        self.assertMatches(
684            '...You logged in...', self.browser.contents)
685        self.browser.getLink("Edit").click()
686        self.browser.getControl(name="form.email").value = 'new_email@aa.ng'
687        self.browser.getControl("Save", index=0).click()
688        self.assertMatches('...Form has been saved...',
689                           self.browser.contents)
690        self.browser.getControl("Save and request registration").click()
691        self.assertMatches('...Passport picture is missing...',
692                           self.browser.contents)
693        self.assertEqual(self.customer.state, 'started')
694        # Customer must upload a passport picture. We are already on
695        # the upload page.
696        ctrl = self.browser.getControl(name='passporteditupload')
697        file_obj = open(SAMPLE_IMAGE, 'rb')
698        file_ctrl = ctrl.mech_control
699        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
700        self.browser.getControl(
701            name='upload_passporteditupload').click()
702        self.assertTrue(
703            'src="http://localhost/app/customers/K1000000/passport.jpg"'
704            in self.browser.contents)
705        self.browser.getControl(name="CANCEL").click()
706        self.assertEqual(self.browser.url, self.customer_path)
707        self.browser.getLink("Edit").click()
708        self.browser.getControl("Save and request registration").click()
709        self.assertMatches('...Registration form has been submitted...',
710                           self.browser.contents)
711        self.assertEqual(self.customer.state, 'requested')
712        # Customer can view history
713        self.browser.getLink("History").click()
714        self.assertMatches('...Customer created by system...',
715            self.browser.contents)
716
717    def test_customer_login(self):
718        # Customer cant login if their password is not set
719        self.customer.password = None
720        self.browser.open(self.login_path)
721        self.browser.getControl(name="form.login").value = self.customer_id
722        self.browser.getControl(name="form.password").value = 'cpwd'
723        self.browser.getControl("Login").click()
724        self.assertTrue(
725            'You entered invalid credentials.' in self.browser.contents)
726        # We set the password again
727        IUserAccount(
728            self.app['customers'][self.customer_id]).setPassword('cpwd')
729        # Customers can't login if their account is suspended/deactivated
730        self.customer.suspended = True
731        self.browser.open(self.login_path)
732        self.browser.getControl(name="form.login").value = self.customer_id
733        self.browser.getControl(name="form.password").value = 'cpwd'
734        self.browser.getControl("Login").click()
735        self.assertMatches(
736            '...<div class="alert alert-warning">'
737            'Your account has been deactivated.</div>...',
738            self.browser.contents)
739        # If suspended_comment is set this message will be flashed instead
740        self.customer.suspended_comment = u'Aetsch baetsch!'
741        self.browser.getControl(name="form.login").value = self.customer_id
742        self.browser.getControl(name="form.password").value = 'cpwd'
743        self.browser.getControl("Login").click()
744        self.assertMatches(
745            '...<div class="alert alert-warning">Aetsch baetsch!</div>...',
746            self.browser.contents)
747        self.customer.suspended = False
748        # Customers can't login if a temporary password has been set and
749        # is not expired
750        self.app['customers'][self.customer_id].setTempPassword(
751            'anybody', 'temp_cpwd')
752        self.browser.open(self.login_path)
753        self.browser.getControl(name="form.login").value = self.customer_id
754        self.browser.getControl(name="form.password").value = 'cpwd'
755        self.browser.getControl("Login").click()
756        self.assertMatches(
757            '...Your account has been temporarily deactivated...',
758            self.browser.contents)
759        # The customer can login with the temporary password
760        self.browser.open(self.login_path)
761        self.browser.getControl(name="form.login").value = self.customer_id
762        self.browser.getControl(name="form.password").value = 'temp_cpwd'
763        self.browser.getControl("Login").click()
764        self.assertMatches(
765            '...You logged in...', self.browser.contents)
766        # Customer can view the base data
767        self.browser.open(self.customer_path)
768        self.assertEqual(self.browser.headers['Status'], '200 Ok')
769        self.assertEqual(self.browser.url, self.customer_path)
770        # When the password expires ...
771        delta = timedelta(minutes=11)
772        self.app['customers'][self.customer_id].temp_password[
773            'timestamp'] = datetime.utcnow() - delta
774        self.app['customers'][self.customer_id]._p_changed = True
775        # ... the customer will be automatically logged out
776        self.assertRaises(
777            Unauthorized, self.browser.open, self.customer_path)
778        # Then the customer can login with the original password
779        self.browser.open(self.login_path)
780        self.browser.getControl(name="form.login").value = self.customer_id
781        self.browser.getControl(name="form.password").value = 'cpwd'
782        self.browser.getControl("Login").click()
783        self.assertMatches(
784            '...You logged in...', self.browser.contents)
785
786    def test_change_password_request(self):
787        self.browser.open('http://localhost/app/changepw')
788        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
789        self.browser.getControl("Send login credentials").click()
790        self.assertTrue('An email with' in self.browser.contents)
791
792
793class CustomerRegistrationTests(CustomersFullSetup):
794    # Tests for customer registration
795
796    layer = FunctionalLayer
797
798    def test_request_pw(self):
799        # Customer with wrong number can't be found.
800        self.browser.open('http://localhost/app/requestpw')
801        self.browser.getControl(name="form.firstname").value = 'Anna'
802        self.browser.getControl(name="form.number").value = 'anynumber'
803        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
804        self.browser.getControl("Send login credentials").click()
805        self.assertTrue('No customer found.'
806            in self.browser.contents)
807        # Anonymous is not informed that firstname verification failed.
808        # It seems that the record doesn't exist.
809        self.browser.open('http://localhost/app/requestpw')
810        self.browser.getControl(name="form.firstname").value = 'Johnny'
811        self.browser.getControl(name="form.number").value = '123'
812        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
813        self.browser.getControl("Send login credentials").click()
814        self.assertTrue('No customer found.'
815            in self.browser.contents)
816        # Even with the correct firstname we can't register if a
817        # password has been set and used.
818        self.browser.getControl(name="form.firstname").value = 'Anna'
819        self.browser.getControl(name="form.number").value = '123'
820        self.browser.getControl("Send login credentials").click()
821        self.assertTrue('Your password has already been set and used.'
822            in self.browser.contents)
823        self.browser.open('http://localhost/app/requestpw')
824        self.app['customers'][self.customer_id].password = None
825        # The firstname field, used for verification, is not case-sensitive.
826        self.browser.getControl(name="form.firstname").value = 'aNNa'
827        self.browser.getControl(name="form.number").value = '123'
828        self.browser.getControl(name="form.email").value = 'new@yy.zz'
829        self.browser.getControl("Send login credentials").click()
830        # Yeah, we succeded ...
831        self.assertTrue('Your request was successful.'
832            in self.browser.contents)
833        # ... and  customer can be found in the catalog via the email address
834        cat = queryUtility(ICatalog, name='customers_catalog')
835        results = list(
836            cat.searchResults(
837            email=('new@yy.zz', 'new@yy.zz')))
838        self.assertEqual(self.customer, results[0])
839        logfile = os.path.join(
840            self.app['datacenter'].storage, 'logs', 'main.log')
841        logcontent = open(logfile).read()
842        self.assertTrue(
843            'zope.anybody - customers.browser.CustomerRequestPasswordPage - '
844            '123 (K1000000) - new@yy.zz' in logcontent)
845        return
846
847    def test_create_account(self):
848        self.browser.open('http://localhost/app/createaccount')
849        self.browser.getControl(name="form.firstname").value = 'Ruben'
850        self.browser.getControl(name="form.lastname").value = 'Gonzales'
851        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
852        self.browser.getControl("Send login credentials").click()
853        # Email address exists.
854        self.assertTrue('Email address in use.' in self.browser.contents)
855        self.browser.getControl(name="form.email").value = 'newcustomer@xx.zz'
856        self.browser.getControl("Send login credentials").click()
857        self.assertTrue(
858            'Your request was successful.' in self.browser.contents)
859        # Customer can be found in the catalog via the email address
860        cat = queryUtility(ICatalog, name='customers_catalog')
861        results = list(
862            cat.searchResults(
863            email=('newcustomer@xx.zz', 'newcustomer@xx.zz')))
864        self.assertEqual(self.app['customers']['K1000001'], results[0])
865        self.assertEqual(self.app['customers']['K1000001'].firstname, 'Ruben')
866        self.assertEqual(
867            self.app['customers']['K1000001'].lastname, 'Gonzales')
868        logfile = os.path.join(
869            self.app['datacenter'].storage, 'logs', 'main.log')
870        logcontent = open(logfile).read()
871        self.assertTrue(
872            'zope.anybody - customers.browser.CustomerCreateAccountPage - '
873            'K1000001 - newcustomer@xx.zz' in logcontent)
874        return
875
876
877class CustomerDataExportTests(CustomersFullSetup, FunctionalAsyncTestCase):
878    # Tests for CustomersContainer class views and pages
879
880    layer = FunctionalLayer
881
882    def wait_for_export_job_completed(self):
883        # helper function waiting until the current export job is completed
884        manager = getUtility(IJobManager)
885        job_id = self.app['datacenter'].running_exports[0][0]
886        job = manager.get(job_id)
887        wait_for_result(job)
888        return job_id
889
890    def test_datacenter_export(self):
891        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
892        self.browser.open('http://localhost/app/datacenter/@@export')
893        self.browser.getControl(name="exporter").value = ['customers']
894        self.browser.getControl("Create CSV file").click()
895
896        # When the job is finished and we reload the page...
897        job_id = self.wait_for_export_job_completed()
898        # ... the csv file can be downloaded ...
899        self.browser.open('http://localhost/app/datacenter/@@export')
900        self.browser.getLink("Download").click()
901        self.assertEqual(self.browser.headers['content-type'],
902            'text/csv; charset=UTF-8')
903        self.assertTrue(
904            'filename="WAeUP.Ikoba_customers_%s.csv' % job_id in
905            self.browser.headers['content-disposition'])
906        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
907        job_id = self.app['datacenter'].running_exports[0][0]
908        # ... and discarded
909        self.browser.open('http://localhost/app/datacenter/@@export')
910        self.browser.getControl("Discard").click()
911        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
912        # Creation, downloading and discarding is logged
913        logfile = os.path.join(
914            self.app['datacenter'].storage, 'logs', 'datacenter.log')
915        logcontent = open(logfile).read()
916        self.assertTrue(
917            'zope.mgr - browser.pages.ExportCSVPage - exported: '
918            'customers, job_id=%s'
919            % job_id in logcontent
920            )
921        self.assertTrue(
922            'zope.mgr - browser.pages.ExportCSVView - downloaded: '
923            'WAeUP.Ikoba_customers_%s.csv, job_id=%s'
924            % (job_id, job_id) in logcontent
925            )
926        self.assertTrue(
927            'zope.mgr - browser.pages.ExportCSVPage - discarded: '
928            'job_id=%s' % job_id in logcontent
929            )
930
931
932class DocumentUITests(CustomersFullSetup):
933    # Tests for customer document related views and pages
934
935    def test_manage_document(self):
936        # Managers can access the pages of customer documentsconter
937        # and can perform actions
938        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
939        self.browser.open(self.customer_path)
940        self.assertEqual(self.browser.headers['Status'], '200 Ok')
941        self.assertEqual(self.browser.url, self.customer_path)
942        self.browser.open(self.customer_path)
943        self.browser.getLink("Documents", index=1).click()
944        self.browser.getControl("Add document").click()
945        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
946        self.browser.getControl(name="form.title").value = 'My PDF Document'
947        self.browser.getControl("Add document").click()
948        self.assertTrue('PDF Document added.' in self.browser.contents)
949        docid = [i for i in self.customer['documents'].keys()
950                 if len(i) > 10][0]
951        document = self.customer['documents'][docid]
952
953        # Document can be edited
954        self.browser.getLink(docid[:9]).click()
955        self.browser.getLink("Manage").click()
956        self.browser.getControl(name="form.title").value = 'My second doc'
957        self.browser.getControl("Save").click()
958        self.assertTrue('Form has been saved.' in self.browser.contents)
959        self.browser.getLink("View").click()
960        self.assertEqual(self.browser.url,
961            self.documents_path + '/' + docid + '/index')
962
963        # Transitions can be performed
964        self.browser.getLink("Transition").click()
965        self.browser.getControl(name="transition").value = ['submit']
966        self.browser.getControl("Apply").click()
967        self.browser.getLink("Transition").click()
968        # Document can only be verified if customer is approved
969        self.browser.getControl(name="transition").value = ['verify']
970        self.browser.getControl("Apply").click()
971        self.assertTrue(
972            'Customer has not yet been approved' in self.browser.contents)
973        IWorkflowState(self.customer).setState(APPROVED)
974        # Document can only be verified if files have been uploaded before
975        self.browser.getLink("Transition").click()
976        self.browser.getControl(name="transition").value = ['verify']
977        self.browser.getControl("Apply").click()
978        self.assertTrue('No file uploaded' in self.browser.contents)
979        self.assertEqual(document.state, 'submitted')
980        # We set state here manually (verification is tested in
981        # test_verify_document)
982        IWorkflowState(document).setState(VERIFIED)
983
984        # Manage button and form is no longer available
985        self.browser.open(self.documents_path + '/' + docid + '/index')
986        self.assertFalse(
987            'href="http://localhost/app/customers/K1000000/'
988            'documents/%s/manage"'
989            % docid in self.browser.contents)
990        self.browser.open(self.documents_path + '/' + docid + '/manage')
991        self.assertTrue(
992            'The requested form is locked (read-only)'
993            in self.browser.contents)
994
995        # Documents can be removed
996        self.browser.getLink("Documents", index=1).click()
997        ctrl = self.browser.getControl(name='val_id')
998        ctrl.getControl(value=document.document_id).selected = True
999        self.browser.getControl("Remove selected", index=0).click()
1000        self.assertTrue('Successfully removed' in self.browser.contents)
1001
1002        # All actions are being logged
1003        logfile = os.path.join(
1004            self.app['datacenter'].storage, 'logs', 'customers.log')
1005        logcontent = open(logfile).read()
1006
1007        self.assertTrue(
1008            'INFO - system - K1000000 - DOC1 - Document created'
1009            in logcontent)
1010        self.assertTrue(
1011            'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
1012            '- K1000000 - added: PDF Document %s'
1013            % document.document_id in logcontent)
1014        self.assertTrue(
1015            'INFO - zope.mgr - customers.browser.DocumentManageFormPage '
1016            '- K1000000 - %s - saved: title' % docid
1017            in logcontent)
1018        self.assertTrue(
1019            'INFO - zope.mgr - K1000000 - %s - Submitted for verification'
1020            % docid in logcontent)
1021        self.assertTrue(
1022            'INFO - zope.mgr - customers.browser.DocumentsManageFormPage '
1023            '- K1000000 - removed: %s' % docid
1024            in logcontent)
1025
1026    def test_edit_sample_document(self):
1027        # Customers can manage documents under certain conditions
1028        self.browser.open(self.login_path)
1029        self.browser.getControl(name="form.login").value = self.customer_id
1030        self.browser.getControl(name="form.password").value = 'cpwd'
1031        self.browser.getControl("Login").click()
1032        self.assertMatches(
1033            '...You logged in...', self.browser.contents)
1034        self.browser.getLink("Documents").click()
1035        self.browser.getControl("Add document").click()
1036        self.assertTrue(
1037            'The requested form is locked' in self.browser.contents)
1038        # Customer is in wrong state
1039        IWorkflowState(self.customer).setState(APPROVED)
1040        self.browser.getControl("Add document").click()
1041        self.browser.getControl(name="doctype").value = [
1042            'CustomerSampleDocument']
1043        self.browser.getControl(name="form.title").value = 'My Sample Document'
1044        self.browser.getControl("Add document").click()
1045        self.assertTrue('Sample Document added.' in self.browser.contents)
1046        docid = [i for i in self.customer['documents'].keys()
1047                 if len(i) > 10][0]
1048        document = self.customer['documents'][docid]
1049        self.browser.getControl(name="form.title").value = 'My second doc'
1050        self.browser.getControl("Save").click()
1051        self.assertEqual(document.title, 'My second doc')
1052        self.assertTrue('Form has been saved.' in self.browser.contents)
1053        self.browser.getLink("View").click()
1054        self.assertEqual(
1055            self.browser.url, self.documents_path + '/%s/index' % docid)
1056        # Customer can upload a document.
1057        self.browser.getLink("Edit").click()
1058        ctrl = self.browser.getControl(name='samplescaneditupload')
1059        file_obj = open(SAMPLE_IMAGE, 'rb')
1060        file_ctrl = ctrl.mech_control
1061        file_ctrl.add_file(file_obj, filename='my_document.jpg')
1062        self.browser.getControl(
1063            name='upload_samplescaneditupload').click()
1064        self.assertTrue(
1065            'href="http://localhost/app/customers/K1000000/'
1066            'documents/%s/sample"'
1067            % docid in self.browser.contents)
1068        # Customer can submit the form. The form is also saved.
1069        self.browser.getControl(name="form.title").value = 'My third doc'
1070        self.browser.getControl("Final Submit").click()
1071        self.assertEqual(document.title, 'My third doc')
1072        self.assertEqual(document.state, 'submitted')
1073        self.assertTrue(
1074            'Document State: submitted for verification'
1075            in self.browser.contents)
1076        # Customer can't edit the document once it has been submitted
1077        self.browser.open(self.documents_path + '/%s/edit' % docid)
1078        self.assertTrue(
1079            'The requested form is locked' in self.browser.contents)
1080
1081    def test_manage_upload_sample_file(self):
1082        # Managers can upload a file via the DocumentManageFormPage
1083        # The image is stored even if form has errors
1084        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1085        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1086        # Create a pseudo image file and select it to be uploaded
1087        image = open(SAMPLE_IMAGE, 'rb')
1088        ctrl = self.browser.getControl(name='samplescanmanageupload')
1089        file_ctrl = ctrl.mech_control
1090        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1091        # The Save action does not upload files
1092        self.browser.getControl("Save").click()  # submit form
1093        self.assertFalse(
1094            'href="http://localhost/app/customers/K1000000/'
1095            'documents/DOC1/sample"'
1096            in self.browser.contents)
1097        # ... but the correct upload submit button does
1098        image = open(SAMPLE_IMAGE)
1099        ctrl = self.browser.getControl(name='samplescanmanageupload')
1100        file_ctrl = ctrl.mech_control
1101        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1102        self.browser.getControl(
1103            name='upload_samplescanmanageupload').click()
1104        self.assertTrue(
1105            'href="http://localhost/app/customers/K1000000/'
1106            'documents/DOC1/sample"'
1107            in self.browser.contents)
1108        # Browsing the link shows a real image
1109        self.browser.open('sample')
1110        self.assertEqual(
1111            self.browser.headers['content-type'], 'image/jpeg')
1112        self.assertEqual(len(self.browser.contents), 2787)
1113        # We can't reupload a file. The existing file must be deleted first.
1114        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1115        self.assertFalse(
1116            'upload_samplescanmanageupload' in self.browser.contents)
1117        # File must be deleted first
1118        self.browser.getControl(name='delete_samplescanmanageupload').click()
1119        self.assertTrue(
1120            'sample deleted' in self.browser.contents)
1121        # Uploading a file which is bigger than 150k will raise an error
1122        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
1123        ctrl = self.browser.getControl(name='samplescanmanageupload')
1124        file_ctrl = ctrl.mech_control
1125        file_ctrl.add_file(big_image, filename='my_sample_scan.jpg')
1126        self.browser.getControl(
1127            name='upload_samplescanmanageupload').click()
1128        self.assertTrue(
1129            'Uploaded file is too big' in self.browser.contents)
1130        # We do not rely on filename extensions given by uploaders
1131        image = open(SAMPLE_IMAGE, 'rb')  # a jpg-file
1132        ctrl = self.browser.getControl(name='samplescanmanageupload')
1133        file_ctrl = ctrl.mech_control
1134        # Tell uploaded file is bmp
1135        file_ctrl.add_file(image, filename='my_sample_scan.bmp')
1136        self.browser.getControl(
1137            name='upload_samplescanmanageupload').click()
1138        self.assertTrue(
1139            # jpg file was recognized
1140            'File sample.jpg uploaded.' in self.browser.contents)
1141        # Delete file again
1142        self.browser.getControl(name='delete_samplescanmanageupload').click()
1143        self.assertTrue(
1144            'sample deleted' in self.browser.contents)
1145        # File names must meet several conditions
1146        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
1147        ctrl = self.browser.getControl(name='samplescanmanageupload')
1148        file_ctrl = ctrl.mech_control
1149        file_ctrl.add_file(bmp_image, filename='my_sample_scan.bmp')
1150        self.browser.getControl(
1151            name='upload_samplescanmanageupload').click()
1152        self.assertTrue('Only the following extensions are allowed'
1153            in self.browser.contents)
1154
1155    def test_verify_document(self):
1156        IWorkflowState(self.customer).setState('approved')
1157        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1158        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1159        image = open(SAMPLE_IMAGE, 'rb')
1160        ctrl = self.browser.getControl(name='samplescanmanageupload')
1161        file_ctrl = ctrl.mech_control
1162        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1163        self.browser.getControl(
1164            name='upload_samplescanmanageupload').click()
1165        IWorkflowState(self.document).setState(SUBMITTED)
1166        # Only after verifying the document, sample_md5 is set
1167        self.assertEqual(
1168            getattr(self.document, 'sample_md5', None), None)
1169        self.browser.open(self.documents_path + '/DOC1/trigtrans')
1170        self.browser.getControl(name="transition").value = ['verify']
1171        self.browser.getControl("Apply").click()
1172        self.assertEqual(
1173            getattr(self.document, 'sample_md5', None),
1174                    '1d1ab893e87c240afb2104d61ddfe180')
1175
1176    def test_manage_upload_pdf_file(self):
1177        # Managers can upload a file via the DocumentManageFormPage
1178        # The image is stored even if form has errors
1179        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1180        self.browser.open(self.customer_path + '/documents')
1181        self.browser.getControl("Add document").click()
1182        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
1183        self.browser.getControl(name="form.title").value = 'My PDF Document'
1184        self.browser.getControl("Add document").click()
1185        docid = [
1186            i for i in self.customer['documents'].keys() if len(i) > 10][0]
1187        self.browser.open(self.documents_path + '/%s/manage' % docid)
1188        # Create a pseudo image file and select it to be uploaded
1189        image = open(SAMPLE_IMAGE, 'rb')
1190        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1191        file_ctrl = ctrl.mech_control
1192        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1193        self.browser.getControl(
1194            name='upload_pdfscanmanageupload').click()
1195        self.assertTrue(
1196            'pdf file format expected' in self.browser.contents)
1197        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1198        file_ctrl = ctrl.mech_control
1199        file_ctrl.add_file(image, filename='my_sample_scan.pdf')
1200        self.browser.getControl(
1201            name='upload_pdfscanmanageupload').click()
1202        self.assertTrue(
1203            'Could not determine file type' in self.browser.contents)
1204        pdf = open(SAMPLE_PDF, 'rb')
1205        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1206        file_ctrl = ctrl.mech_control
1207        file_ctrl.add_file(pdf, filename='my_sample_scan.pdf')
1208        self.browser.getControl(
1209            name='upload_pdfscanmanageupload').click()
1210        self.assertTrue(
1211            'href="http://localhost/app/customers/K1000000/'
1212            'documents/%s/sample.pdf">%s.pdf</a>'
1213            % (docid, docid[:9]) in self.browser.contents)
1214        # Browsing the link shows a real pdf
1215        self.browser.open('sample.pdf')
1216        self.assertEqual(
1217            self.browser.headers['content-type'], 'application/pdf')
1218        # The name of the downloaded file will be different
1219        self.assertEqual(
1220            self.browser.headers['Content-Disposition'],
1221            'attachment; filename="%s.pdf' % docid[:9])
1222
1223    def test_view_slips(self):
1224        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1225        # Officers can open the document overview
1226        self.browser.open(self.customer_path + '/documents')
1227        self.browser.getLink("Download documents overview").click()
1228        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1229        self.assertEqual(
1230            self.browser.headers['Content-Type'], 'application/pdf')
1231        path = os.path.join(samples_dir(), 'documents_overview_slip.pdf')
1232        open(path, 'wb').write(self.browser.contents)
1233        print "Sample PDF overview_slip.pdf written to %s" % path
1234        # Officers can open document slips which shows a thumbnail of
1235        # the jpeg file attached.
1236        file_id = IFileStoreNameChooser(self.document).chooseName(
1237            attr='sample.jpg')
1238        fs = ExtFileStore(root=self.dc_root)
1239        jpegfile = open(SAMPLE_IMAGE, 'rb')
1240        fs.createFile(file_id, jpegfile)
1241        self.browser.open(self.customer_path + '/documents/DOC1')
1242        self.browser.getLink("Download document slip").click()
1243        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1244        self.assertEqual(
1245            self.browser.headers['Content-Type'], 'application/pdf')
1246        path = os.path.join(samples_dir(), 'document_slip.pdf')
1247        open(path, 'wb').write(self.browser.contents)
1248        print "Sample document_slip.pdf written to %s" % path
1249        # Officers can open merged pdf document slips
1250        pdfdocument = createObject('waeup.CustomerPDFDocument')
1251        pdfdocument.title = u'My first document'
1252        self.customer['documents'].addDocument(pdfdocument)
1253        # Add pdf file
1254        file_id = IFileStoreNameChooser(pdfdocument).chooseName(
1255            attr='sample.pdf')
1256        fs = ExtFileStore(root=self.dc_root)
1257        pdffile = open(SAMPLE_PDF, 'rb')
1258        fs.createFile(file_id, pdffile)
1259        docid = [i for i in self.customer['documents'].keys()
1260                 if len(i) > 10][0]
1261        self.browser.open(self.customer_path + '/documents/' + docid)
1262        self.browser.getLink("Download document slip").click()
1263        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1264        self.assertEqual(
1265            self.browser.headers['Content-Type'], 'application/pdf')
1266        path = os.path.join(samples_dir(), 'pdfdocument_slip.pdf')
1267        open(path, 'wb').write(self.browser.contents)
1268        print "Sample pdfdocument_slip.pdf written to %s" % path
1269
1270    def test_get_setmd5_file(self):
1271        # A proper file name chooser is registered for customer documents.
1272        # This is not a UI test. It's just a functional test.
1273        file_id = IFileStoreNameChooser(self.document).chooseName(
1274            attr='sample')
1275        fs = ExtFileStore(root=self.dc_root)
1276        fs.createFile(file_id, StringIO('my sample 1'))
1277        result = fs.getFileByContext(self.document, attr='sample')
1278        self.assertEqual(
1279            file_id, '__file-customerdocument__01000/'
1280            'K1000000/sample_DOC1_K1000000')
1281        self.assertEqual(result.read(), 'my sample 1')
1282        self.assertEqual(
1283            self.document.connected_files[0][1].read(), 'my sample 1')
1284        self.document.setMD5()
1285        self.assertEqual(
1286            self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
1287        return
1288
1289
1290class ContractUITests(CustomersFullSetup):
1291    # Tests for contract related views and pages
1292
1293    never_ending_button_text = (
1294        'Select payment method (final submission)')
1295
1296    def setup_payment(self):
1297        payer = IPayer(self.customer)
1298        payable = IPayable(self.contract)
1299        self.payment = Payment(payer, payable)
1300        self.payment.gateway_service = 'demo_creditcard'
1301        self.payment.state = STATE_PAID
1302        self.payment.title = u'My payment'
1303        self.app['payments'][self.payment.payment_id] = self.payment
1304
1305    def add_product_option(self, contract):
1306        prodoption = ProductOption()
1307        prodoption.title = u'Any product option'
1308        prodoption.fee = Decimal('88.8')
1309        prodoption.currency = 'EUR'
1310        contract.product_options = [prodoption, ]
1311
1312    def test_multiple_currencies(self):
1313        prodoption1 = ProductOption()
1314        prodoption1.title = u'Any product option in Euros'
1315        prodoption1.fee = Decimal('88.8')
1316        prodoption1.currency = 'EUR'
1317        prodoption2 = ProductOption()
1318        prodoption2.title = u'Any product option in Dollars'
1319        prodoption2.fee = Decimal('99.9')
1320        prodoption2.currency = 'USD'
1321        self.assertRaises(CurrencyMismatch,
1322                          setattr, self.contract,
1323                          'product_options',
1324                          [prodoption1, prodoption2]
1325                          )
1326
1327    def prepare_payment_select(self):
1328        IWorkflowState(self.customer).setState('approved')
1329        IWorkflowState(self.document).setState('verified')
1330        self.contract.document_object = self.document
1331        self.add_product_option(self.contract)
1332        IWorkflowState(self.contract).setState('created')
1333        # login as customer
1334        self.browser.open(self.login_path)
1335        self.browser.getControl(name="form.login").value = self.customer_id
1336        self.browser.getControl(name="form.password").value = 'cpwd'
1337        self.browser.getControl("Login").click()
1338
1339    def test_manage_contract(self):
1340        # Managers can access the pages of customer contractsconter
1341        # and can perform actions
1342        IWorkflowState(self.customer).setState(APPROVED)
1343        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1344        self.browser.open(self.customer_path)
1345        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1346        self.assertEqual(self.browser.url, self.customer_path)
1347        self.browser.open(self.customer_path)
1348        self.browser.getLink("Contracts").click()
1349        self.browser.getControl("Add contract").click()
1350        self.browser.getControl(name="contype").value = ['SampleContract']
1351        self.browser.getControl("Add contract").click()
1352        self.assertTrue('Sample Contract added.' in self.browser.contents)
1353        conid = [i for i in self.customer['contracts'].keys()
1354                 if len(i) > 10][0]
1355        contract = self.customer['contracts'][conid]
1356        self.assertEqual(
1357            self.browser.url,
1358            self.contracts_path + '/%s/selectproduct' % conid)
1359        # SAM is in the correct contract_category
1360        self.assertTrue('<option value="SAM">' in self.browser.contents)
1361        # So far last_product_id is None.
1362        self.assertTrue(
1363            self.customer['contracts'][conid].last_product_id is None)
1364        self.browser.getControl(name="form.product_object").value = ['SAM']
1365        self.browser.getControl("Save and proceed").click()
1366        self.assertEqual(
1367            self.browser.url, self.contracts_path + '/%s/manage' % conid)
1368        self.browser.getLink("View").click()
1369        self.assertEqual(
1370            self.browser.url, self.contracts_path + '/%s/index' % conid)
1371        self.assertEqual(contract.tc_dict, {'en': u'Hello world'})
1372
1373        # Transitions can be performed
1374        self.browser.getLink("Transition").click()
1375        self.browser.getControl(name="transition").value = ['submit']
1376        self.browser.getControl("Apply").click()
1377        self.browser.getLink("Transition").click()
1378        self.browser.getControl(name="transition").value = ['approve']
1379        self.browser.getControl("Apply").click()
1380        self.assertEqual(contract.state, 'approved')
1381
1382        # Even in state approved the official use data can be edited
1383        self.browser.open(self.contracts_path + '/%s/index' % conid)
1384        self.browser.getLink("Manage official data").click()
1385        self.browser.getControl(name="form.comment").value = u'Nice place'
1386        self.browser.getControl("Save").click()
1387        self.assertEqual(contract.comment, 'Nice place')
1388        self.assertTrue('Form has been saved.' in self.browser.contents)
1389
1390        # Contracts can be removed
1391        self.browser.getLink("Contracts").click()
1392        ctrl = self.browser.getControl(name='val_id')
1393        ctrl.getControl(value=contract.contract_id).selected = True
1394        self.browser.getControl("Remove selected", index=0).click()
1395        self.assertTrue('Successfully removed' in self.browser.contents)
1396
1397        # All actions are being logged
1398        logfile = os.path.join(
1399            self.app['datacenter'].storage, 'logs', 'customers.log')
1400        logcontent = open(logfile).read()
1401        self.assertTrue(
1402            'INFO - zope.mgr - K1000000 - %s - Contract created' % conid
1403            in logcontent)
1404        self.assertTrue(
1405            'INFO - zope.mgr - customers.browser.ContractAddFormPage '
1406            '- K1000000 - added: Sample Contract %s'
1407            % contract.contract_id in logcontent)
1408        self.assertTrue(
1409            'INFO - zope.mgr - K1000000 - %s - Contract submitted' % conid
1410            in logcontent)
1411        self.assertTrue(
1412            'INFO - zope.mgr - K1000000 - %s - Contract approved' % conid
1413            in logcontent)
1414        self.assertTrue(
1415            'INFO - zope.mgr - customers.browser.ContractsFormPage '
1416            '- K1000000 - removed: %s' % conid
1417            in logcontent)
1418
1419    def test_edit_sample_contract(self):
1420        # We add a second product.
1421        product = createObject('waeup.Product')
1422        product.product_id = u'LIC'
1423        product.title = u'Our License Product'
1424        product.contract_category = u'license'
1425        product.valid_from = date(2014, 12, 4)
1426        self.app['products'].addProduct(product)
1427        # Customers can manage contracts under certain conditions
1428        self.browser.open(self.login_path)
1429        self.browser.getControl(name="form.login").value = self.customer_id
1430        self.browser.getControl(name="form.password").value = 'cpwd'
1431        self.browser.getControl("Login").click()
1432        self.assertMatches(
1433            '...You logged in...', self.browser.contents)
1434        self.browser.getLink("Contracts").click()
1435        # Customer is in wrong state
1436        self.assertFalse('Add contract' in self.browser.contents)
1437        self.browser.open(self.contracts_path + '/addcontract')
1438        self.assertTrue(
1439            'The requested form is locked' in self.browser.contents)
1440        IWorkflowState(self.customer).setState(APPROVED)
1441        self.browser.open(self.contracts_path)
1442        # Now customer can add a contract
1443        self.browser.getControl("Add contract").click()
1444        self.browser.getControl(name="contype").value = ['SampleContract']
1445        self.browser.getControl("Add contract").click()
1446        self.assertTrue('Sample Contract added.' in self.browser.contents)
1447        conid = [i for i in self.customer['contracts'].keys()
1448                 if len(i) > 10][0]
1449        contract = self.customer['contracts'][conid]
1450        self.assertEqual(
1451            self.browser.url,
1452            self.contracts_path + '/%s/selectproduct' % conid)
1453        # SAM is in the correct contract_category ...
1454        self.assertTrue('<option value="SAM">' in self.browser.contents)
1455        # ... but LIC not.
1456        self.assertFalse('<option value="LIC">' in self.browser.contents)
1457        # So far last_product_id is None.
1458        self.assertTrue(
1459            self.customer['contracts'][conid].last_product_id is None)
1460        self.browser.getControl(name="form.product_object").value = ['SAM']
1461        self.browser.getControl("Save and proceed").click()
1462        self.assertEqual(
1463            self.browser.url, self.contracts_path + '/%s/edit' % conid)
1464        # Document is a required field on edit form page.
1465        self.browser.getControl("Save").click()
1466        self.assertTrue(
1467            'Document: <span class="error">Required input is missing.</span>'
1468            in self.browser.contents)
1469        # But our document can't be selected because it's not submitted
1470        self.assertFalse('My first document' in self.browser.contents)
1471        IWorkflowState(self.document).setState(SUBMITTED)
1472        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1473        self.browser.getControl(name="form.document_object").value = ['DOC1']
1474        self.browser.getControl("Save").click()
1475        # After saving the form, last_product_id and other attributes are set
1476        self.assertTrue('Form has been saved.' in self.browser.contents)
1477        self.assertEqual(
1478            self.customer['contracts'][conid].last_product_id, 'SAM')
1479        self.assertEqual(contract.title, 'Our Sample Product')
1480        self.assertEqual(contract.product_object, self.product)
1481        self.assertEqual(contract.document_object, self.document)
1482        # Saving the form again does not unset last_product_id
1483        self.browser.getControl("Save").click()
1484        self.assertEqual(
1485            self.customer['contracts'][conid].last_product_id, 'SAM')
1486        self.assertTrue('Form has been saved.' in self.browser.contents)
1487        # So far we have not yet set product options.
1488        # Unfortunately, we can't set them in test browser
1489        prodoption = ProductOption()
1490        prodoption.title = u'Any product option'
1491        prodoption.fee = Decimal('88.8')
1492        prodoption.currency = 'EUR'
1493        contract.product_options = [prodoption, ]
1494        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1495        # We can see both the stored and the recent product options
1496        # from the chosen product.
1497        self.assertTrue(
1498            '<option selected="selected" value="Any product option_88.8_EUR">'
1499            'Any product option @ 88.8 Euro</option>'
1500            in self.browser.contents)
1501        self.assertTrue('<option value="First option_99.9_USD">First option '
1502                        '@ 99.9 US Dollar</option>' in self.browser.contents)
1503        # In test browser we can at least replace the option
1504        self.browser.getControl(
1505            name="form.product_options.0.").value = ['First option_99.9_USD']
1506        self.assertEqual(
1507            contract.product_options[0].title, 'Any product option')
1508        self.browser.getControl("Save").click()
1509        self.assertEqual(contract.product_options[0].title, 'First option')
1510        self.browser.getLink("View").click()
1511        self.assertTrue(
1512            '<span>First option @ 99.9 US Dollar</span>'
1513            in self.browser.contents)
1514        self.assertEqual(
1515            self.browser.url, self.contracts_path + '/%s/index' % conid)
1516        # An href attribute is referring to the document and product objects
1517        self.assertTrue('<a href="http://localhost/app/products/SAM">SAM -'
1518            in self.browser.contents)
1519        self.assertTrue(
1520            '<a href="http://localhost/app/customers/K1000000/'
1521            'documents/DOC1">DOC1 -'
1522            in self.browser.contents)
1523        # Customer can submit the form if confirmation box is ticket.
1524        # The form is also saved.
1525        self.browser.getLink("Edit").click()
1526        self.browser.getControl("Proceed to checkout").click()
1527        self.assertTrue(
1528            'confirm your acceptance of these by ticking'
1529            in self.browser.contents)
1530        self.assertEqual(contract.state, 'created')
1531        self.browser.getControl(name="confirm_tc").value = True
1532        self.browser.getControl("Proceed to checkout").click()
1533        self.assertEqual(contract.state, 'created')
1534        radio_ctrl = self.browser.getControl(name='gw')
1535        radio_ctrl.value = [radio_ctrl.options[0]]  # pick first payment opt
1536        self.browser.getControl("Select payment method").click()
1537        # Select demo payment
1538        self.browser.getControl(name="cc_number").value = '123456789012345'
1539        self.browser.getControl(name="csc").value = '1234'
1540        self.browser.getControl("Authorize Payment").click()
1541        self.assertTrue(
1542            'Your payment will now be verified'
1543            in self.browser.contents)
1544        self.assertEqual(contract.state, 'awaiting')
1545        # Customer can't edit the contract once it has been submitted
1546        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1547        self.assertTrue(
1548            'The requested form is locked' in self.browser.contents)
1549
1550    def test_view_slips(self):
1551        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1552        # Officers can open the contract overview
1553        self.browser.open(self.customer_path + '/contracts')
1554        self.browser.getLink("Download contracts overview").click()
1555        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1556        self.assertEqual(
1557            self.browser.headers['Content-Type'], 'application/pdf')
1558        path = os.path.join(samples_dir(), 'contracts_overview_slip.pdf')
1559        open(path, 'wb').write(self.browser.contents)
1560        print "Sample PDF overview_slip.pdf written to %s" % path
1561        # Officers can open contract slips.
1562        # First we add a submitted document and a product.
1563        IWorkflowState(self.document).setState(SUBMITTED)
1564        self.contract.document_object = self.document
1565        self.contract.product_object = self.product
1566        self.contract.tc_dict = {'en': u'<strong>Hello world</strong>'}
1567        self.contract.valid_from = date(2015, 12, 4)
1568        self.contract.valid_to = 'anything'
1569        self.contract.title = u'Contract Title'
1570        self.browser.open(self.customer_path + '/contracts/CON1')
1571        self.browser.getLink("Download contract slip").click()
1572        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1573        self.assertEqual(
1574            self.browser.headers['Content-Type'], 'application/pdf')
1575        path = os.path.join(samples_dir(), 'contract_slip.pdf')
1576        open(path, 'wb').write(self.browser.contents)
1577        print "Sample contract_slip.pdf written to %s" % path
1578
1579    def test_view_receipt(self):
1580        self.add_product_option(self.contract)
1581        self.setup_payment()
1582        IWorkflowState(self.document).setState(SUBMITTED)
1583        self.contract.document_object = self.document
1584        self.contract.product_object = self.product
1585        self.contract.title = u'Contract Title'
1586        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1587        # Officers can open the receipt
1588        self.browser.open(self.customer_path + '/contracts/CON1')
1589        self.browser.getLink("Download payment receipt").click()
1590        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1591        self.assertEqual(
1592            self.browser.headers['Content-Type'], 'application/pdf')
1593        path = os.path.join(samples_dir(), 'contract_payment_receipt.pdf')
1594        open(path, 'wb').write(self.browser.contents)
1595        print "Sample contract_payment_receipt.pdf written to %s" % path
1596
1597    def test_contract_approval(self):
1598        # This is not a UI test. It's just a functional test.
1599        self.assertRaises(ConstraintNotSatisfied,
1600            self.contract.document_object, self.document)
1601        # Document must be at least submitted
1602        IWorkflowState(self.document).setState('submitted')
1603        self.contract.document_object = self.document
1604        IWorkflowState(self.contract).setState('submitted')
1605        self.assertRaises(InvalidTransitionError,
1606            IWorkflowInfo(self.contract).fireTransition, 'approve')
1607        # Customer must be approved and
1608        # document must be verified for the approval of contracts
1609        IWorkflowState(self.document).setState('verified')
1610        IWorkflowState(self.customer).setState('approved')
1611        IWorkflowInfo(self.contract).fireTransition('approve')
1612        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
1613
1614    def test_contract_approval_in_UI(self):
1615        # Now let's see what the UI says when trying to approve a contract
1616        # with unverified documents.
1617        IWorkflowState(self.customer).setState('approved')
1618        IWorkflowState(self.document).setState('submitted')
1619        self.contract.document_object = self.document
1620        IWorkflowState(self.contract).setState('submitted')
1621        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1622        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1623        self.browser.getControl(name="transition").value = ['approve']
1624        self.browser.getControl("Apply").click()
1625        # InvalidTransitionError is catched
1626        self.assertTrue(
1627            '<div class="alert alert-warning">Attached documents '
1628            'must be verified first.</div>'
1629            in self.browser.contents)
1630        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1631        IWorkflowState(self.document).setState('verified')
1632        self.browser.getControl(name="transition").value = ['approve']
1633        self.browser.getControl("Apply").click()
1634        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
1635
1636    def test_select_payment(self):
1637        # select payment
1638        self.prepare_payment_select()
1639        self.browser.open('%s/CON1/edit' % self.contracts_path)
1640        self.browser.getControl("Proceed to checkout").click()
1641        self.assertTrue(
1642            "Select payment method" in self.browser.contents)
1643        self.assertTrue(
1644            'Credit Card (Demo Payments)' in self.browser.contents)
1645
1646    def test_select_payment_no_choice(self):
1647        # we get warned if no payment was selected
1648        self.prepare_payment_select()
1649        self.browser.open(
1650            '%s/CON1/select_payment_method' % self.contracts_path)
1651        self.browser.getControl(self.never_ending_button_text).click()
1652        self.assertTrue(
1653            'Please pick a payment method' in self.browser.contents)
1654
1655    def test_select_payment_demo_provider(self):
1656        # we can proceed with payments if we select a payment method
1657        self.prepare_payment_select()
1658        self.browser.open(
1659            '%s/CON1/select_payment_method' % self.contracts_path)
1660        radio_ctrl = self.browser.getControl(name='gw')
1661        radio_ctrl.displayValue = ['Credit Card (Demo Payments)']
1662        self.browser.getControl(self.never_ending_button_text).click()
1663
1664
1665class PaymentsUITests(CustomersFullSetup):
1666    # Tests for contract related views and pages
1667
1668    def setup_payment(self):
1669        payer = IPayer(self.customer)
1670        payable = IPayable(self.contract)
1671        self.payment = Payment(payer, payable)
1672        self.payment.gateway_service = 'demo_creditcard'
1673        self.payment.state = STATE_PAID
1674        self.payment.title = u'My payment'
1675        self.app['payments'][self.payment.payment_id] = self.payment
1676
1677    def add_product_option(self, contract):
1678        prodoption = ProductOption()
1679        prodoption.title = u'Any product option'
1680        prodoption.fee = Decimal('88.8')
1681        prodoption.currency = 'EUR'
1682        contract.product_options = [prodoption, ]
1683
1684    def test_view_payments(self):
1685        self.add_product_option(self.contract)
1686        self.setup_payment()
1687        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1688        self.browser.open(self.customer_path)
1689        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1690        self.assertEqual(self.browser.url, self.customer_path)
1691        self.browser.open(self.customer_path)
1692        self.browser.getLink("Payments", index=1).click()
1693        self.assertTrue(
1694            '<td>Credit Card (Demo Payments)</td>' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.