source: main/waeup.sirp/trunk/src/waeup/sirp/accesscodes/browser.txt @ 6774

Last change on this file since 6774 was 6552, checked in by uli, 13 years ago

Add tests for searching accesscodes via browser. Browser test coverage
for accesscode not at 100%, test coverage at all for accesscode at
96%.

File size: 13.7 KB
Line 
1:mod:`waeup.sirp.accesscodes.browser` -- UI components for access-codes
2***********************************************************************
3
4.. module:: waeup.sirp.accesscodes.browser
5
6Here we visit the access-code related parts of a WAeUP SIRP site using
7a virtual browser.
8
9:NOTTest-Layer: functional
10
11Preliminaries
12=============
13
14Before we can do anything, we have to create a university site:
15
16    >>> from zope.testbrowser.testing import Browser
17    >>> browser = Browser()
18    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
19    >>> browser.handleErrors = False
20    >>> root = getRootFolder()
21
22    >>> from waeup.sirp.app import University
23    >>> u = University()
24    >>> root['myuniversity'] = u
25
26We set a new datacenter storage path:
27
28  >>> import os
29  >>> browser.open('http://localhost/myuniversity')
30  >>> browser.getLink('Data Center').click()
31  >>> browser.getLink('Edit settings').click()
32  >>> pathsetting = browser.getControl(name='newpath')
33
34  >>> cwd = os.getcwd()
35  >>> uploadpath = os.path.join(cwd, 'ac_testfiles')
36  >>> os.mkdir(uploadpath)
37  >>> pathsetting.value = uploadpath
38  >>> browser.getControl(name='save').click()
39
40We remove any existing 'accesscodes' dir from datacenter dir:
41
42  >>> import shutil
43  >>> if os.path.exists(os.path.join(uploadpath, 'accesscodes')):
44  ...   shutil.rmtree(os.path.join(uploadpath, 'accesscodes'))
45
46
47Access-code management screen
48=============================
49
50For users that have the right to manage access-code related stuff, the
51home page of a `University` instance provides a link to the
52access-code management screen. In the beginning, there are naturally
53no batches available:
54
55    >>> browser.open('http://localhost/myuniversity')
56    >>> browser.getLink('Access Codes').click()
57    >>> print browser.contents
58    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
59    ...
60    <h2>Access Code Batches</h2>
61    ...
62    ... The following batches are available:
63    ...No batches yet...
64    ...
65
66Adding batches
67==============
68
69We can add a batch of access-codes using a button displayed in the
70action bar:
71
72    >>> browser.getLink('Add Access Code Batch').click()
73
74The add screen shows a form where we have to enter a prefix, the
75number of access codes to be generated and the costs for each card.
76
77    >>> browser.getControl(name='form.prefix').value = 'APP'
78    >>> browser.getControl(name='form.entry_num').value = '5'
79    >>> browser.getControl(name='form.cost').value = '12.12'
80
81If we click 'cancel' afterwards, the whole process will be cancelled
82and we'll be redirected to the management screen:
83
84    >>> browser.getControl('Cancel').click()
85    >>> browser.url
86    'http://localhost/myuniversity/accesscodes'
87
88    >>> 'Batch creation cancelled' in browser.contents
89    True
90
91Now let's try again and this time we finish the procedure by clicking
92'Create batch' in the form:
93
94    >>> browser.getLink('Add Access Code Batch').click()
95    >>> browser.getControl(name='form.prefix').value = 'APP'
96    >>> browser.getControl(name='form.entry_num').value = '5'
97    >>> browser.getControl(name='form.cost').value = '12.12'
98    >>> browser.getControl('Create batch').click()
99
100We're also redirected to the management screen, with a notice about
101the freshly created batch:
102
103    >>> print browser.contents
104    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
105    ...
106    <h2>Access Code Batches</h2>
107    ...
108    ... The following batches are available:
109    ...APP
110    ...-
111    ...1
112    ...
113    ...5
114    .../
115    ...0
116    ...
117    ...12.12...
118    ...zope.mgr...
119    ...
120
121which means: there exists a batch named ``APP-1`` with 5 entries of
122which 0 have been devalidated, each one costs ``12.12`` and the batch
123was created by ``zope.mgr``.
124
125We create a second batch to see whether searching and related stuff
126works:
127
128    >>> browser.getLink('Add Access Code Batch').click()
129    >>> browser.getControl(name='form.prefix').value = 'APP'
130    >>> browser.getControl(name='form.entry_num').value = '5'
131    >>> browser.getControl(name='form.cost').value = '10.12'
132    >>> browser.getControl('Create batch').click()
133
134And a third one that can be deleted afterwards:
135
136    >>> browser.getLink('Add Access Code Batch').click()
137    >>> browser.getControl(name='form.prefix').value = 'BLA'
138    >>> browser.getControl(name='form.entry_num').value = '3'
139    >>> browser.getControl(name='form.cost').value = '19.12'
140    >>> browser.getControl('Create batch').click()
141
142
143Creating Archive Files
144======================
145
146Once a batch is created, we can archive it. To do so we have to tick
147the respective checkbox and click on 'Archive':
148
149    >>> ctrl = browser.getControl(name='batches')
150    >>> ctrl.getControl(value='APP-2').selected = True
151    >>> browser.getControl(name='archive').click()
152    >>> print browser.contents
153    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
154    ...
155    <li ...>Archived APP-2 (APP-2_archive-...-zope.mgr.csv)</li>
156    ...
157
158If we do not select a batch and try to archive or delete, the system
159will complain:
160
161    >>> browser.getControl(name='archive').click()
162    >>> print browser.contents
163    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
164    ...
165    <li ...>No batch selected.</li>
166    ...
167
168Deleting Batches
169================
170
171We can delete batches. They are automatically archived when doing so:
172
173    >>> ctrl = browser.getControl(name='batches')
174    >>> ctrl.getControl(value='BLA-1').selected = True
175    >>> browser.getControl('Archive and Delete').click()
176    >>> print browser.contents
177    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
178    ...Archived BLA-1 (BLA-1_archive-...-zope.mgr.csv)...
179    ...Deleted batch BLA-1...
180    ...
181
182
183Reimporting Batches
184===================
185
186We can reimport batches using the log files written when a batch was
187created before. So one can reimport the freshly deleted BLA-1
188batch.
189
190To do so we must copy the logfile into the ``imports`` dir of
191accesscodes inside the university's datacenter storage. Otherwisae the
192list of importable files is empty:
193
194    >>> browser.getLink('Reimport Access Code Batch').click()
195    >>> print browser.contents
196    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
197    ...
198    ...No import batches available...
199    ...
200
201We can cancel that operation:
202
203    >>> browser.getControl('Cancel').click()
204
205We copy the ``BLA-1`` archive batch file over to the ``imports`` directory:
206
207    >>> ac_storage = os.path.join(uploadpath, 'accesscodes')
208    >>> logfile2 = os.path.join(ac_storage,
209    ...                         sorted(os.listdir(ac_storage))[-2])
210    >>> filename = os.path.basename(logfile2)
211    >>> filename
212    'BLA-1_archive...-zope.mgr.csv'
213
214    >>> import shutil
215    >>> import_path = os.path.join(ac_storage, 'imports')
216    >>> shutil.copy(logfile2, import_path)
217
218Now the file will be presented as import source:
219
220    >>> browser.getLink('Reimport Access Code Batch').click()
221    >>> filename in browser.contents
222    True
223
224If we do not tick a filename to import and click 'Reimport', we will
225be warned:
226
227    >>> browser.getControl('Reimport').click()
228    >>> print browser.contents
229    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
230    ...No file chosen. Action cancelled...
231
232Now let's really reimport the batch:
233
234    >>> browser.getLink('Reimport Access Code Batch').click()
235    >>> ctrl = browser.getControl(name='filenames')
236    >>> ctrl.getControl(value=filename).selected = True
237    >>> browser.getControl('Reimport').click()
238
239The batch does exist now again:
240
241    >>> print browser.contents
242    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
243    ...Successfully reimported: BLA-1_archive...-zope.mgr.csv...
244    ...
245
246If we try to reimport an existing batch, that won't work:
247
248    >>> browser.getLink('Reimport Access Code Batch').click()
249    >>> ctrl = browser.getControl(name='filenames')
250    >>> ctrl.getControl(value=filename).selected = True
251    >>> browser.getControl('Reimport').click()
252    >>> print browser.contents
253    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
254    ...This batch already exists: BLA-1_archive...-zope.mgr.csv...
255    ...
256
257Searching Access Codes
258======================
259
260We can search for access codes already created.
261
262First we pick some really existing access codes to look for:
263
264    >>> codes = getRootFolder()['myuniversity']['accesscodes']
265    >>> app_1_codes = codes[u'APP-1']
266    >>> app_2_codes = codes[u'APP-2']
267    >>> bla_1_codes = codes[u'BLA-1']
268
269    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
270    >>> ctrl = browser.getControl(name='searchtype')
271    >>> ctrl.getControl(value='code').selected = True
272    >>> browser.getControl(name='searchterm').value = app_1_codes.keys()[0]
273    >>> browser.getControl('Search').click()
274
275The first access code in the ``APP-1`` batch is displayed:
276
277    >>> print browser.contents
278    <!DOCTYPE html ...
279    ...
280      value="APP-1..." /></td>
281      <td>...</td>
282      <td>APP-1-...</td>
283      <td>initialized</td>
284      <td>... - AC initialized by zope.mgr</td>
285    ...
286
287We can also search for batch serials (the number of an access code
288inside its batch). Looking for number ``1`` will display first access
289code of each batch ('APP-1', 'APP-2', and 'BLA-1')
290
291    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
292    >>> ctrl = browser.getControl(name='searchtype')
293    >>> ctrl.getControl(value='batch_serial').selected = True
294    >>> browser.getControl(name='searchterm').value = '1'
295    >>> browser.getControl('Search').click()
296    >>> print browser.contents
297    <!DOCTYPE html ...
298    ...
299      value="APP-1..." /></td>
300      <td>1</td>
301      <td>APP-1-...</td>
302      <td>initialized</td>
303      <td>... - AC initialized by zope.mgr</td>
304    ...
305
306And we can search for text in history messages of access
307codes. Looking for the string ``AC initialized`` we get nearly all
308entries:
309
310    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
311    >>> ctrl = browser.getControl(name='searchtype')
312    >>> ctrl.getControl(value='history').selected = True
313    >>> browser.getControl(name='searchterm').value = 'AC initialized'
314    >>> browser.getControl('Search').click()
315    >>> print browser.contents
316    <!DOCTYPE html ...
317    ...
318      value="APP-1-<10-DIGITS>" /></td>
319      <td>1</td>
320      <td>APP-1-<10-DIGITS></td>
321      <td>initialized</td>
322      <td>... - AC initialized by zope.mgr</td>
323    ...
324
325Enabling and Disabling Found Access Codes
326-----------------------------------------
327
328If a search is successfull, we can enable or disable the found access
329codes:
330
331    >>> browser.open('http://localhost/myuniversity/accesscodes/search')
332    >>> ctrl = browser.getControl(name='searchtype')
333    >>> ctrl.getControl(value='history').selected = True
334    >>> browser.getControl(name='searchterm').value = 'AC initialized'
335    >>> browser.getControl('Search').click()
336
337This lists all access codes. We now tick one to disable it and click
338on the appropriate button:
339
340    >>> entries = browser.getControl(name='entries')
341    >>> entries.controls[0].selected = True
342    >>> browser.getControl('Disable ACs').click()
343    >>> print browser.contents
344    <!DOCTYPE html ...
345    <li class="message">APP-1-<10-DIGITS> disabled.</li>
346    ...
347
348We cannot disable the same entry again:
349
350    >>> entries = browser.getControl(name='entries')
351    >>> entries.controls[0].selected = True
352    >>> browser.getControl('Disable ACs').click()
353    >>> print browser.contents
354    <!DOCTYPE html ...
355    <li ...>APP-1-<10-DIGITS>: Disable transition not allowed.</li>
356    ...
357
358But we can reenable the same entry:
359
360    >>> entries = browser.getControl(name='entries')
361    >>> entries.controls[0].selected = True
362    >>> browser.getControl('Enable ACs').click()
363    >>> print browser.contents
364    <!DOCTYPE html ...
365    <li class="message">APP-1-<10-DIGITS> (re-)enabled.</li>
366    ...
367
368Enabling already enabled items gives a warning:
369
370    >>> entries = browser.getControl(name='entries')
371    >>> entries.controls[0].selected = True
372    >>> browser.getControl('Enable ACs').click()
373    >>> print browser.contents
374    <!DOCTYPE html ...
375    <li ...>APP-1-<10-DIGITS>: Re-enable transition not allowed.</li>
376    ...
377
378Log- and Archive Files
379======================
380
381We store log- and archive-files inside the storage managed by the
382local datacenter. Access-code related files are placed inside an
383``accesscode`` subdir:
384
385    >>> ac_storage = os.path.join(uploadpath, 'accesscodes')
386
387Log files for access-code batches
388---------------------------------
389
390Whenever a batch is created, there is also a log file with all entries
391created:
392
393    >>> sorted(os.listdir(ac_storage))
394    ['APP-1-...-zope.mgr.csv', 'APP-2-...-zope.mgr.csv', ...]
395
396Each logfile name contains the prefix, batch number, date of creation
397and userid of creator.
398
399    >>> logfile1 = os.path.join(ac_storage,
400    ...                         sorted(os.listdir(ac_storage))[0])
401    >>> print open(logfile1, 'rb').read()
402    "serial","ac","cost"
403    "APP","1","12.12"
404    "0","APP-1-<10-DIGITS>"
405    "1","APP-1-<10-DIGITS>"
406    "2","APP-1-<10-DIGITS>"
407    "3","APP-1-<10-DIGITS>"
408    "4","APP-1-<10-DIGITS>"
409
410
411
412Archive files
413-------------
414
415We created an archive file above. An archive file name consists of the
416batch prefix, batch number, the string ``_archive``, creation datetime of
417the archive file and userid of batch creator:
418
419    >>> sorted(os.listdir(ac_storage))
420    [..., 'BLA-1_archive-...-zope.mgr.csv', 'imports']
421
422Archive files eventually also contain infos about invalidation dates
423and have a slightly different format therefore.
424
425    >>> archive_file = os.path.join(ac_storage,
426    ...                             sorted(os.listdir(ac_storage))[-2])
427    >>> print open(archive_file, 'rb').read()
428    "prefix","serial","ac","state","history"
429    "BLA","19.12","1","3"
430    "BLA","0","BLA-1-<10-DIGITS>","initialized","..."
431    "BLA","1","BLA-1-<10-DIGITS>","initialized","..."
432    "BLA","2","BLA-1-<10-DIGITS>","initialized","..."
433
434Clean up:
435
436    >>> shutil.rmtree(uploadpath)
Note: See TracBrowser for help on using the repository browser.