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

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

Define product validity period in base package.

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