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

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

Render terms and conditions on contract slip.

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