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

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

Add Cancel button.

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