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

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

Move ProductOption? interfaces to productoptions to avoid nasty circular imports.

Use ISO_4217_CURRENCIES.

  • Property svn:keywords set to Id
File size: 69.2 KB
Line 
1## $Id: test_browser.py 12343 2014-12-30 12:52:40Z 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
605    def test_customer_baseedit(self):
606        # Customers can change the password
607        self.customer.personal_updated = datetime.utcnow()
608        self.browser.open(self.login_path)
609        self.browser.getControl(name="form.login").value = self.customer_id
610        self.browser.getControl(name="form.password").value = 'cpwd'
611        self.browser.getControl("Login").click()
612        self.assertEqual(self.browser.url, self.customer_path)
613        self.assertTrue('You logged in' in self.browser.contents)
614        self.browser.getLink("Edit").click()
615        self.browser.getControl(name="form.email").value = 'new_email@aa.ng'
616        self.browser.getControl("Save").click()
617        self.assertMatches('...Form has been saved...',
618                           self.browser.contents)
619        # Customer can view history
620        self.browser.getLink("History").click()
621        self.assertMatches('...Customer created by system...',
622            self.browser.contents)
623
624    def test_customer_login(self):
625        # Customer cant login if their password is not set
626        self.customer.password = None
627        self.browser.open(self.login_path)
628        self.browser.getControl(name="form.login").value = self.customer_id
629        self.browser.getControl(name="form.password").value = 'cpwd'
630        self.browser.getControl("Login").click()
631        self.assertTrue(
632            'You entered invalid credentials.' in self.browser.contents)
633        # We set the password again
634        IUserAccount(
635            self.app['customers'][self.customer_id]).setPassword('cpwd')
636        # Customers can't login if their account is suspended/deactivated
637        self.customer.suspended = True
638        self.browser.open(self.login_path)
639        self.browser.getControl(name="form.login").value = self.customer_id
640        self.browser.getControl(name="form.password").value = 'cpwd'
641        self.browser.getControl("Login").click()
642        self.assertMatches(
643            '...<div class="alert alert-warning">'
644            'Your account has been deactivated.</div>...', self.browser.contents)
645        # If suspended_comment is set this message will be flashed instead
646        self.customer.suspended_comment = u'Aetsch baetsch!'
647        self.browser.getControl(name="form.login").value = self.customer_id
648        self.browser.getControl(name="form.password").value = 'cpwd'
649        self.browser.getControl("Login").click()
650        self.assertMatches(
651            '...<div class="alert alert-warning">Aetsch baetsch!</div>...',
652            self.browser.contents)
653        self.customer.suspended = False
654        # Customers can't login if a temporary password has been set and
655        # is not expired
656        self.app['customers'][self.customer_id].setTempPassword(
657            'anybody', 'temp_cpwd')
658        self.browser.open(self.login_path)
659        self.browser.getControl(name="form.login").value = self.customer_id
660        self.browser.getControl(name="form.password").value = 'cpwd'
661        self.browser.getControl("Login").click()
662        self.assertMatches(
663            '...Your account has been temporarily deactivated...',
664            self.browser.contents)
665        # The customer can login with the temporary password
666        self.browser.open(self.login_path)
667        self.browser.getControl(name="form.login").value = self.customer_id
668        self.browser.getControl(name="form.password").value = 'temp_cpwd'
669        self.browser.getControl("Login").click()
670        self.assertMatches(
671            '...You logged in...', self.browser.contents)
672        # Customer can view the base data
673        self.browser.open(self.customer_path)
674        self.assertEqual(self.browser.headers['Status'], '200 Ok')
675        self.assertEqual(self.browser.url, self.customer_path)
676        # When the password expires ...
677        delta = timedelta(minutes=11)
678        self.app['customers'][self.customer_id].temp_password[
679            'timestamp'] = datetime.utcnow() - delta
680        self.app['customers'][self.customer_id]._p_changed = True
681        # ... the customer will be automatically logged out
682        self.assertRaises(
683            Unauthorized, self.browser.open, self.customer_path)
684        # Then the customer can login with the original password
685        self.browser.open(self.login_path)
686        self.browser.getControl(name="form.login").value = self.customer_id
687        self.browser.getControl(name="form.password").value = 'cpwd'
688        self.browser.getControl("Login").click()
689        self.assertMatches(
690            '...You logged in...', self.browser.contents)
691
692    def test_change_password_request(self):
693        self.browser.open('http://localhost/app/changepw')
694        self.browser.getControl(name="form.identifier").value = '123'
695        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
696        self.browser.getControl("Send login credentials").click()
697        self.assertTrue('An email with' in self.browser.contents)
698
699class CustomerRegistrationTests(CustomersFullSetup):
700    # Tests for customer registration
701
702    layer = FunctionalLayer
703
704    def test_request_pw(self):
705        # Customer with wrong number can't be found.
706        self.browser.open('http://localhost/app/requestpw')
707        self.browser.getControl(name="form.firstname").value = 'Anna'
708        self.browser.getControl(name="form.number").value = 'anynumber'
709        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
710        self.browser.getControl("Send login credentials").click()
711        self.assertTrue('No customer found.'
712            in self.browser.contents)
713        # Anonymous is not informed that firstname verification failed.
714        # It seems that the record doesn't exist.
715        self.browser.open('http://localhost/app/requestpw')
716        self.browser.getControl(name="form.firstname").value = 'Johnny'
717        self.browser.getControl(name="form.number").value = '123'
718        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
719        self.browser.getControl("Send login credentials").click()
720        self.assertTrue('No customer found.'
721            in self.browser.contents)
722        # Even with the correct firstname we can't register if a
723        # password has been set and used.
724        self.browser.getControl(name="form.firstname").value = 'Anna'
725        self.browser.getControl(name="form.number").value = '123'
726        self.browser.getControl("Send login credentials").click()
727        self.assertTrue('Your password has already been set and used.'
728            in self.browser.contents)
729        self.browser.open('http://localhost/app/requestpw')
730        self.app['customers'][self.customer_id].password = None
731        # The firstname field, used for verification, is not case-sensitive.
732        self.browser.getControl(name="form.firstname").value = 'aNNa'
733        self.browser.getControl(name="form.number").value = '123'
734        self.browser.getControl(name="form.email").value = 'new@yy.zz'
735        self.browser.getControl("Send login credentials").click()
736        # Yeah, we succeded ...
737        self.assertTrue('Your request was successful.'
738            in self.browser.contents)
739        # ... and  customer can be found in the catalog via the email address
740        cat = queryUtility(ICatalog, name='customers_catalog')
741        results = list(
742            cat.searchResults(
743            email=('new@yy.zz', 'new@yy.zz')))
744        self.assertEqual(self.customer,results[0])
745        logfile = os.path.join(
746            self.app['datacenter'].storage, 'logs', 'main.log')
747        logcontent = open(logfile).read()
748        self.assertTrue('zope.anybody - customers.browser.CustomerRequestPasswordPage - '
749                        '123 (K1000000) - new@yy.zz' in logcontent)
750        return
751
752    def test_create_account(self):
753        # Customer with wrong number can't be found.
754        self.browser.open('http://localhost/app/createaccount')
755        self.browser.getControl(name="form.firstname").value = 'Ruben'
756        self.browser.getControl(name="form.lastname").value = 'Gonzales'
757        self.browser.getControl(name="form.email").value = 'newcustomer@xx.zz'
758        self.browser.getControl("Send login credentials").click()
759        self.assertTrue('Your request was successful.'
760            in self.browser.contents)
761        # ... and  customer can be found in the catalog via the email address
762        cat = queryUtility(ICatalog, name='customers_catalog')
763        results = list(
764            cat.searchResults(
765            email=('newcustomer@xx.zz', 'newcustomer@xx.zz')))
766        self.assertEqual(self.app['customers']['K1000001'], results[0])
767        self.assertEqual(self.app['customers']['K1000001'].firstname, 'Ruben')
768        self.assertEqual(self.app['customers']['K1000001'].lastname, 'Gonzales')
769        logfile = os.path.join(
770            self.app['datacenter'].storage, 'logs', 'main.log')
771        logcontent = open(logfile).read()
772        self.assertTrue('zope.anybody - customers.browser.CustomerCreateAccountPage - '
773                        'K1000001 - newcustomer@xx.zz' in logcontent)
774        return
775
776class CustomerDataExportTests(CustomersFullSetup, FunctionalAsyncTestCase):
777    # Tests for CustomersContainer class views and pages
778
779    layer = FunctionalLayer
780
781    def wait_for_export_job_completed(self):
782        # helper function waiting until the current export job is completed
783        manager = getUtility(IJobManager)
784        job_id = self.app['datacenter'].running_exports[0][0]
785        job = manager.get(job_id)
786        wait_for_result(job)
787        return job_id
788
789    def test_datacenter_export(self):
790        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
791        self.browser.open('http://localhost/app/datacenter/@@export')
792        self.browser.getControl(name="exporter").value = ['customers']
793        self.browser.getControl("Create CSV file").click()
794
795        # When the job is finished and we reload the page...
796        job_id = self.wait_for_export_job_completed()
797        # ... the csv file can be downloaded ...
798        self.browser.open('http://localhost/app/datacenter/@@export')
799        self.browser.getLink("Download").click()
800        self.assertEqual(self.browser.headers['content-type'],
801            'text/csv; charset=UTF-8')
802        self.assertTrue(
803            'filename="WAeUP.Ikoba_customers_%s.csv' % job_id in
804            self.browser.headers['content-disposition'])
805        self.assertEqual(len(self.app['datacenter'].running_exports), 1)
806        job_id = self.app['datacenter'].running_exports[0][0]
807        # ... and discarded
808        self.browser.open('http://localhost/app/datacenter/@@export')
809        self.browser.getControl("Discard").click()
810        self.assertEqual(len(self.app['datacenter'].running_exports), 0)
811        # Creation, downloading and discarding is logged
812        logfile = os.path.join(
813            self.app['datacenter'].storage, 'logs', 'datacenter.log')
814        logcontent = open(logfile).read()
815        self.assertTrue(
816            'zope.mgr - browser.pages.ExportCSVPage - exported: '
817            'customers, job_id=%s'
818            % job_id in logcontent
819            )
820        self.assertTrue(
821            'zope.mgr - browser.pages.ExportCSVView - downloaded: '
822            'WAeUP.Ikoba_customers_%s.csv, job_id=%s'
823            % (job_id, job_id) in logcontent
824            )
825        self.assertTrue(
826            'zope.mgr - browser.pages.ExportCSVPage - discarded: '
827            'job_id=%s' % job_id in logcontent
828            )
829
830
831class DocumentUITests(CustomersFullSetup):
832    # Tests for customer document related views and pages
833
834    def test_manage_document(self):
835        # Managers can access the pages of customer documentsconter
836        # and can perform actions
837        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
838        self.browser.open(self.customer_path)
839        self.assertEqual(self.browser.headers['Status'], '200 Ok')
840        self.assertEqual(self.browser.url, self.customer_path)
841        self.browser.open(self.customer_path)
842        self.browser.getLink("Documents", index=1).click()
843        self.browser.getControl("Add document").click()
844        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
845        self.browser.getControl(name="form.title").value = 'My PDF Document'
846        self.browser.getControl("Add document").click()
847        self.assertTrue('PDF Document added.' in self.browser.contents)
848        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
849        document = self.customer['documents'][docid]
850
851        # Document can be edited
852        self.browser.getLink(docid).click()
853        self.browser.getLink("Manage").click()
854        self.browser.getControl(name="form.title").value = 'My second doc'
855        self.browser.getControl("Save").click()
856        self.assertTrue('Form has been saved.' in self.browser.contents)
857        self.browser.getLink("View").click()
858        self.assertEqual(self.browser.url,
859            self.documents_path + '/' + docid + '/index')
860
861        # Transitions can be performed
862        self.browser.getLink("Transition").click()
863        self.browser.getControl(name="transition").value = ['submit']
864        self.browser.getControl("Apply now").click()
865        self.browser.getLink("Transition").click()
866        # Document can only be verified if files have been uploaded before
867        self.browser.getControl(name="transition").value = ['verify']
868        self.browser.getControl("Apply now").click()
869        self.assertTrue('No file uploaded' in self.browser.contents)
870        self.assertEqual(document.state, 'submitted')
871        # We set state here manually (verification is tested in test_verify_document)
872        IWorkflowState(document).setState(VERIFIED)
873
874        # Manage button and form is no longer available
875        self.browser.open(self.documents_path + '/' + docid + '/index')
876        self.assertFalse(
877            'href="http://localhost/app/customers/K1000000/documents/%s/manage"'
878            % docid in self.browser.contents)
879        self.browser.open(self.documents_path + '/' + docid + '/manage')
880        self.assertTrue(
881            'The requested form is locked (read-only)'
882            in self.browser.contents)
883
884        # Documents can be removed
885        self.browser.getLink("Documents", index=1).click()
886        ctrl = self.browser.getControl(name='val_id')
887        ctrl.getControl(value=document.document_id).selected = True
888        self.browser.getControl("Remove selected", index=0).click()
889        self.assertTrue('Successfully removed' in self.browser.contents)
890
891        # All actions are being logged
892        logfile = os.path.join(
893            self.app['datacenter'].storage, 'logs', 'customers.log')
894        logcontent = open(logfile).read()
895
896        self.assertTrue(
897            'INFO - system - K1000000 - DOC1 - Document created'
898            in logcontent)
899        self.assertTrue(
900            'INFO - zope.mgr - customers.browser.DocumentAddFormPage '
901            '- K1000000 - added: PDF Document %s'
902            % document.document_id in logcontent)
903        self.assertTrue(
904            'INFO - zope.mgr - customers.browser.DocumentManageFormPage '
905            '- K1000000 - %s - saved: title' % docid
906            in logcontent)
907        self.assertTrue(
908            'INFO - zope.mgr - K1000000 - %s - Submitted for verification'
909            % docid in logcontent)
910        self.assertTrue(
911            'INFO - zope.mgr - customers.browser.DocumentsManageFormPage '
912            '- K1000000 - removed: %s' % docid
913            in logcontent)
914
915    def test_edit_sample_document(self):
916        # Customers can manage documents under certain conditions
917        self.browser.open(self.login_path)
918        self.browser.getControl(name="form.login").value = self.customer_id
919        self.browser.getControl(name="form.password").value = 'cpwd'
920        self.browser.getControl("Login").click()
921        self.assertMatches(
922            '...You logged in...', self.browser.contents)
923        self.browser.getLink("Documents").click()
924        self.browser.getControl("Add document").click()
925        self.browser.getControl(name="doctype").value = ['CustomerSampleDocument']
926        self.browser.getControl(name="form.title").value = 'My Sample Document'
927        self.browser.getControl("Add document").click()
928        self.assertTrue('Sample Document added.' in self.browser.contents)
929        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
930        document = self.customer['documents'][docid]
931
932        # Document can be edited ...
933        self.browser.getLink(docid).click()
934        self.browser.open(self.documents_path + '/' + docid + '/edit')
935        #self.browser.getLink("Edit").click()
936        self.assertTrue('The requested form is locked' in self.browser.contents)
937        # Customer is in wrong state
938        IWorkflowState(self.customer).setState(APPROVED)
939        self.browser.open(self.documents_path + '/' + docid + '/edit')
940        self.browser.getControl(name="form.title").value = 'My second doc'
941        self.browser.getControl("Save").click()
942        self.assertEqual(document.title, 'My second doc')
943        self.assertTrue('Form has been saved.' in self.browser.contents)
944        self.browser.getLink("View").click()
945        self.assertEqual(
946            self.browser.url, self.documents_path + '/%s/index' % docid)
947        # Costumer can upload a document.
948        self.browser.getLink("Edit").click()
949        ctrl = self.browser.getControl(name='samplescaneditupload')
950        file_obj = open(SAMPLE_IMAGE, 'rb')
951        file_ctrl = ctrl.mech_control
952        file_ctrl.add_file(file_obj, filename='my_document.jpg')
953        self.browser.getControl(
954            name='upload_samplescaneditupload').click()
955        self.assertTrue(
956            'href="http://localhost/app/customers/K1000000/documents/%s/sample"'
957            % docid in self.browser.contents)
958        # Costumer can submit the form. The form is also saved.
959        self.browser.getControl(name="form.title").value = 'My third doc'
960        self.browser.getControl("Final Submit").click()
961        self.assertEqual(document.title, 'My third doc')
962        self.assertEqual(document.state, 'submitted')
963        self.assertTrue('Document State: submitted for verification' in self.browser.contents)
964        # Customer can't edit the document once it has been submitted
965        self.browser.open(self.documents_path + '/%s/edit' % docid)
966        self.assertTrue('The requested form is locked' in self.browser.contents)
967
968    def test_manage_upload_sample_file(self):
969        # Managers can upload a file via the DocumentManageFormPage
970        # The image is stored even if form has errors
971        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
972        self.browser.open(self.customer_path + '/documents/DOC1/manage')
973        # Create a pseudo image file and select it to be uploaded
974        image = open(SAMPLE_IMAGE, 'rb')
975        ctrl = self.browser.getControl(name='samplescanmanageupload')
976        file_ctrl = ctrl.mech_control
977        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
978        # The Save action does not upload files
979        self.browser.getControl("Save").click() # submit form
980        self.assertFalse(
981            'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
982            in self.browser.contents)
983        # ... but the correct upload submit button does
984        image = open(SAMPLE_IMAGE)
985        ctrl = self.browser.getControl(name='samplescanmanageupload')
986        file_ctrl = ctrl.mech_control
987        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
988        self.browser.getControl(
989            name='upload_samplescanmanageupload').click()
990        self.assertTrue(
991            'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
992            in self.browser.contents)
993        # Browsing the link shows a real image
994        self.browser.open('sample')
995        self.assertEqual(
996            self.browser.headers['content-type'], 'image/jpeg')
997        self.assertEqual(len(self.browser.contents), 2787)
998        # We can't reupload a file. The existing file must be deleted first.
999        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1000        self.assertFalse(
1001            'upload_samplescanmanageupload' in self.browser.contents)
1002        # File must be deleted first
1003        self.browser.getControl(name='delete_samplescanmanageupload').click()
1004        self.assertTrue(
1005            'sample deleted' in self.browser.contents)
1006        # Uploading a file which is bigger than 150k will raise an error
1007        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
1008        ctrl = self.browser.getControl(name='samplescanmanageupload')
1009        file_ctrl = ctrl.mech_control
1010        file_ctrl.add_file(big_image, filename='my_sample_scan.jpg')
1011        self.browser.getControl(
1012            name='upload_samplescanmanageupload').click()
1013        self.assertTrue(
1014            'Uploaded file is too big' in self.browser.contents)
1015        # We do not rely on filename extensions given by uploaders
1016        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
1017        ctrl = self.browser.getControl(name='samplescanmanageupload')
1018        file_ctrl = ctrl.mech_control
1019        # Tell uploaded file is bmp
1020        file_ctrl.add_file(image, filename='my_sample_scan.bmp')
1021        self.browser.getControl(
1022            name='upload_samplescanmanageupload').click()
1023        self.assertTrue(
1024            # jpg file was recognized
1025            'File sample.jpg uploaded.' in self.browser.contents)
1026        # Delete file again
1027        self.browser.getControl(name='delete_samplescanmanageupload').click()
1028        self.assertTrue(
1029            'sample deleted' in self.browser.contents)
1030        # File names must meet several conditions
1031        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
1032        ctrl = self.browser.getControl(name='samplescanmanageupload')
1033        file_ctrl = ctrl.mech_control
1034        file_ctrl.add_file(bmp_image, filename='my_sample_scan.bmp')
1035        self.browser.getControl(
1036            name='upload_samplescanmanageupload').click()
1037        self.assertTrue('Only the following extensions are allowed'
1038            in self.browser.contents)
1039
1040    def test_verify_document(self):
1041        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1042        self.browser.open(self.customer_path + '/documents/DOC1/manage')
1043        image = open(SAMPLE_IMAGE, 'rb')
1044        ctrl = self.browser.getControl(name='samplescanmanageupload')
1045        file_ctrl = ctrl.mech_control
1046        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1047        self.browser.getControl(
1048            name='upload_samplescanmanageupload').click()
1049        IWorkflowState(self.document).setState(SUBMITTED)
1050        # Only after verifying the document, sample_md5 is set
1051        self.assertEqual(
1052            getattr(self.document, 'sample_md5', None), None)
1053        self.browser.open(self.documents_path + '/DOC1/trigtrans')
1054        self.browser.getControl(name="transition").value = ['verify']
1055        self.browser.getControl("Apply now").click()
1056        self.assertEqual(
1057            getattr(self.document, 'sample_md5', None),
1058                    '1d1ab893e87c240afb2104d61ddfe180')
1059
1060    def test_manage_upload_pdf_file(self):
1061        # Managers can upload a file via the DocumentManageFormPage
1062        # The image is stored even if form has errors
1063        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1064        self.browser.open(self.customer_path + '/documents')
1065        self.browser.getControl("Add document").click()
1066        self.browser.getControl(name="doctype").value = ['CustomerPDFDocument']
1067        self.browser.getControl(name="form.title").value = 'My PDF Document'
1068        self.browser.getControl("Add document").click()
1069        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
1070        self.browser.open(self.documents_path + '/%s/manage' % docid)
1071        # Create a pseudo image file and select it to be uploaded
1072        image = open(SAMPLE_IMAGE, 'rb')
1073        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1074        file_ctrl = ctrl.mech_control
1075        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
1076        self.browser.getControl(
1077            name='upload_pdfscanmanageupload').click()
1078        self.assertTrue(
1079            'pdf file extension expected' in self.browser.contents)
1080        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1081        file_ctrl = ctrl.mech_control
1082        file_ctrl.add_file(image, filename='my_sample_scan.pdf')
1083        self.browser.getControl(
1084            name='upload_pdfscanmanageupload').click()
1085        self.assertTrue(
1086            'Could not determine file type' in self.browser.contents)
1087        pdf = open(SAMPLE_PDF, 'rb')
1088        ctrl = self.browser.getControl(name='pdfscanmanageupload')
1089        file_ctrl = ctrl.mech_control
1090        file_ctrl.add_file(pdf, filename='my_sample_scan.pdf')
1091        self.browser.getControl(
1092            name='upload_pdfscanmanageupload').click()
1093        self.assertTrue(
1094            'href="http://localhost/app/customers/K1000000/documents/%s/sample.pdf">PDF File</a>'
1095            % docid in self.browser.contents)
1096        # Browsing the link shows a real pdf
1097        self.browser.open('sample.pdf')
1098        self.assertEqual(
1099            self.browser.headers['content-type'], 'application/pdf')
1100
1101    def test_view_slips(self):
1102        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1103        # Officers can open the document overview
1104        self.browser.open(self.customer_path + '/documents')
1105        self.browser.getLink("Download documents overview").click()
1106        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1107        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1108        path = os.path.join(samples_dir(), 'documents_overview_slip.pdf')
1109        open(path, 'wb').write(self.browser.contents)
1110        print "Sample PDF overview_slip.pdf written to %s" % path
1111        # Officers can open document slips which shows a thumbnail of
1112        # the jpeg file attached.
1113        file_id = IFileStoreNameChooser(self.document).chooseName(attr='sample.jpg')
1114        fs = ExtFileStore(root=self.dc_root)
1115        jpegfile = open(SAMPLE_IMAGE, 'rb')
1116        fs.createFile(file_id, jpegfile)
1117        self.browser.open(self.customer_path + '/documents/DOC1')
1118        self.browser.getLink("Download document slip").click()
1119        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1120        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1121        path = os.path.join(samples_dir(), 'document_slip.pdf')
1122        open(path, 'wb').write(self.browser.contents)
1123        print "Sample document_slip.pdf written to %s" % path
1124        # Officers can open merged pdf document slips
1125        pdfdocument = createObject('waeup.CustomerPDFDocument')
1126        pdfdocument.title = u'My first document'
1127        self.customer['documents'].addDocument(pdfdocument)
1128        # Add pdf file
1129        file_id = IFileStoreNameChooser(pdfdocument).chooseName(attr='sample.pdf')
1130        fs = ExtFileStore(root=self.dc_root)
1131        pdffile = open(SAMPLE_PDF, 'rb')
1132        fs.createFile(file_id, pdffile)
1133        docid = [i for i in self.customer['documents'].keys() if len(i) > 10][0]
1134        self.browser.open(self.customer_path + '/documents/' + docid)
1135        self.browser.getLink("Download document slip").click()
1136        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1137        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1138        path = os.path.join(samples_dir(), 'pdfdocument_slip.pdf')
1139        open(path, 'wb').write(self.browser.contents)
1140        print "Sample pdfdocument_slip.pdf written to %s" % path
1141
1142    def test_get_setmd5_file(self):
1143        # A proper file name chooser is registered for customer documents.
1144        # This is not a UI test. It's just a functional test.
1145        file_id = IFileStoreNameChooser(self.document).chooseName(attr='sample')
1146        fs = ExtFileStore(root=self.dc_root)
1147        fs.createFile(file_id, StringIO('my sample 1'))
1148        result = fs.getFileByContext(self.document, attr='sample')
1149        self.assertEqual(file_id, '__file-customerdocument__01000/K1000000/sample_DOC1_K1000000')
1150        self.assertEqual(result.read(), 'my sample 1')
1151        self.assertEqual(self.document.connected_files[0][1].read(), 'my sample 1')
1152        self.document.setMD5()
1153        self.assertEqual(self.document.sample_md5, 'a406995ee8eb6772bacf51aa4b0caa24')
1154        return
1155
1156
1157class ContractUITests(CustomersFullSetup):
1158    # Tests for contract related views and pages
1159
1160    def test_manage_contract(self):
1161        # Managers can access the pages of customer contractsconter
1162        # and can perform actions
1163        IWorkflowState(self.customer).setState(APPROVED)
1164        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1165        self.browser.open(self.customer_path)
1166        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1167        self.assertEqual(self.browser.url, self.customer_path)
1168        self.browser.open(self.customer_path)
1169        self.browser.getLink("Contracts").click()
1170        self.browser.getControl("Add contract").click()
1171        self.browser.getControl(name="contype").value = ['SampleContract']
1172        self.browser.getControl("Add contract").click()
1173        self.assertTrue('Sample Contract added.' in self.browser.contents)
1174        conid = [i for i in self.customer['contracts'].keys() if len(i) > 10][0]
1175        contract = self.customer['contracts'][conid]
1176
1177        # Contract can be edited
1178        self.browser.getLink(conid).click()
1179        self.browser.getLink("Manage").click()
1180        self.browser.getControl(name="form.product_object").value = ['SAM']
1181        self.browser.getControl("Save").click()
1182        self.assertTrue('Form has been saved.' in self.browser.contents)
1183        self.browser.getLink("View").click()
1184        self.assertEqual(self.browser.url, self.contracts_path + '/%s/index' % conid)
1185
1186        # Transitions can be performed
1187        self.browser.getLink("Transition").click()
1188        self.browser.getControl(name="transition").value = ['submit']
1189        self.browser.getControl("Apply now").click()
1190        self.browser.getLink("Transition").click()
1191        self.browser.getControl(name="transition").value = ['approve']
1192        self.browser.getControl("Apply now").click()
1193        self.assertEqual(contract.state, 'approved')
1194
1195        # Contracts can be removed
1196        self.browser.getLink("Contracts").click()
1197        ctrl = self.browser.getControl(name='val_id')
1198        ctrl.getControl(value=contract.contract_id).selected = True
1199        self.browser.getControl("Remove selected", index=0).click()
1200        self.assertTrue('Successfully removed' in self.browser.contents)
1201
1202        # All actions are being logged
1203        logfile = os.path.join(
1204            self.app['datacenter'].storage, 'logs', 'customers.log')
1205        logcontent = open(logfile).read()
1206        self.assertTrue(
1207            'INFO - zope.mgr - K1000000 - %s - Contract created' % conid
1208            in logcontent)
1209        self.assertTrue(
1210            'INFO - zope.mgr - customers.browser.ContractAddPage '
1211            '- K1000000 - added: Sample Contract %s'
1212            % contract.contract_id in logcontent)
1213        self.assertTrue(
1214            'INFO - zope.mgr - K1000000 - %s - Submitted for approval' % conid
1215            in logcontent)
1216        self.assertTrue(
1217            'INFO - zope.mgr - K1000000 - %s - Approved' % conid
1218            in logcontent)
1219        self.assertTrue(
1220            'INFO - zope.mgr - customers.browser.ContractsFormPage '
1221            '- K1000000 - removed: %s' % conid
1222            in logcontent)
1223
1224    def test_edit_sample_contract(self):
1225        # We add a second product.
1226        product = createObject('waeup.Product')
1227        product.product_id = u'LIC'
1228        product.title = u'Our License Product'
1229        product.contract_category = u'license'
1230        self.app['products'].addProduct(product)
1231        # Customers can manage contracts under certain conditions
1232        self.browser.open(self.login_path)
1233        self.browser.getControl(name="form.login").value = self.customer_id
1234        self.browser.getControl(name="form.password").value = 'cpwd'
1235        self.browser.getControl("Login").click()
1236        self.assertMatches(
1237            '...You logged in...', self.browser.contents)
1238        self.browser.getLink("Contracts").click()
1239        # Customer is in wrong state
1240        self.assertFalse('Add contract' in self.browser.contents)
1241        self.browser.open(self.contracts_path + '/addcontract')
1242        self.assertTrue('The requested form is locked' in self.browser.contents)
1243        IWorkflowState(self.customer).setState(APPROVED)
1244        self.browser.open(self.contracts_path)
1245        # Now customer can add a contract
1246        self.browser.getControl("Add contract").click()
1247        self.browser.getControl(name="contype").value = ['SampleContract']
1248        self.browser.getControl("Add contract").click()
1249        self.assertTrue('Sample Contract added.' in self.browser.contents)
1250        conid = [i for i in self.customer['contracts'].keys() if len(i) > 10][0]
1251        contract = self.customer['contracts'][conid]
1252        self.assertEqual(
1253            self.browser.url, self.contracts_path + '/%s/selectproduct' % conid)
1254        # SAM is in the correct contract_category ...
1255        self.assertTrue('<option value="SAM">' in self.browser.contents)
1256        # ... but NOTSAM not.
1257        self.assertFalse('<option value="LIC">' in self.browser.contents)
1258        # So far last_product_id is None.
1259        self.assertTrue(self.customer['contracts'][conid].last_product_id is None)
1260        self.browser.getControl(name="form.product_object").value = ['SAM']
1261        self.browser.getControl("Save and proceed").click()
1262        self.assertEqual(
1263            self.browser.url, self.contracts_path + '/%s/edit' % conid)
1264        # Document is a required field on edit form page.
1265        self.browser.getControl("Save").click()
1266        self.assertTrue('Document: <span class="error">Required input is missing.</span>'
1267            in self.browser.contents)
1268        # But our document can't be selected because it's not submitted
1269        self.assertFalse('My first document' in self.browser.contents)
1270        IWorkflowState(self.document).setState(SUBMITTED)
1271        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1272        self.browser.getControl(name="form.document_object").value = ['DOC1']
1273        self.browser.getControl("Save").click()
1274        # After saving the form, last_product_id and other attributes are set
1275        self.assertTrue('Form has been saved.' in self.browser.contents)
1276        self.assertEqual(self.customer['contracts'][conid].last_product_id, 'SAM')
1277        self.assertEqual(contract.title, 'Our Sample Product')
1278        self.assertEqual(contract.product_object, self.product)
1279        self.assertEqual(contract.document_object, self.document)
1280        # Saving the form again does not unset last_product_id
1281        self.browser.getControl("Save").click()
1282        self.assertEqual(self.customer['contracts'][conid].last_product_id, 'SAM')
1283        self.assertTrue('Form has been saved.' in self.browser.contents)
1284        # So far we have not yet set product options.
1285        # Unfortunately, we can't set them in test browser
1286        prodoption = ProductOption()
1287        prodoption.title = u'Any product option'
1288        prodoption.fee = Decimal('88.8')
1289        prodoption.currency = 'EUR'
1290        contract.product_options = [prodoption,]
1291        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1292        # We can see both the stored and the recent product options
1293        # from the chosen product.
1294        self.assertTrue('<option selected="selected" value="Any product option">'
1295                        'Any product option @ 88.8 Euro</option>'
1296                        in self.browser.contents)
1297        self.assertTrue('<option value="First option">First option '
1298                        '@ 99.9 US Dollar</option>' in self.browser.contents)
1299        # In test browser we can at least replace the option
1300        self.browser.getControl(
1301            name="form.product_options.0.").value = ['First option']
1302        self.assertEqual(contract.product_options[0].title, 'Any product option')
1303        self.browser.getControl("Save").click()
1304        self.assertEqual(contract.product_options[0].title, 'First option')
1305        self.browser.getLink("View").click()
1306        self.assertTrue('<span>First option @ 99.9 US Dollar</span>' in self.browser.contents)
1307        self.assertEqual(self.browser.url, self.contracts_path + '/%s/index' % conid)
1308        # An href attribute is referring to the document and product objects
1309        self.assertTrue('<a href="http://localhost/app/products/SAM">SAM -'
1310            in self.browser.contents)
1311        self.assertTrue(
1312            '<a href="http://localhost/app/customers/K1000000/documents/DOC1">DOC1 -'
1313            in self.browser.contents)
1314        # Customer can submit the form. The form is also saved.
1315        self.browser.getLink("Edit").click()
1316        self.browser.getControl("Apply now").click()
1317        self.assertEqual(contract.state, 'submitted')
1318        self.assertTrue('Contract State: submitted for approval' in self.browser.contents)
1319        # Customer can't edit the contract once it has been submitted
1320        self.browser.open(self.contracts_path + '/%s/edit' % conid)
1321        self.assertTrue('The requested form is locked' in self.browser.contents)
1322
1323    def test_view_slips(self):
1324        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1325        # Officers can open the contract overview
1326        self.browser.open(self.customer_path + '/contracts')
1327        self.browser.getLink("Download contracts overview").click()
1328        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1329        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1330        path = os.path.join(samples_dir(), 'contracts_overview_slip.pdf')
1331        open(path, 'wb').write(self.browser.contents)
1332        print "Sample PDF overview_slip.pdf written to %s" % path
1333        # Officers can open contract slips.
1334        # First we add a submitted document and a product.
1335        IWorkflowState(self.document).setState(SUBMITTED)
1336        self.contract.document_object = self.document
1337        self.contract.product_object = self.product
1338        self.browser.open(self.customer_path + '/contracts/CON1')
1339        self.browser.getLink("Download contract slip").click()
1340        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1341        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1342        path = os.path.join(samples_dir(), 'contract_slip.pdf')
1343        open(path, 'wb').write(self.browser.contents)
1344        print "Sample contract_slip.pdf written to %s" % path
1345
1346    def test_contract_approval(self):
1347        # This is not a UI test. It just a functional test.
1348        self.assertRaises(ConstraintNotSatisfied,
1349            self.contract.document_object, self.document)
1350        # Document must be at least submitted
1351        IWorkflowState(self.document).setState('submitted')
1352        self.contract.document_object = self.document
1353        IWorkflowState(self.contract).setState('submitted')
1354        self.assertRaises(InvalidTransitionError,
1355            IWorkflowInfo(self.contract).fireTransition, 'approve')
1356        # Document must be verified for the approval of contracts
1357        IWorkflowState(self.document).setState('verified')
1358        IWorkflowInfo(self.contract).fireTransition('approve')
1359        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
1360
1361    def test_contract_approval_in_UI(self):
1362        # Now let's see what the UI says why trying to approve a contract
1363        # with unverified documents.
1364        IWorkflowState(self.document).setState('submitted')
1365        self.contract.document_object = self.document
1366        IWorkflowState(self.contract).setState('submitted')
1367        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1368        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1369        self.browser.getControl(name="transition").value = ['approve']
1370        self.browser.getControl("Apply now").click()
1371        # InvalidTransitionError is catched
1372        self.assertTrue(
1373            '<div class="alert alert-warning">Attached documents must be verified first.</div>'
1374            in self.browser.contents)
1375        self.browser.open(self.contracts_path + '/CON1/trigtrans')
1376        IWorkflowState(self.document).setState('verified')
1377        self.browser.getControl(name="transition").value = ['approve']
1378        self.browser.getControl("Apply now").click()
1379        self.assertEqual(IWorkflowState(self.contract).getState(), 'approved')
Note: See TracBrowser for help on using the repository browser.