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

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

Email address must be required.

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