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

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

Rearrange some pages for customers.

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