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

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

Replace error message: 'Email address in use.'

  • Property svn:keywords set to Id
File size: 72.1 KB
Line 
1## $Id: test_browser.py 12524 2015-01-30 11:19:26Z 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    def setup_customizable_params(self):
86        self._contract_category = u'sample'
87        self._document_factory = 'waeup.CustomerSampleDocument'
88        self._contract_factory = 'waeup.SampleContract'
89        return
90
91    def setUp(self):
92        super(CustomersFullSetup, self).setUp()
93        self.setup_customizable_params()
94        # Setup a sample site for each test
95        app = Company()
96        self.dc_root = tempfile.mkdtemp()
97        app['datacenter'].setStoragePath(self.dc_root)
98
99        # Prepopulate the ZODB...
100        self.getRootFolder()['app'] = app
101        # we add the site immediately after creation to the
102        # ZODB. Catalogs and other local utilities are not setup
103        # before that step.
104        self.app = self.getRootFolder()['app']
105        # Set site here. Some of the following setup code might need
106        # to access grok.getSite() and should get our new app then
107        setSite(app)
108
109        # Add some products
110        self.product = createObject('waeup.Product')
111        self.product.product_id = u'SAM'
112        self.product.title = u'Our Sample Product'
113        self.product.contract_category = self._contract_category
114        self.product.valid_from = date(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 = u'aa@aa.ng'
130        customer.phone = u'1234'
131        customer.date_of_birth = date(1981, 2, 4)
132        self.app['customers'].addCustomer(customer)
133        self.customer_id = customer.customer_id
134        self.customer = self.app['customers'][self.customer_id]
135        self.document = createObject(self._document_factory)
136        self.document.title = u'My first document'
137        self.document.document_id = u'DOC1'
138        self.assertRaises(
139            NotIdValue, setattr, self.document, 'document_id', u'id with spaces')
140        self.customer['documents'].addDocument(self.document)
141        self.contract = createObject(self._contract_factory)
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 in use.'
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 in use...',
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 = u'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,bb@aa.ng,1234,m,mypwd1
423Claus,Finau,2,1990-01-03,cc@aa.ng,1234,m,mypwd1
424Brit,Berson,2,1990-01-04,dd@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        self.assertEqual(self.customer.state, 'created')
503        # Login as customer impersonator
504        self.browser.open(self.login_path)
505        self.browser.getControl(name="form.login").value = 'mrofficer'
506        self.browser.getControl(name="form.password").value = 'mrofficersecret'
507        self.browser.getControl("Login").click()
508        self.assertMatches('...You logged in...', self.browser.contents)
509        self.browser.open(self.customer_path)
510        self.browser.getLink("Login as").click()
511        self.browser.getControl("Set password now").click()
512        temp_password = self.browser.getControl(name='form.password').value
513        self.browser.getControl("Login now").click()
514        self.assertMatches(
515            '...You successfully logged in as...', self.browser.contents)
516        # Status has changed
517        self.assertEqual(self.customer.state, 'started')
518        # We are logged in as customer and can see the 'My Data' tab
519        self.assertMatches(
520            '...<a href="#" class="dropdown-toggle" data-toggle="dropdown">...',
521            self.browser.contents)
522        self.assertMatches(
523            '...My Data...',
524            self.browser.contents)
525        self.browser.getLink("Logout").click()
526        # The customer can't login with the original password ...
527        self.browser.open(self.login_path)
528        self.browser.getControl(name="form.login").value = self.customer_id
529        self.browser.getControl(name="form.password").value = 'cpwd'
530        self.browser.getControl("Login").click()
531        self.assertMatches(
532            '...Your account has been temporarily deactivated...',
533            self.browser.contents)
534        # ... but with the temporary password
535        self.browser.open(self.login_path)
536        self.browser.getControl(name="form.login").value = self.customer_id
537        self.browser.getControl(name="form.password").value = temp_password
538        self.browser.getControl("Login").click()
539        self.assertMatches('...You logged in...', self.browser.contents)
540        # Creation of temp_password is properly logged
541        logfile = os.path.join(
542            self.app['datacenter'].storage, 'logs', 'customers.log')
543        logcontent = open(logfile).read()
544        self.assertTrue(
545            'mrofficer - customers.browser.LoginAsCustomerStep1 - K1000000 - '
546            'temp_password generated: %s' % temp_password in logcontent)
547
548
549class CustomerUITests(CustomersFullSetup):
550    # Tests for Customer class views and pages
551
552    def test_customer_login_with_email(self):
553        self.assertEqual(self.customer.state, 'created')
554        self.browser.open(self.login_path)
555        self.browser.getControl(name="form.login").value = self.customer.email
556        self.browser.getControl(name="form.password").value = 'cpwd'
557        self.browser.getControl("Login").click()
558        self.assertEqual(self.browser.url, self.customer_path)
559        self.assertTrue('You logged in' in self.browser.contents)
560        # Status has changed
561        self.assertEqual(self.customer.state, 'started')
562        return
563
564    def test_customer_change_password(self):
565        # Customers can change the password
566        self.assertEqual(self.customer.state, 'created')
567        self.customer.personal_updated = datetime.utcnow()
568        self.browser.open(self.login_path)
569        self.browser.getControl(name="form.login").value = self.customer_id
570        self.browser.getControl(name="form.password").value = 'cpwd'
571        self.browser.getControl("Login").click()
572        self.assertEqual(self.browser.url, self.customer_path)
573        self.assertTrue('You logged in' in self.browser.contents)
574        # Status has changed
575        self.assertEqual(self.customer.state, 'started')
576        # Change password
577        self.browser.getLink("Change password").click()
578        self.browser.getControl(name="change_password").value = 'pw'
579        self.browser.getControl(
580            name="change_password_repeat").value = 'pw'
581        self.browser.getControl("Save").click()
582        self.assertTrue('Password must have at least' in self.browser.contents)
583        self.browser.getControl(name="change_password").value = 'new_password'
584        self.browser.getControl(
585            name="change_password_repeat").value = 'new_passssword'
586        self.browser.getControl("Save").click()
587        self.assertTrue('Passwords do not match' in self.browser.contents)
588        self.browser.getControl(name="change_password").value = 'new_password'
589        self.browser.getControl(
590            name="change_password_repeat").value = 'new_password'
591        self.browser.getControl("Save").click()
592        self.assertTrue('Password changed' in self.browser.contents)
593        # We are still logged in. Changing the password hasn't thrown us out.
594        self.browser.getLink("Base Data").click()
595        self.assertEqual(self.browser.url, self.customer_path)
596        # We can logout
597        self.browser.getLink("Logout").click()
598        self.assertTrue('You have been logged out' in self.browser.contents)
599        self.assertEqual(self.browser.url, 'http://localhost/app/index')
600        # We can login again with the new password
601        self.browser.getLink("Login").click()
602        self.browser.open(self.login_path)
603        self.browser.getControl(name="form.login").value = self.customer_id
604        self.browser.getControl(name="form.password").value = 'new_password'
605        self.browser.getControl("Login").click()
606        self.assertEqual(self.browser.url, self.customer_path)
607        self.assertTrue('You logged in' in self.browser.contents)
608        return
609
610    def test_customer_upload_passport(self):
611        # Customer cant login if their password is not set
612        self.browser.open(self.login_path)
613        self.browser.getControl(name="form.login").value = self.customer_id
614        self.browser.getControl(name="form.password").value = 'cpwd'
615        self.browser.getControl("Login").click()
616        self.assertMatches(
617            '...You logged in...', self.browser.contents)
618        # Admitted customer can upload a passport picture
619        self.browser.getLink("Change portrait").click()
620        ctrl = self.browser.getControl(name='passporteditupload')
621        file_obj = open(SAMPLE_IMAGE, 'rb')
622        file_ctrl = ctrl.mech_control
623        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
624        self.browser.getControl(
625            name='upload_passporteditupload').click()
626        self.assertTrue(
627            'src="http://localhost/app/customers/K1000000/passport.jpg"'
628            in self.browser.contents)
629        self.browser.getControl(name="CANCEL").click()
630        self.assertEqual(self.browser.url, self.customer_path)
631
632    def test_customer_baseedit(self):
633        # Customers can change the password
634        self.customer.personal_updated = datetime.utcnow()
635        self.browser.open(self.login_path)
636        self.browser.getControl(name="form.login").value = self.customer_id
637        self.browser.getControl(name="form.password").value = 'cpwd'
638        self.browser.getControl("Login").click()
639        self.assertEqual(self.browser.url, self.customer_path)
640        self.assertTrue('You logged in' in self.browser.contents)
641        self.browser.getLink("Edit").click()
642        self.browser.getControl(name="form.email").value = 'new_email@aa.ng'
643        self.browser.getControl("Save").click()
644        self.assertMatches('...Form has been saved...',
645                           self.browser.contents)
646        # Customer can view history
647        self.browser.getLink("History").click()
648        self.assertMatches('...Customer created by system...',
649            self.browser.contents)
650
651    def test_customer_login(self):
652        # Customer cant login if their password is not set
653        self.customer.password = None
654        self.browser.open(self.login_path)
655        self.browser.getControl(name="form.login").value = self.customer_id
656        self.browser.getControl(name="form.password").value = 'cpwd'
657        self.browser.getControl("Login").click()
658        self.assertTrue(
659            'You entered invalid credentials.' in self.browser.contents)
660        # We set the password again
661        IUserAccount(
662            self.app['customers'][self.customer_id]).setPassword('cpwd')
663        # Customers can't login if their account is suspended/deactivated
664        self.customer.suspended = True
665        self.browser.open(self.login_path)
666        self.browser.getControl(name="form.login").value = self.customer_id
667        self.browser.getControl(name="form.password").value = 'cpwd'
668        self.browser.getControl("Login").click()
669        self.assertMatches(
670            '...<div class="alert alert-warning">'
671            'Your account has been deactivated.</div>...', self.browser.contents)
672        # If suspended_comment is set this message will be flashed instead
673        self.customer.suspended_comment = u'Aetsch baetsch!'
674        self.browser.getControl(name="form.login").value = self.customer_id
675        self.browser.getControl(name="form.password").value = 'cpwd'
676        self.browser.getControl("Login").click()
677        self.assertMatches(
678            '...<div class="alert alert-warning">Aetsch baetsch!</div>...',
679            self.browser.contents)
680        self.customer.suspended = False
681        # Customers can't login if a temporary password has been set and
682        # is not expired
683        self.app['customers'][self.customer_id].setTempPassword(
684            'anybody', 'temp_cpwd')
685        self.browser.open(self.login_path)
686        self.browser.getControl(name="form.login").value = self.customer_id
687        self.browser.getControl(name="form.password").value = 'cpwd'
688        self.browser.getControl("Login").click()
689        self.assertMatches(
690            '...Your account has been temporarily deactivated...',
691            self.browser.contents)
692        # The customer can login with the temporary password
693        self.browser.open(self.login_path)
694        self.browser.getControl(name="form.login").value = self.customer_id
695        self.browser.getControl(name="form.password").value = 'temp_cpwd'
696        self.browser.getControl("Login").click()
697        self.assertMatches(
698            '...You logged in...', self.browser.contents)
699        # Customer can view the base data
700        self.browser.open(self.customer_path)
701        self.assertEqual(self.browser.headers['Status'], '200 Ok')
702        self.assertEqual(self.browser.url, self.customer_path)
703        # When the password expires ...
704        delta = timedelta(minutes=11)
705        self.app['customers'][self.customer_id].temp_password[
706            'timestamp'] = datetime.utcnow() - delta
707        self.app['customers'][self.customer_id]._p_changed = True
708        # ... the customer will be automatically logged out
709        self.assertRaises(
710            Unauthorized, self.browser.open, self.customer_path)
711        # Then the customer can login with the original password
712        self.browser.open(self.login_path)
713        self.browser.getControl(name="form.login").value = self.customer_id
714        self.browser.getControl(name="form.password").value = 'cpwd'
715        self.browser.getControl("Login").click()
716        self.assertMatches(
717            '...You logged in...', self.browser.contents)
718
719    def test_change_password_request(self):
720        self.browser.open('http://localhost/app/changepw')
721        self.browser.getControl(name="form.identifier").value = '123'
722        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
723        self.browser.getControl("Send login credentials").click()
724        self.assertTrue('An email with' in self.browser.contents)
725
726class CustomerRegistrationTests(CustomersFullSetup):
727    # Tests for customer registration
728
729    layer = FunctionalLayer
730
731    def test_request_pw(self):
732        # Customer with wrong number can't be found.
733        self.browser.open('http://localhost/app/requestpw')
734        self.browser.getControl(name="form.firstname").value = 'Anna'
735        self.browser.getControl(name="form.number").value = 'anynumber'
736        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
737        self.browser.getControl("Send login credentials").click()
738        self.assertTrue('No customer found.'
739            in self.browser.contents)
740        # Anonymous is not informed that firstname verification failed.
741        # It seems that the record doesn't exist.
742        self.browser.open('http://localhost/app/requestpw')
743        self.browser.getControl(name="form.firstname").value = 'Johnny'
744        self.browser.getControl(name="form.number").value = '123'
745        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
746        self.browser.getControl("Send login credentials").click()
747        self.assertTrue('No customer found.'
748            in self.browser.contents)
749        # Even with the correct firstname we can't register if a
750        # password has been set and used.
751        self.browser.getControl(name="form.firstname").value = 'Anna'
752        self.browser.getControl(name="form.number").value = '123'
753        self.browser.getControl("Send login credentials").click()
754        self.assertTrue('Your password has already been set and used.'
755            in self.browser.contents)
756        self.browser.open('http://localhost/app/requestpw')
757        self.app['customers'][self.customer_id].password = None
758        # The firstname field, used for verification, is not case-sensitive.
759        self.browser.getControl(name="form.firstname").value = 'aNNa'
760        self.browser.getControl(name="form.number").value = '123'
761        self.browser.getControl(name="form.email").value = 'new@yy.zz'
762        self.browser.getControl("Send login credentials").click()
763        # Yeah, we succeded ...
764        self.assertTrue('Your request was successful.'
765            in self.browser.contents)
766        # ... and  customer can be found in the catalog via the email address
767        cat = queryUtility(ICatalog, name='customers_catalog')
768        results = list(
769            cat.searchResults(
770            email=('new@yy.zz', 'new@yy.zz')))
771        self.assertEqual(self.customer,results[0])
772        logfile = os.path.join(
773            self.app['datacenter'].storage, 'logs', 'main.log')
774        logcontent = open(logfile).read()
775        self.assertTrue('zope.anybody - customers.browser.CustomerRequestPasswordPage - '
776                        '123 (K1000000) - new@yy.zz' in logcontent)
777        return
778
779    def test_create_account(self):
780        self.browser.open('http://localhost/app/createaccount')
781        self.browser.getControl(name="form.firstname").value = 'Ruben'
782        self.browser.getControl(name="form.lastname").value = 'Gonzales'
783        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
784        self.browser.getControl("Send login credentials").click()
785        # Email address exists.
786        self.assertTrue('Email address in use.' in self.browser.contents)
787        self.browser.getControl(name="form.email").value = 'newcustomer@xx.zz'
788        self.browser.getControl("Send login credentials").click()
789        self.assertTrue('Your request was successful.' in self.browser.contents)
790        # Customer can be found in the catalog via the email address
791        cat = queryUtility(ICatalog, name='customers_catalog')
792        results = list(
793            cat.searchResults(
794            email=('newcustomer@xx.zz', 'newcustomer@xx.zz')))
795        self.assertEqual(self.app['customers']['K1000001'], results[0])
796        self.assertEqual(self.app['customers']['K1000001'].firstname, 'Ruben')
797        self.assertEqual(self.app['customers']['K1000001'].lastname, 'Gonzales')
798        logfile = os.path.join(
799            self.app['datacenter'].storage, 'logs', 'main.log')
800        logcontent = open(logfile).read()
801        self.assertTrue('zope.anybody - customers.browser.CustomerCreateAccountPage - '
802                        'K1000001 - newcustomer@xx.zz' in logcontent)
803        return
804
805class CustomerDataExportTests(CustomersFullSetup, FunctionalAsyncTestCase):
806    # Tests for CustomersContainer class views and pages
807
808    layer = FunctionalLayer
809
810    def wait_for_export_job_completed(self):
811        # helper function waiting until the current export job is completed
812        manager = getUtility(IJobManager)
813        job_id = self.app['datacenter'].running_exports[0][0]
814        job = manager.get(job_id)
815        wait_for_result(job)
816        return job_id
817
818    def test_datacenter_export(self):
819        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
820        self.browser.open('http://localhost/app/datacenter/@@export')
821        self.browser.getControl(name="exporter").value = ['customers']
822        self.browser.getControl("Create CSV file").click()
823
824        # When the job is finished and we reload the page...
825        job_id = self.wait_for_export_job_completed()
826        # ... the csv file can be downloaded ...
827        self.browser.open('http://localhost/app/datacenter/@@export')
828        self.browser.getLink("Download").click()
829        self.assertEqual(self.browser.headers['content-type'],
830            'text/csv; charset=UTF-8')
831        self.assertTrue(
832            'filename="WAeUP.Ikoba_customers_%s.csv' % job_id in
833            self.browser.headers['content-disposition'])
834        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
835        job_id = self.app['datacenter'].running_exports[0][0]
836        # ... and discarded
837        self.browser.open('http://localhost/app/datacenter/@@export')
838        self.browser.getControl("Discard").click()
839        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
840        # Creation, downloading and discarding is logged
841        logfile = os.path.join(
842            self.app['datacenter'].storage, 'logs', 'datacenter.log')
843        logcontent = open(logfile).read()
844        self.assertTrue(
845            'zope.mgr - browser.pages.ExportCSVPage - exported: '
846            'customers, job_id=%s'
847            % job_id in logcontent
848            )
849        self.assertTrue(
850            'zope.mgr - browser.pages.ExportCSVView - downloaded: '
851            'WAeUP.Ikoba_customers_%s.csv, job_id=%s'
852            % (job_id, job_id) in logcontent
853            )
854        self.assertTrue(
855            'zope.mgr - browser.pages.ExportCSVPage - discarded: '
856            'job_id=%s' % job_id in logcontent
857            )
858
859
860class DocumentUITests(CustomersFullSetup):
861    # Tests for customer document related views and pages
862
863    def test_manage_document(self):
864        # Managers can access the pages of customer documentsconter
865        # and can perform actions
866        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
867        self.browser.open(self.customer_path)
868        self.assertEqual(self.browser.headers['Status'], '200 Ok')
869        self.assertEqual(self.browser.url, self.customer_path)
870        self.browser.open(self.customer_path)
871        self.browser.getLink("Documents", index=1).click()
872        self.browser.getControl("Add document").click()
873        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
874        self.browser.getControl(name="form.title").value = 'My PDF Document'
875        self.browser.getControl("Add document").click()
876        self.assertTrue('PDF Document added.' in self.browser.contents)
877        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
878        document = self.customer['documents'][docid]
879
880        # Document can be edited
881        self.browser.getLink(docid[:9]).click()
882        self.browser.getLink("Manage").click()
883        self.browser.getControl(name="form.title").value = 'My second doc'
884        self.browser.getControl("Save").click()
885        self.assertTrue('Form has been saved.' in self.browser.contents)
886        self.browser.getLink("View").click()
887        self.assertEqual(self.browser.url,
888            self.documents_path + '/' + docid + '/index')
889
890        # Transitions can be performed
891        self.browser.getLink("Transition").click()
892        self.browser.getControl(name="transition").value = ['submit']
893        self.browser.getControl("Apply").click()
894        self.browser.getLink("Transition").click()
895        # Document can only be verified if customer is approved
896        self.browser.getControl(name="transition").value = ['verify']
897        self.browser.getControl("Apply").click()
898        self.assertTrue('Customer has not yet been approved' in self.browser.contents)
899        IWorkflowState(self.customer).setState(APPROVED)
900        # Document can only be verified if files have been uploaded before
901        self.browser.getLink("Transition").click()
902        self.browser.getControl(name="transition").value = ['verify']
903        self.browser.getControl("Apply").click()
904        self.assertTrue('No file uploaded' in self.browser.contents)
905        self.assertEqual(document.state, 'submitted')
906        # We set state here manually (verification is tested in test_verify_document)
907        IWorkflowState(document).setState(VERIFIED)
908
909        # Manage button and form is no longer available
910        self.browser.open(self.documents_path + '/' + docid + '/index')
911        self.assertFalse(
912            'href="http://localhost/app/customers/K1000000/documents/%s/manage"'
913            % docid in self.browser.contents)
914        self.browser.open(self.documents_path + '/' + docid + '/manage')
915        self.assertTrue(
916            'The requested form is locked (read-only)'
917            in self.browser.contents)
918
919        # Documents can be removed
920        self.browser.getLink("Documents", index=1).click()
921        ctrl = self.browser.getControl(name='val_id')
922        ctrl.getControl(value=document.document_id).selected = True
923        self.browser.getControl("Remove selected", index=0).click()
924        self.assertTrue('Successfully removed' in self.browser.contents)
925
926        # All actions are being logged
927        logfile = os.path.join(
928            self.app['datacenter'].storage, 'logs', 'customers.log')
929        logcontent = open(logfile).read()
930
931        self.assertTrue(
932            'INFO - system - K1000000 - DOC1 - Document created'
933            in logcontent)
934        self.assertTrue(
935            'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
936            '- K1000000 - added: PDF Document %s'
937            % document.document_id in logcontent)
938        self.assertTrue(
939            'INFO - zope.mgr - customers.browser.DocumentManageFormPage '
940            '- K1000000 - %s - saved: title' % docid
941            in logcontent)
942        self.assertTrue(
943            'INFO - zope.mgr - K1000000 - %s - Submitted for verification'
944            % docid in logcontent)
945        self.assertTrue(
946            'INFO - zope.mgr - customers.browser.DocumentsManageFormPage '
947            '- K1000000 - removed: %s' % docid
948            in logcontent)
949
950    def test_edit_sample_document(self):
951        # Customers can manage documents under certain conditions
952        self.browser.open(self.login_path)
953        self.browser.getControl(name="form.login").value = self.customer_id
954        self.browser.getControl(name="form.password").value = 'cpwd'
955        self.browser.getControl("Login").click()
956        self.assertMatches(
957            '...You logged in...', self.browser.contents)
958        self.browser.getLink("Documents").click()
959        self.browser.getControl("Add document").click()
960        self.assertTrue('The requested form is locked' in self.browser.contents)
961        # Customer is in wrong state
962        IWorkflowState(self.customer).setState(APPROVED)
963        self.browser.getControl("Add document").click()
964        self.browser.getControl(name="doctype").value = ['CustomerSampleDocument']
965        self.browser.getControl(name="form.title").value = 'My Sample Document'
966        self.browser.getControl("Add document").click()
967        self.assertTrue('Sample Document added.' in self.browser.contents)
968        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
969        document = self.customer['documents'][docid]
970        self.browser.getControl(name="form.title").value = 'My second doc'
971        self.browser.getControl("Save").click()
972        self.assertEqual(document.title, 'My second doc')
973        self.assertTrue('Form has been saved.' in self.browser.contents)
974        self.browser.getLink("View").click()
975        self.assertEqual(
976            self.browser.url, self.documents_path + '/%s/index' % docid)
977        # Customer can upload a document.
978        self.browser.getLink("Edit").click()
979        ctrl = self.browser.getControl(name='samplescaneditupload')
980        file_obj = open(SAMPLE_IMAGE, 'rb')
981        file_ctrl = ctrl.mech_control
982        file_ctrl.add_file(file_obj, filename='my_document.jpg')
983        self.browser.getControl(
984            name='upload_samplescaneditupload').click()
985        self.assertTrue(
986            'href="http://localhost/app/customers/K1000000/documents/%s/sample"'
987            % docid in self.browser.contents)
988        # Customer can submit the form. The form is also saved.
989        self.browser.getControl(name="form.title").value = 'My third doc'
990        self.browser.getControl("Final Submit").click()
991        self.assertEqual(document.title, 'My third doc')
992        self.assertEqual(document.state, 'submitted')
993        self.assertTrue('Document State: submitted for verification' in self.browser.contents)
994        # Customer can't edit the document once it has been submitted
995        self.browser.open(self.documents_path + '/%s/edit' % docid)
996        self.assertTrue('The requested form is locked' in self.browser.contents)
997
998    def test_manage_upload_sample_file(self):
999        # Managers can upload a file via the DocumentManageFormPage
1000        # The image is stored even if form has errors
1001        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1002        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1003        # Create a pseudo image file and select it to be uploaded
1004        image = open(SAMPLE_IMAGE, 'rb')
1005        ctrl = self.browser.getControl(name='samplescanmanageupload')
1006        file_ctrl = ctrl.mech_control
1007        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1008        # The Save action does not upload files
1009        self.browser.getControl("Save").click() # submit form
1010        self.assertFalse(
1011            'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
1012            in self.browser.contents)
1013        # ... but the correct upload submit button does
1014        image = open(SAMPLE_IMAGE)
1015        ctrl = self.browser.getControl(name='samplescanmanageupload')
1016        file_ctrl = ctrl.mech_control
1017        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1018        self.browser.getControl(
1019            name='upload_samplescanmanageupload').click()
1020        self.assertTrue(
1021            'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
1022            in self.browser.contents)
1023        # Browsing the link shows a real image
1024        self.browser.open('sample')
1025        self.assertEqual(
1026            self.browser.headers['content-type'], 'image/jpeg')
1027        self.assertEqual(len(self.browser.contents), 2787)
1028        # We can't reupload a file. The existing file must be deleted first.
1029        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1030        self.assertFalse(
1031            'upload_samplescanmanageupload' in self.browser.contents)
1032        # File must be deleted first
1033        self.browser.getControl(name='delete_samplescanmanageupload').click()
1034        self.assertTrue(
1035            'sample deleted' in self.browser.contents)
1036        # Uploading a file which is bigger than 150k will raise an error
1037        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
1038        ctrl = self.browser.getControl(name='samplescanmanageupload')
1039        file_ctrl = ctrl.mech_control
1040        file_ctrl.add_file(big_image, filename='my_sample_scan.jpg')
1041        self.browser.getControl(
1042            name='upload_samplescanmanageupload').click()
1043        self.assertTrue(
1044            'Uploaded file is too big' in self.browser.contents)
1045        # We do not rely on filename extensions given by uploaders
1046        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
1047        ctrl = self.browser.getControl(name='samplescanmanageupload')
1048        file_ctrl = ctrl.mech_control
1049        # Tell uploaded file is bmp
1050        file_ctrl.add_file(image, filename='my_sample_scan.bmp')
1051        self.browser.getControl(
1052            name='upload_samplescanmanageupload').click()
1053        self.assertTrue(
1054            # jpg file was recognized
1055            'File sample.jpg uploaded.' in self.browser.contents)
1056        # Delete file again
1057        self.browser.getControl(name='delete_samplescanmanageupload').click()
1058        self.assertTrue(
1059            'sample deleted' in self.browser.contents)
1060        # File names must meet several conditions
1061        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
1062        ctrl = self.browser.getControl(name='samplescanmanageupload')
1063        file_ctrl = ctrl.mech_control
1064        file_ctrl.add_file(bmp_image, filename='my_sample_scan.bmp')
1065        self.browser.getControl(
1066            name='upload_samplescanmanageupload').click()
1067        self.assertTrue('Only the following extensions are allowed'
1068            in self.browser.contents)
1069
1070    def test_verify_document(self):
1071        IWorkflowState(self.customer).setState('approved')
1072        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1073        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1074        image = open(SAMPLE_IMAGE, 'rb')
1075        ctrl = self.browser.getControl(name='samplescanmanageupload')
1076        file_ctrl = ctrl.mech_control
1077        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1078        self.browser.getControl(
1079            name='upload_samplescanmanageupload').click()
1080        IWorkflowState(self.document).setState(SUBMITTED)
1081        # Only after verifying the document, sample_md5 is set
1082        self.assertEqual(
1083            getattr(self.document, 'sample_md5', None), None)
1084        self.browser.open(self.documents_path + '/DOC1/trigtrans')
1085        self.browser.getControl(name="transition").value = ['verify']
1086        self.browser.getControl("Apply").click()
1087        self.assertEqual(
1088            getattr(self.document, 'sample_md5', None),
1089                    '1d1ab893e87c240afb2104d61ddfe180')
1090
1091    def test_manage_upload_pdf_file(self):
1092        # Managers can upload a file via the DocumentManageFormPage
1093        # The image is stored even if form has errors
1094        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1095        self.browser.open(self.customer_path + '/documents')
1096        self.browser.getControl("Add document").click()
1097        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
1098        self.browser.getControl(name="form.title").value = 'My PDF Document'
1099        self.browser.getControl("Add document").click()
1100        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
1101        self.browser.open(self.documents_path + '/%s/manage' % docid)
1102        # Create a pseudo image file and select it to be uploaded
1103        image = open(SAMPLE_IMAGE, 'rb')
1104        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1105        file_ctrl = ctrl.mech_control
1106        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1107        self.browser.getControl(
1108            name='upload_pdfscanmanageupload').click()
1109        self.assertTrue(
1110            'pdf file format expected' in self.browser.contents)
1111        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1112        file_ctrl = ctrl.mech_control
1113        file_ctrl.add_file(image, filename='my_sample_scan.pdf')
1114        self.browser.getControl(
1115            name='upload_pdfscanmanageupload').click()
1116        self.assertTrue(
1117            'Could not determine file type' in self.browser.contents)
1118        pdf = open(SAMPLE_PDF, 'rb')
1119        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1120        file_ctrl = ctrl.mech_control
1121        file_ctrl.add_file(pdf, filename='my_sample_scan.pdf')
1122        self.browser.getControl(
1123            name='upload_pdfscanmanageupload').click()
1124        self.assertTrue(
1125            'href="http://localhost/app/customers/K1000000/documents/%s/sample.pdf">%s.pdf</a>'
1126            % (docid, docid[:9]) in self.browser.contents)
1127        # Browsing the link shows a real pdf
1128        self.browser.open('sample.pdf')
1129        self.assertEqual(
1130            self.browser.headers['content-type'], 'application/pdf')
1131        # The name of the downloaded file will be different
1132        self.assertEqual(
1133            self.browser.headers['Content-Disposition'],
1134            'attachment; filename="%s.pdf' % docid[:9])
1135
1136    def test_view_slips(self):
1137        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1138        # Officers can open the document overview
1139        self.browser.open(self.customer_path + '/documents')
1140        self.browser.getLink("Download documents overview").click()
1141        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1142        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1143        path = os.path.join(samples_dir(), 'documents_overview_slip.pdf')
1144        open(path, 'wb').write(self.browser.contents)
1145        print "Sample PDF overview_slip.pdf written to %s" % path
1146        # Officers can open document slips which shows a thumbnail of
1147        # the jpeg file attached.
1148        file_id = IFileStoreNameChooser(self.document).chooseName(attr='sample.jpg')
1149        fs = ExtFileStore(root=self.dc_root)
1150        jpegfile = open(SAMPLE_IMAGE, 'rb')
1151        fs.createFile(file_id, jpegfile)
1152        self.browser.open(self.customer_path + '/documents/DOC1')
1153        self.browser.getLink("Download document slip").click()
1154        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1155        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1156        path = os.path.join(samples_dir(), 'document_slip.pdf')
1157        open(path, 'wb').write(self.browser.contents)
1158        print "Sample document_slip.pdf written to %s" % path
1159        # Officers can open merged pdf document slips
1160        pdfdocument = createObject('waeup.CustomerPDFDocument')
1161        pdfdocument.title = u'My first document'
1162        self.customer['documents'].addDocument(pdfdocument)
1163        # Add pdf file
1164        file_id = IFileStoreNameChooser(pdfdocument).chooseName(attr='sample.pdf')
1165        fs = ExtFileStore(root=self.dc_root)
1166        pdffile = open(SAMPLE_PDF, 'rb')
1167        fs.createFile(file_id, pdffile)
1168        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
1169        self.browser.open(self.customer_path + '/documents/' + docid)
1170        self.browser.getLink("Download document slip").click()
1171        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1172        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1173        path = os.path.join(samples_dir(), 'pdfdocument_slip.pdf')
1174        open(path, 'wb').write(self.browser.contents)
1175        print "Sample pdfdocument_slip.pdf written to %s" % path
1176
1177    def test_get_setmd5_file(self):
1178        # A proper file name chooser is registered for customer documents.
1179        # This is not a UI test. It's just a functional test.
1180        file_id = IFileStoreNameChooser(self.document).chooseName(attr='sample')
1181        fs = ExtFileStore(root=self.dc_root)
1182        fs.createFile(file_id, StringIO('my sample 1'))
1183        result = fs.getFileByContext(self.document, attr='sample')
1184        self.assertEqual(file_id, '__file-customerdocument__01000/K1000000/sample_DOC1_K1000000')
1185        self.assertEqual(result.read(), 'my sample 1')
1186        self.assertEqual(self.document.connected_files[0][1].read(), 'my sample 1')
1187        self.document.setMD5()
1188        self.assertEqual(self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
1189        return
1190
1191
1192class ContractUITests(CustomersFullSetup):
1193    # Tests for contract related views and pages
1194
1195    def test_manage_contract(self):
1196        # Managers can access the pages of customer contractsconter
1197        # and can perform actions
1198        IWorkflowState(self.customer).setState(APPROVED)
1199        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1200        self.browser.open(self.customer_path)
1201        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1202        self.assertEqual(self.browser.url, self.customer_path)
1203        self.browser.open(self.customer_path)
1204        self.browser.getLink("Contracts").click()
1205        self.browser.getControl("Add contract").click()
1206        self.browser.getControl(name="contype").value = ['SampleContract']
1207        self.browser.getControl("Add contract").click()
1208        self.assertTrue('Sample Contract added.' in self.browser.contents)
1209        conid = [i for i in self.customer['contracts'].keys() if len(i) > 10][0]
1210        contract = self.customer['contracts'][conid]
1211        self.assertEqual(
1212            self.browser.url, self.contracts_path + '/%s/selectproduct' % conid)
1213        # SAM is in the correct contract_category ...
1214        self.assertTrue('<option value="SAM">' in self.browser.contents)
1215        # ... but NOTSAM not.
1216        self.assertFalse('<option value="LIC">' in self.browser.contents)
1217        # So far last_product_id is None.
1218        self.assertTrue(self.customer['contracts'][conid].last_product_id is None)
1219        self.browser.getControl(name="form.product_object").value = ['SAM']
1220        self.browser.getControl("Save and proceed").click()
1221        self.assertEqual(
1222            self.browser.url, self.contracts_path + '/%s/manage' % conid)
1223        self.browser.getLink("View").click()
1224        self.assertEqual(self.browser.url, self.contracts_path + '/%s/index' % conid)
1225        self.assertEqual(contract.tc_dict, {'en': u'Hello world'})
1226
1227        # Transitions can be performed
1228        self.browser.getLink("Transition").click()
1229        self.browser.getControl(name="transition").value = ['submit']
1230        self.browser.getControl("Apply").click()
1231        self.browser.getLink("Transition").click()
1232        self.browser.getControl(name="transition").value = ['approve']
1233        self.browser.getControl("Apply").click()
1234        self.assertEqual(contract.state, 'approved')
1235
1236        # Even in state approved the official use data can be edited
1237        self.browser.open(self.contracts_path + '/%s/index' % conid)
1238        self.browser.getLink("Manage official data").click()
1239        self.browser.getControl(name="form.comment").value = u'Nice place'
1240        self.browser.getControl("Save").click()
1241        self.assertEqual(contract.comment, 'Nice place')
1242        self.assertTrue('Form has been saved.' in self.browser.contents)
1243
1244        # Contracts can be removed
1245        self.browser.getLink("Contracts").click()
1246        ctrl = self.browser.getControl(name='val_id')
1247        ctrl.getControl(value=contract.contract_id).selected = True
1248        self.browser.getControl("Remove selected", index=0).click()
1249        self.assertTrue('Successfully removed' in self.browser.contents)
1250
1251        # All actions are being logged
1252        logfile = os.path.join(
1253            self.app['datacenter'].storage, 'logs', 'customers.log')
1254        logcontent = open(logfile).read()
1255        self.assertTrue(
1256            'INFO - zope.mgr - K1000000 - %s - Contract created' % conid
1257            in logcontent)
1258        self.assertTrue(
1259            'INFO - zope.mgr - customers.browser.ContractAddFormPage '
1260            '- K1000000 - added: Sample Contract %s'
1261            % contract.contract_id in logcontent)
1262        self.assertTrue(
1263            'INFO - zope.mgr - K1000000 - %s - Submitted for approval' % conid
1264            in logcontent)
1265        self.assertTrue(
1266            'INFO - zope.mgr - K1000000 - %s - Approved' % conid
1267            in logcontent)
1268        self.assertTrue(
1269            'INFO - zope.mgr - customers.browser.ContractsFormPage '
1270            '- K1000000 - removed: %s' % conid
1271            in logcontent)
1272
1273    def test_edit_sample_contract(self):
1274        # We add a second product.
1275        product = createObject('waeup.Product')
1276        product.product_id = u'LIC'
1277        product.title = u'Our License Product'
1278        product.contract_category = u'license'
1279        self.app['products'].addProduct(product)
1280        # Customers can manage contracts under certain conditions
1281        self.browser.open(self.login_path)
1282        self.browser.getControl(name="form.login").value = self.customer_id
1283        self.browser.getControl(name="form.password").value = 'cpwd'
1284        self.browser.getControl("Login").click()
1285        self.assertMatches(
1286            '...You logged in...', self.browser.contents)
1287        self.browser.getLink("Contracts").click()
1288        # Customer is in wrong state
1289        self.assertFalse('Add contract' in self.browser.contents)
1290        self.browser.open(self.contracts_path + '/addcontract')
1291        self.assertTrue('The requested form is locked' in self.browser.contents)
1292        IWorkflowState(self.customer).setState(APPROVED)
1293        self.browser.open(self.contracts_path)
1294        # Now customer can add a contract
1295        self.browser.getControl("Add contract").click()
1296        self.browser.getControl(name="contype").value = ['SampleContract']
1297        self.browser.getControl("Add contract").click()
1298        self.assertTrue('Sample Contract added.' in self.browser.contents)
1299        conid = [i for i in self.customer['contracts'].keys() if len(i) > 10][0]
1300        contract = self.customer['contracts'][conid]
1301        self.assertEqual(
1302            self.browser.url, self.contracts_path + '/%s/selectproduct' % conid)
1303        # SAM is in the correct contract_category ...
1304        self.assertTrue('<option value="SAM">' in self.browser.contents)
1305        # ... but NOTSAM not.
1306        self.assertFalse('<option value="LIC">' in self.browser.contents)
1307        # So far last_product_id is None.
1308        self.assertTrue(self.customer['contracts'][conid].last_product_id is None)
1309        self.browser.getControl(name="form.product_object").value = ['SAM']
1310        self.browser.getControl("Save and proceed").click()
1311        self.assertEqual(
1312            self.browser.url, self.contracts_path + '/%s/edit' % conid)
1313        # Document is a required field on edit form page.
1314        self.browser.getControl("Save").click()
1315        self.assertTrue('Document: <span class="error">Required input is missing.</span>'
1316            in self.browser.contents)
1317        # But our document can't be selected because it's not submitted
1318        self.assertFalse('My first document' in self.browser.contents)
1319        IWorkflowState(self.document).setState(SUBMITTED)
1320        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1321        self.browser.getControl(name="form.document_object").value = ['DOC1']
1322        self.browser.getControl("Save").click()
1323        # After saving the form, last_product_id and other attributes are set
1324        self.assertTrue('Form has been saved.' in self.browser.contents)
1325        self.assertEqual(self.customer['contracts'][conid].last_product_id, 'SAM')
1326        self.assertEqual(contract.title, 'Our Sample Product')
1327        self.assertEqual(contract.product_object, self.product)
1328        self.assertEqual(contract.document_object, self.document)
1329        # Saving the form again does not unset last_product_id
1330        self.browser.getControl("Save").click()
1331        self.assertEqual(self.customer['contracts'][conid].last_product_id, 'SAM')
1332        self.assertTrue('Form has been saved.' in self.browser.contents)
1333        # So far we have not yet set product options.
1334        # Unfortunately, we can't set them in test browser
1335        prodoption = ProductOption()
1336        prodoption.title = u'Any product option'
1337        prodoption.fee = Decimal('88.8')
1338        prodoption.currency = 'EUR'
1339        contract.product_options = [prodoption,]
1340        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1341        # We can see both the stored and the recent product options
1342        # from the chosen product.
1343        self.assertTrue('<option selected="selected" value="Any product option">'
1344                        'Any product option @ 88.8 Euro</option>'
1345                        in self.browser.contents)
1346        self.assertTrue('<option value="First option">First option '
1347                        '@ 99.9 US Dollar</option>' in self.browser.contents)
1348        # In test browser we can at least replace the option
1349        self.browser.getControl(
1350            name="form.product_options.0.").value = ['First option']
1351        self.assertEqual(contract.product_options[0].title, 'Any product option')
1352        self.browser.getControl("Save").click()
1353        self.assertEqual(contract.product_options[0].title, 'First option')
1354        self.browser.getLink("View").click()
1355        self.assertTrue('<span>First option @ 99.9 US Dollar</span>' in self.browser.contents)
1356        self.assertEqual(self.browser.url, self.contracts_path + '/%s/index' % conid)
1357        # An href attribute is referring to the document and product objects
1358        self.assertTrue('<a href="http://localhost/app/products/SAM">SAM -'
1359            in self.browser.contents)
1360        self.assertTrue(
1361            '<a href="http://localhost/app/customers/K1000000/documents/DOC1">DOC1 -'
1362            in self.browser.contents)
1363        # Customer can submit the form. The form is also saved.
1364        self.browser.getLink("Edit").click()
1365        self.browser.getControl("Apply").click()
1366        self.assertEqual(contract.state, 'submitted')
1367        self.assertTrue('Contract State: submitted for approval' in self.browser.contents)
1368        # Customer can't edit the contract once it has been submitted
1369        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1370        self.assertTrue('The requested form is locked' in self.browser.contents)
1371
1372    def test_view_slips(self):
1373        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1374        # Officers can open the contract overview
1375        self.browser.open(self.customer_path + '/contracts')
1376        self.browser.getLink("Download contracts overview").click()
1377        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1378        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1379        path = os.path.join(samples_dir(), 'contracts_overview_slip.pdf')
1380        open(path, 'wb').write(self.browser.contents)
1381        print "Sample PDF overview_slip.pdf written to %s" % path
1382        # Officers can open contract slips.
1383        # First we add a submitted document and a product.
1384        IWorkflowState(self.document).setState(SUBMITTED)
1385        self.contract.document_object = self.document
1386        self.contract.product_object = self.product
1387        self.contract.tc_dict = {'en': u'<strong>Hello world</strong>'}
1388        self.browser.open(self.customer_path + '/contracts/CON1')
1389        self.browser.getLink("Download contract slip").click()
1390        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1391        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1392        path = os.path.join(samples_dir(), 'contract_slip.pdf')
1393        open(path, 'wb').write(self.browser.contents)
1394        print "Sample contract_slip.pdf written to %s" % path
1395
1396    def test_contract_approval(self):
1397        # This is not a UI test. It's just a functional test.
1398        self.assertRaises(ConstraintNotSatisfied,
1399            self.contract.document_object, self.document)
1400        # Document must be at least submitted
1401        IWorkflowState(self.document).setState('submitted')
1402        self.contract.document_object = self.document
1403        IWorkflowState(self.contract).setState('submitted')
1404        self.assertRaises(InvalidTransitionError,
1405            IWorkflowInfo(self.contract).fireTransition, 'approve')
1406        # Customer must be approved and
1407        # document must be verified for the approval of contracts
1408        IWorkflowState(self.document).setState('verified')
1409        IWorkflowState(self.customer).setState('approved')
1410        IWorkflowInfo(self.contract).fireTransition('approve')
1411        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
1412
1413    def test_contract_approval_in_UI(self):
1414        # Now let's see what the UI says when trying to approve a contract
1415        # with unverified documents.
1416        IWorkflowState(self.customer).setState('approved')
1417        IWorkflowState(self.document).setState('submitted')
1418        self.contract.document_object = self.document
1419        IWorkflowState(self.contract).setState('submitted')
1420        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1421        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1422        self.browser.getControl(name="transition").value = ['approve']
1423        self.browser.getControl("Apply").click()
1424        # InvalidTransitionError is catched
1425        self.assertTrue(
1426            '<div class="alert alert-warning">Attached documents must be verified first.</div>'
1427            in self.browser.contents)
1428        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1429        IWorkflowState(self.document).setState('verified')
1430        self.browser.getControl(name="transition").value = ['approve']
1431        self.browser.getControl("Apply").click()
1432        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
Note: See TracBrowser for help on using the repository browser.