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

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

Notify customer by email after customer and contract transitions.

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