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

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

Contracts must not be added if there are no active products for this category.

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