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

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

Add activate/deactivate test.

File size: 21.4 KB
Line 
1## $Id: test_browser.py 11862 2014-10-21 07:07:04Z 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 datetime import datetime, timedelta, date
26from StringIO import StringIO
27import os
28import grok
29from zc.async.testing import wait_for_result
30from zope.event import notify
31from zope.component import createObject, queryUtility, getUtility
32from zope.component.hooks import setSite, clearSite
33from zope.catalog.interfaces import ICatalog
34from zope.security.interfaces import Unauthorized
35from zope.securitypolicy.interfaces import IPrincipalRoleManager
36from zope.testbrowser.testing import Browser
37from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
38from waeup.ikoba.testing import FunctionalLayer, FunctionalTestCase
39from waeup.ikoba.app import Company
40from waeup.ikoba.customers.interfaces import ICustomersUtils
41from waeup.ikoba.customers.customer import Customer
42from waeup.ikoba.interfaces import IUserAccount, IJobManager
43from waeup.ikoba.authentication import LocalRoleSetEvent
44from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
45
46PH_LEN = 15911  # Length of placeholder file
47
48SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
49SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
50
51def lookup_submit_value(name, value, browser):
52    """Find a button with a certain value."""
53    for num in range(0, 100):
54        try:
55            button = browser.getControl(name=name, index=num)
56            if button.value.endswith(value):
57                return button
58        except IndexError:
59            break
60    return None
61
62class CustomersFullSetup(FunctionalTestCase):
63    # A test case that only contains a setup and teardown
64    #
65    # Complete setup for customers handlings is rather complex and
66    # requires lots of things created before we can start. This is a
67    # setup that does all this, creates a university, creates PINs,
68    # etc.  so that we do not have to bother with that in different
69    # test cases.
70
71    layer = FunctionalLayer
72
73    def setUp(self):
74        super(CustomersFullSetup, self).setUp()
75
76        # Setup a sample site for each test
77        app = Company()
78        self.dc_root = tempfile.mkdtemp()
79        app['datacenter'].setStoragePath(self.dc_root)
80
81        # Prepopulate the ZODB...
82        self.getRootFolder()['app'] = app
83        # we add the site immediately after creation to the
84        # ZODB. Catalogs and other local utilities are not setup
85        # before that step.
86        self.app = self.getRootFolder()['app']
87        # Set site here. Some of the following setup code might need
88        # to access grok.getSite() and should get our new app then
89        setSite(app)
90
91        # Add customer with subobjects
92        customer = createObject('waeup.Customer')
93        customer.firstname = u'Anna'
94        customer.lastname = u'Tester'
95        customer.reg_number = u'123'
96        customer.sex = u'm'
97        customer.email = 'aa@aa.ng'
98        customer.phone = u'1234'
99        customer.date_of_birth = date(1981, 2, 4)
100        self.app['customers'].addCustomer(customer)
101        self.customer_id = customer.customer_id
102        self.customer = self.app['customers'][self.customer_id]
103
104        # Set password
105        IUserAccount(
106            self.app['customers'][self.customer_id]).setPassword('spwd')
107
108        self.login_path = 'http://localhost/app/login'
109        self.container_path = 'http://localhost/app/customers'
110        self.manage_container_path = self.container_path + '/@@manage'
111        self.add_customer_path = self.container_path + '/addcustomer'
112        self.customer_path = self.container_path + '/' + self.customer_id
113        self.manage_customer_path = self.customer_path + '/manage_base'
114        self.trigtrans_path = self.customer_path + '/trigtrans'
115        self.history_path = self.customer_path + '/history'
116
117        self.app['configuration'].carry_over = True
118        configuration = createObject('waeup.SessionConfiguration')
119        self.app['configuration'].addSessionConfiguration(configuration)
120
121        # Update the catalog
122        notify(grok.ObjectModifiedEvent(self.customer))
123
124        # Put the prepopulated site into test ZODB and prepare test
125        # browser
126        self.browser = Browser()
127        self.browser.handleErrors = False
128
129    def tearDown(self):
130        super(CustomersFullSetup, self).tearDown()
131        clearSite()
132        shutil.rmtree(self.dc_root)
133
134
135
136class CustomersContainerUITests(CustomersFullSetup):
137    # Tests for CustomersContainer class views and pages
138
139    layer = FunctionalLayer
140
141    def test_anonymous_access(self):
142        # Anonymous users can't access customers containers
143        self.assertRaises(
144            Unauthorized, self.browser.open, self.container_path)
145        self.assertRaises(
146            Unauthorized, self.browser.open, self.manage_container_path)
147        return
148
149    def test_manage_access(self):
150        # Managers can access the view page of customers
151        # containers and can perform actions
152        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
153        self.browser.open(self.container_path)
154        self.assertEqual(self.browser.headers['Status'], '200 Ok')
155        self.assertEqual(self.browser.url, self.container_path)
156        self.browser.getLink("Manage customer section").click()
157        self.assertEqual(self.browser.headers['Status'], '200 Ok')
158        self.assertEqual(self.browser.url, self.manage_container_path)
159        return
160
161    def test_add_search_delete_customers(self):
162        # Managers can add search and remove customers
163        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
164        self.browser.open(self.manage_container_path)
165        self.browser.getLink("Add customer").click()
166        self.assertEqual(self.browser.headers['Status'], '200 Ok')
167        self.assertEqual(self.browser.url, self.add_customer_path)
168        self.browser.getControl(name="form.firstname").value = 'Bob'
169        self.browser.getControl(name="form.lastname").value = 'Tester'
170        self.browser.getControl(name="form.reg_number").value = '123'
171        self.browser.getControl("Create customer record").click()
172        self.assertTrue('Registration number exists already'
173            in self.browser.contents)
174        self.browser.getControl(name="form.reg_number").value = '1234'
175        self.browser.getControl("Create customer record").click()
176        self.assertTrue('Customer record created' in self.browser.contents)
177
178        # Registration must be unique
179        self.browser.getLink("Manage").click()
180        self.browser.getControl(name="form.reg_number").value = '123'
181        self.browser.getControl("Save").click()
182        self.assertMatches('...Registration number exists...',
183                           self.browser.contents)
184
185        # We can find a customer with a certain customer_id
186        self.browser.open(self.container_path)
187        self.browser.getControl("Find customer(s)").click()
188        self.assertTrue('Empty search string' in self.browser.contents)
189        self.browser.getControl(name="searchtype").value = ['customer_id']
190        self.browser.getControl(name="searchterm").value = self.customer_id
191        self.browser.getControl("Find customer(s)").click()
192        self.assertTrue('Anna Tester' in self.browser.contents)
193
194        # We can find a customer by searching for all kind of name parts
195        self.browser.open(self.manage_container_path)
196        self.browser.getControl("Find customer(s)").click()
197        self.assertTrue('Empty search string' in self.browser.contents)
198        self.browser.getControl(name="searchtype").value = ['fullname']
199        self.browser.getControl(name="searchterm").value = 'Anna Tester'
200        self.browser.getControl("Find customer(s)").click()
201        self.assertTrue('Anna Tester' in self.browser.contents)
202        self.browser.open(self.manage_container_path)
203        self.browser.getControl(name="searchtype").value = ['fullname']
204        self.browser.getControl(name="searchterm").value = 'Anna'
205        self.browser.getControl("Find customer(s)").click()
206        self.assertTrue('Anna Tester' in self.browser.contents)
207        self.browser.open(self.manage_container_path)
208        self.browser.getControl(name="searchtype").value = ['fullname']
209        self.browser.getControl(name="searchterm").value = 'Tester'
210        self.browser.getControl("Find customer(s)").click()
211        self.assertTrue('Anna Tester' in self.browser.contents)
212        self.browser.open(self.manage_container_path)
213        self.browser.getControl(name="searchtype").value = ['fullname']
214        self.browser.getControl(name="searchterm").value = 'An'
215        self.browser.getControl("Find customer(s)").click()
216        self.assertFalse('Anna Tester' in self.browser.contents)
217        self.browser.open(self.manage_container_path)
218        self.browser.getControl(name="searchtype").value = ['fullname']
219        self.browser.getControl(name="searchterm").value = 'An*'
220        self.browser.getControl("Find customer(s)").click()
221        self.assertTrue('Anna Tester' in self.browser.contents)
222        self.browser.open(self.manage_container_path)
223        self.browser.getControl(name="searchtype").value = ['fullname']
224        self.browser.getControl(name="searchterm").value = 'tester'
225        self.browser.getControl("Find customer(s)").click()
226        self.assertTrue('Anna Tester' in self.browser.contents)
227        self.browser.open(self.manage_container_path)
228        self.browser.getControl(name="searchtype").value = ['fullname']
229        self.browser.getControl(name="searchterm").value = 'Tester Ana'
230        self.browser.getControl("Find customer(s)").click()
231        self.assertFalse('Anna Tester' in self.browser.contents)
232        self.browser.open(self.manage_container_path)
233        self.browser.getControl(name="searchtype").value = ['fullname']
234        self.browser.getControl(name="searchterm").value = 'Tester Anna'
235        self.browser.getControl("Find customer(s)").click()
236        self.assertTrue('Anna Tester' in self.browser.contents)
237        # The old searchterm will be used again
238        self.browser.getControl("Find customer(s)").click()
239        self.assertTrue('Anna Tester' in self.browser.contents)
240
241        # We can find suspended customers
242        self.customer.suspended = True
243        notify(grok.ObjectModifiedEvent(self.customer))
244        self.browser.open(self.manage_container_path)
245        self.browser.getControl(name="searchtype").value = ['suspended']
246        self.browser.getControl("Find customer(s)").click()
247        self.assertTrue('Anna Tester' in self.browser.contents)
248
249        # Removed customers won't be found
250        ctrl = self.browser.getControl(name='entries')
251        ctrl.getControl(value=self.customer_id).selected = True
252        self.browser.getControl("Remove selected", index=0).click()
253        self.assertTrue('Successfully removed' in self.browser.contents)
254        self.browser.getControl(name="searchtype").value = ['customer_id']
255        self.browser.getControl(name="searchterm").value = self.customer_id
256        self.browser.getControl("Find customer(s)").click()
257        self.assertTrue('No customer found' in self.browser.contents)
258        return
259
260class OfficerUITests(CustomersFullSetup):
261    # Tests for Customer class views and pages
262
263    def test_basic_auth(self):
264        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
265        self.browser.open('http://localhost/app')
266        self.browser.getLink("Logout").click()
267        self.assertTrue('You have been logged out' in self.browser.contents)
268        # But we are still logged in since we've used basic authentication here.
269        # Wikipedia says: Existing browsers retain authentication information
270        # until the tab or browser is closed or the user clears the history.
271        # HTTP does not provide a method for a server to direct clients to
272        # discard these cached credentials. This means that there is no
273        # effective way for a server to "log out" the user without closing
274        # the browser. This is a significant defect that requires browser
275        # manufacturers to support a "logout" user interface element ...
276        self.assertTrue('Manager' in self.browser.contents)
277
278    def test_basic_auth_base64(self):
279        auth_token = base64.b64encode('mgr:mgrpw')
280        self.browser.addHeader('Authorization', 'Basic %s' % auth_token)
281        self.browser.open(self.manage_container_path)
282        self.assertEqual(self.browser.headers['Status'], '200 Ok')
283
284    def test_manage_access(self):
285        # Managers can access the pages of customers
286        # and can perform actions
287        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
288        self.browser.open(self.customer_path)
289        self.assertEqual(self.browser.headers['Status'], '200 Ok')
290        self.assertEqual(self.browser.url, self.customer_path)
291        self.browser.getLink("Trigger").click()
292        self.assertEqual(self.browser.headers['Status'], '200 Ok')
293        # Managers can trigger transitions
294        self.browser.getControl(name="transition").value = ['start']
295        self.browser.getControl("Save").click()
296        # Managers can edit base
297        self.browser.open(self.customer_path)
298        self.browser.getLink("Manage").click()
299        self.assertEqual(self.browser.url, self.manage_customer_path)
300        self.assertEqual(self.browser.headers['Status'], '200 Ok')
301        self.browser.getControl(name="form.firstname").value = 'John'
302        self.browser.getControl(name="form.lastname").value = 'Tester'
303        self.browser.getControl(name="form.reg_number").value = '345'
304        self.browser.getControl(name="password").value = 'secret'
305        self.browser.getControl(name="control_password").value = 'secret'
306        self.browser.getControl("Save").click()
307        self.assertMatches('...Form has been saved...',
308                           self.browser.contents)
309
310    def test_manage_contact_customer(self):
311        # Managers can contact customer
312        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
313        self.customer.email = None
314        self.browser.open(self.customer_path)
315        self.browser.getLink("Send email").click()
316        self.browser.getControl(name="form.subject").value = 'Important subject'
317        self.browser.getControl(name="form.body").value = 'Hello!'
318        self.browser.getControl("Send message now").click()
319        self.assertTrue('An smtp server error occurred' in self.browser.contents)
320        self.customer.email = 'xx@yy.zz'
321        self.browser.getControl("Send message now").click()
322        self.assertTrue('Your message has been sent' in self.browser.contents)
323        return
324
325    def test_manage_upload_passport(self):
326        # Managers can upload a file via the CustomerBaseManageFormPage
327        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
328        self.browser.open(self.manage_customer_path)
329        image = open(SAMPLE_IMAGE_BMP, 'rb')
330        ctrl = self.browser.getControl(name='passportuploadmanage')
331        file_ctrl = ctrl.mech_control
332        file_ctrl.add_file(image, filename='my_photo.bmp')
333        self.browser.getControl(
334            name='upload_passportuploadmanage').click()
335        self.assertTrue('jpg file extension expected'
336            in self.browser.contents)
337        ctrl = self.browser.getControl(name='passportuploadmanage')
338        file_ctrl = ctrl.mech_control
339        image = open(SAMPLE_IMAGE, 'rb')
340        file_ctrl.add_file(image, filename='my_photo.jpg')
341        self.browser.getControl(
342            name='upload_passportuploadmanage').click()
343        self.assertTrue(
344            'src="http://localhost/app/customers/K1000000/passport.jpg"'
345            in self.browser.contents)
346        # We remove the passport file again
347        self.browser.open(self.manage_customer_path)
348        self.browser.getControl('Delete').click()
349        self.assertTrue('passport.jpg deleted' in self.browser.contents)
350
351
352    def test_manage_workflow(self):
353        # Managers can pass through the whole workflow
354        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
355        customer = self.app['customers'][self.customer_id]
356        self.browser.open(self.trigtrans_path)
357        self.browser.getControl(name="transition").value = ['start']
358        self.browser.getControl("Save").click()
359        self.browser.getControl(name="transition").value = ['request']
360        self.browser.getControl("Save").click()
361        self.browser.getControl(name="transition").value = ['reject']
362        self.browser.getControl("Save").click()
363        self.browser.getControl(name="transition").value = ['request']
364        self.browser.getControl("Save").click()
365        self.browser.getControl(name="transition").value = ['approve']
366        self.browser.getControl("Save").click()
367        self.browser.getControl(name="transition").value = ['reset1']
368        self.browser.getControl("Save").click()
369        return
370
371    def test_manage_import(self):
372        # Managers can import customer data files
373        datacenter_path = 'http://localhost/app/datacenter'
374        # Prepare a csv file for customers
375        open('customers.csv', 'wb').write(
376"""firstname,lastname,reg_number,date_of_birth,email,phone,sex,password
377Aaren,Pieri,1,1990-01-02,aa@aa.ng,1234,m,mypwd1
378Claus,Finau,2,1990-01-03,aa@aa.ng,1234,m,mypwd1
379Brit,Berson,2,1990-01-04,aa@aa.ng,1234,m,mypwd1
380""")
381        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
382        self.browser.open(datacenter_path)
383        self.browser.getLink('Upload data').click()
384        filecontents = StringIO(open('customers.csv', 'rb').read())
385        filewidget = self.browser.getControl(name='uploadfile:file')
386        filewidget.add_file(filecontents, 'text/plain', 'customers.csv')
387        self.browser.getControl(name='SUBMIT').click()
388        self.browser.getLink('Process data').click()
389        button = lookup_submit_value(
390            'select', 'customers_zope.mgr.csv', self.browser)
391        button.click()
392        importerselect = self.browser.getControl(name='importer')
393        modeselect = self.browser.getControl(name='mode')
394        importerselect.getControl('Customer Processor').selected = True
395        modeselect.getControl(value='create').selected = True
396        self.browser.getControl('Proceed to step 3').click()
397        self.assertTrue('Header fields OK' in self.browser.contents)
398        self.browser.getControl('Perform import').click()
399        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
400        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
401        self.assertTrue('Batch processing finished' in self.browser.contents)
402
403        # The customers are properly indexed and we can
404        # thus find a customer in  the department
405        self.browser.open(self.manage_container_path)
406        # We can search for a new customer by name ...
407        self.browser.getControl(name="searchtype").value = ['fullname']
408        self.browser.getControl(name="searchterm").value = 'Claus'
409        self.browser.getControl("Find customer(s)").click()
410        self.assertTrue('Claus Finau' in self.browser.contents)
411        # ... and check if the imported password has been properly set
412        ctrl = self.browser.getControl(name='entries')
413        value = ctrl.options[0]
414        claus = self.app['customers'][value]
415        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
416        return
417
418    def test_activate_deactivate_buttons(self):
419        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
420        self.browser.open(self.customer_path)
421        self.browser.getLink("Deactivate").click()
422        self.assertTrue(
423            'Customer account has been deactivated.' in self.browser.contents)
424        self.assertTrue(
425            'Base Data (account deactivated)' in self.browser.contents)
426        self.assertTrue(self.customer.suspended)
427        self.browser.getLink("Activate").click()
428        self.assertTrue(
429            'Customer account has been activated.' in self.browser.contents)
430        self.assertFalse(
431            'Base Data (account deactivated)' in self.browser.contents)
432        self.assertFalse(self.customer.suspended)
433        # History messages have been added ...
434        self.browser.getLink("History").click()
435        self.assertTrue(
436            'Customer account deactivated by Manager<br />' in self.browser.contents)
437        self.assertTrue(
438            'Customer account activated by Manager<br />' in self.browser.contents)
439        # ... and actions have been logged.
440        logfile = os.path.join(
441            self.app['datacenter'].storage, 'logs', 'customers.log')
442        logcontent = open(logfile).read()
443        self.assertTrue('zope.mgr - customers.browser.CustomerDeactivatePage - '
444                        'K1000000 - account deactivated' in logcontent)
445        self.assertTrue('zope.mgr - customers.browser.CustomerActivatePage - '
446                        'K1000000 - account activated' in logcontent)
447
Note: See TracBrowser for help on using the repository browser.