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

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

Make contract_category custimizable in tests.

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