• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / z3c.form / 16248976312

02 Jul 2025 05:53AM UTC coverage: 95.275%. Remained the same
16248976312

push

github

icemac
Back to development: 6.1

717 of 800 branches covered (89.63%)

Branch coverage included in aggregate %.

3699 of 3835 relevant lines covered (96.45%)

0.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.09
/src/z3c/form/testing.py
1
##############################################################################
2
#
3
# Copyright (c) 2007 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""Common z3c.form test setups"""
15

16
import base64
1✔
17
import os
1✔
18
import pprint
1✔
19
import shutil
1✔
20
from doctest import register_optionflag
1✔
21

22
import lxml.doctestcompare
1✔
23
import lxml.html
1✔
24
import zope.browserresource
1✔
25
import zope.component
1✔
26
import zope.configuration.xmlconfig
1✔
27
import zope.interface
1✔
28
import zope.schema
1✔
29
from zope.browserpage.viewpagetemplatefile import BoundPageTemplate
1✔
30
from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile
1✔
31
from zope.pagetemplate.interfaces import IPageTemplate
1✔
32
from zope.publisher.browser import TestRequest
1✔
33
from zope.schema.fieldproperty import FieldProperty
1✔
34
from zope.security import checker
1✔
35
from zope.security.interfaces import IInteraction
1✔
36
from zope.security.interfaces import ISecurityPolicy
1✔
37

38
import z3c.form
1✔
39
from z3c.form import browser
1✔
40
from z3c.form import button
1✔
41
from z3c.form import contentprovider
1✔
42
from z3c.form import converter
1✔
43
from z3c.form import datamanager
1✔
44
from z3c.form import error
1✔
45
from z3c.form import field
1✔
46
from z3c.form import form
1✔
47
from z3c.form import interfaces
1✔
48
from z3c.form import outputchecker
1✔
49
from z3c.form import term
1✔
50
from z3c.form import tests
1✔
51
from z3c.form import validator
1✔
52
from z3c.form import widget
1✔
53
from z3c.form.browser import radio
1✔
54
from z3c.form.browser import select
1✔
55
from z3c.form.browser import text
1✔
56
from z3c.form.browser import textarea
1✔
57

58

59
# register lxml doctest option flags
60
lxml.doctestcompare.NOPARSE_MARKUP = register_optionflag('NOPARSE_MARKUP')
1✔
61

62
outputChecker = outputchecker.OutputChecker(patterns=())
1✔
63

64

65
class TestingFileUploadDataConverter(converter.FileUploadDataConverter):
1✔
66
    """A special file upload data converter that works with testing."""
67
    zope.component.adapts(
1✔
68
        zope.schema.interfaces.IBytes, interfaces.IFileWidget)
69

70
    def toFieldValue(self, value):
1✔
71
        if value is None or value == '':
1!
72
            value = self.widget.request.get(self.widget.name + '.testing', '')
1✔
73
            encoding = self.widget.request.get(
1✔
74
                self.widget.name + '.encoding', 'plain')
75

76
            # allow for the case where the file contents are base64 encoded.
77
            if encoding == 'base64':
1✔
78
                value = base64.b64decode(value)
1✔
79
            self.widget.request.form[self.widget.name] = value
1✔
80

81
        return super().toFieldValue(value)
1✔
82

83

84
@zope.interface.implementer(interfaces.IFormLayer)
1✔
85
class TestRequest(TestRequest):
1✔
86
    def __init__(self, body_instream=None, environ=None, form=None,
1✔
87
                 skin=None, **kw):
88
        if form is not None:
1✔
89
            lists = {}
1✔
90
            for k in list(form.keys()):
1✔
91
                if isinstance(form[k], str):
1✔
92
                    # ensure unicode otherwise z3c.forms burps on str
93
                    form[k] = str(form[k])
1✔
94

95
                if k.endswith(':list'):
1✔
96
                    key = k.split(':', 1)[0]
1✔
97
                    lists.setdefault(key, []).append(form[k])
1✔
98
                    del form[k]
1✔
99

100
            form.update(lists)
1✔
101

102
        super().__init__(
1✔
103
            body_instream, environ, form, skin, **kw)
104

105

106
@zope.interface.implementer(IInteraction)
1✔
107
@zope.interface.provider(ISecurityPolicy)
1✔
108
class SimpleSecurityPolicy:
1✔
109
    """Allow all access."""
110

111
    loggedIn = False
1✔
112
    allowedPermissions = ()
1✔
113

114
    def __init__(self, loggedIn=False, allowedPermissions=()):
1✔
115
        self.loggedIn = loggedIn
1✔
116
        self.allowedPermissions = allowedPermissions + (checker.CheckerPublic,)
1✔
117

118
    def __call__(self, *participations):
1✔
119
        self.participations = []
1✔
120
        return self
1✔
121

122
    def checkPermission(self, permission, object):
1✔
123
        if self.loggedIn:
1✔
124
            if permission in self.allowedPermissions:
1!
125
                return True
1✔
126
        return False
1✔
127

128

129
def getPath(filename):
1✔
130
    return os.path.join(os.path.dirname(browser.__file__), filename)
1✔
131

132

133
#############################
134
# classes required by ObjectWidget tests
135
#
136

137
class IMySubObject(zope.interface.Interface):
1✔
138
    foofield = zope.schema.Int(
1✔
139
        title="My foo field",
140
        default=1111,
141
        max=9999,
142
        required=True)
143
    barfield = zope.schema.Int(
1✔
144
        title="My dear bar",
145
        default=2222,
146
        required=False)
147

148

149
@zope.interface.implementer(IMySubObject)
1✔
150
class MySubObject:
1✔
151

152
    foofield = FieldProperty(IMySubObject['foofield'])
1✔
153
    barfield = FieldProperty(IMySubObject['barfield'])
1✔
154

155

156
class IMySecond(zope.interface.Interface):
1✔
157
    subfield = zope.schema.Object(
1✔
158
        title="Second-subobject",
159
        schema=IMySubObject)
160
    moofield = zope.schema.TextLine(title="Something")
1✔
161

162

163
@zope.interface.implementer(IMySecond)
1✔
164
class MySecond:
1✔
165

166
    subfield = FieldProperty(IMySecond['subfield'])
1✔
167
    moofield = FieldProperty(IMySecond['moofield'])
1✔
168

169

170
class IMyObject(zope.interface.Interface):
1✔
171
    subobject = zope.schema.Object(title='my object', schema=IMySubObject)
1✔
172
    name = zope.schema.TextLine(title='name')
1✔
173

174

175
@zope.interface.implementer(IMyObject)
1✔
176
class MyObject:
1✔
177

178
    def __init__(self, name='', subobject=None):
1✔
179
        self.subobject = subobject
1✔
180
        self.name = name
1✔
181

182

183
class IMyComplexObject(zope.interface.Interface):
1✔
184
    subobject = zope.schema.Object(title='my object', schema=IMySecond)
1✔
185
    name = zope.schema.TextLine(title='name')
1✔
186

187

188
class IMySubObjectMulti(zope.interface.Interface):
1✔
189
    foofield = zope.schema.Int(
1✔
190
        title="My foo field",
191
        default=None,  # default is None here!
192
        max=9999,
193
        required=True)
194
    barfield = zope.schema.Int(
1✔
195
        title="My dear bar",
196
        default=2222,
197
        required=False)
198

199

200
@zope.interface.implementer(IMySubObjectMulti)
1✔
201
class MySubObjectMulti:
1✔
202

203
    foofield = FieldProperty(IMySubObjectMulti['foofield'])
1✔
204
    barfield = FieldProperty(IMySubObjectMulti['barfield'])
1✔
205

206

207
class IMyMultiObject(zope.interface.Interface):
1✔
208
    listOfObject = zope.schema.List(
1✔
209
        title="My list field",
210
        value_type=zope.schema.Object(
211
            title='my object widget',
212
            schema=IMySubObjectMulti),
213
    )
214
    name = zope.schema.TextLine(title='name')
1✔
215

216

217
@zope.interface.implementer(IMyMultiObject)
1✔
218
class MyMultiObject:
1✔
219

220
    listOfObject = FieldProperty(IMyMultiObject['listOfObject'])
1✔
221
    name = FieldProperty(IMyMultiObject['name'])
1✔
222

223
    def __init__(self, name='', listOfObject=None):
1✔
224
        self.listOfObject = listOfObject
1✔
225
        self.name = name
1✔
226

227

228
def setUp(test):
1✔
229
    from zope.component.testing import setUp
1✔
230
    setUp()
1✔
231
    from zope.container.testing import setUp
1✔
232
    setUp()
1✔
233
    from zope.component import hooks
1✔
234
    hooks.setHooks()
1✔
235
    from zope.traversing.testing import setUp
1✔
236
    setUp()
1✔
237

238
    from zope.i18n.interfaces import IUserPreferredLanguages
1✔
239
    from zope.publisher.browser import BrowserLanguages
1✔
240
    from zope.publisher.interfaces.http import IHTTPRequest
1✔
241
    zope.component.provideAdapter(BrowserLanguages, [IHTTPRequest],
1✔
242
                                  IUserPreferredLanguages)
243

244
    from zope.site.folder import rootFolder
1✔
245
    site = rootFolder()
1✔
246
    from zope.component.interfaces import ISite
1✔
247
    from zope.site.site import LocalSiteManager
1✔
248
    if not ISite.providedBy(site):
1!
249
        site.setSiteManager(LocalSiteManager(site))
1✔
250
    hooks.setSite(site)
1✔
251
    test.globs = {
1✔
252
        'root': site,
253
        'pprint': pprint.pprint}
254

255

256
def setUpZPT(suite):
1✔
257
    setUp(suite)
1✔
258

259

260
def setUpZ3CPT(suite):
1✔
261
    import z3c.pt
×
262
    import z3c.ptcompat
×
263
    setUp(suite)
×
264
    zope.configuration.xmlconfig.XMLConfig('configure.zcml', z3c.pt)()
×
265
    zope.configuration.xmlconfig.XMLConfig('configure.zcml', z3c.ptcompat)()
×
266

267

268
def setUpIntegration(test):
1✔
269
    setUp(test)
1✔
270
    zope.configuration.xmlconfig.XMLConfig('meta.zcml', zope.component)()
1✔
271
    zope.configuration.xmlconfig.XMLConfig('meta.zcml', zope.security)()
1✔
272
    zope.configuration.xmlconfig.XMLConfig('meta.zcml', zope.i18n)()
1✔
273
    zope.configuration.xmlconfig.XMLConfig('meta.zcml', zope.browserresource)()
1✔
274
    zope.configuration.xmlconfig.XMLConfig('meta.zcml', z3c.form)()
1✔
275
    zope.configuration.xmlconfig.XMLConfig('configure.zcml', z3c.form)()
1✔
276

277

278
def setupFormDefaults():
1✔
279
    # Validator adapters
280
    zope.component.provideAdapter(validator.SimpleFieldValidator)
1✔
281
    zope.component.provideAdapter(validator.InvariantsValidator)
1✔
282
    # Data manager adapter to get and set values to content
283
    zope.component.provideAdapter(datamanager.AttributeField)
1✔
284
    # Adapter to use form.fields to generate widgets
285
    zope.component.provideAdapter(field.FieldWidgets)
1✔
286
    # Adapter that uses form.fields to generate widgets
287
    # AND interlace content providers
288
    zope.component.provideAdapter(contentprovider.FieldWidgetsAndProviders)
1✔
289
    # Adapters to lookup the widget for a field
290
    # Text Field Widget
291
    zope.component.provideAdapter(
1✔
292
        text.TextFieldWidget,
293
        adapts=(zope.schema.interfaces.IBytesLine, interfaces.IFormLayer))
294
    zope.component.provideAdapter(
1✔
295
        text.TextFieldWidget,
296
        adapts=(zope.schema.interfaces.IASCIILine, interfaces.IFormLayer))
297
    zope.component.provideAdapter(
1✔
298
        text.TextFieldWidget,
299
        adapts=(zope.schema.interfaces.ITextLine, interfaces.IFormLayer))
300
    zope.component.provideAdapter(
1✔
301
        text.TextFieldWidget,
302
        adapts=(zope.schema.interfaces.IId, interfaces.IFormLayer))
303
    zope.component.provideAdapter(
1✔
304
        text.TextFieldWidget,
305
        adapts=(zope.schema.interfaces.IInt, interfaces.IFormLayer))
306
    zope.component.provideAdapter(
1✔
307
        text.TextFieldWidget,
308
        adapts=(zope.schema.interfaces.IFloat, interfaces.IFormLayer))
309
    zope.component.provideAdapter(
1✔
310
        text.TextFieldWidget,
311
        adapts=(zope.schema.interfaces.IDecimal, interfaces.IFormLayer))
312
    zope.component.provideAdapter(
1✔
313
        text.TextFieldWidget,
314
        adapts=(zope.schema.interfaces.IDate, interfaces.IFormLayer))
315
    zope.component.provideAdapter(
1✔
316
        text.TextFieldWidget,
317
        adapts=(zope.schema.interfaces.IDatetime, interfaces.IFormLayer))
318
    zope.component.provideAdapter(
1✔
319
        text.TextFieldWidget,
320
        adapts=(zope.schema.interfaces.ITime, interfaces.IFormLayer))
321
    zope.component.provideAdapter(
1✔
322
        text.TextFieldWidget,
323
        adapts=(zope.schema.interfaces.ITimedelta, interfaces.IFormLayer))
324
    zope.component.provideAdapter(
1✔
325
        text.TextFieldWidget,
326
        adapts=(zope.schema.interfaces.IURI, interfaces.IFormLayer))
327

328
    # Widget Layout
329
    zope.component.provideAdapter(
1✔
330
        widget.WidgetLayoutFactory(getPath('widget_layout.pt'), 'text/html'),
331
        (None, None, None, None, interfaces.IWidget),
332
        interfaces.IWidgetLayoutTemplate, name=interfaces.INPUT_MODE)
333
    zope.component.provideAdapter(
1✔
334
        widget.WidgetLayoutFactory(getPath('widget_layout.pt'), 'text/html'),
335
        (None, None, None, None, interfaces.IWidget),
336
        interfaces.IWidgetLayoutTemplate, name=interfaces.DISPLAY_MODE)
337
    zope.component.provideAdapter(
1✔
338
        widget.WidgetLayoutFactory(
339
            getPath('widget_layout_hidden.pt'), 'text/html'),
340
        (None, None, None, None, interfaces.IWidget),
341
        interfaces.IWidgetLayoutTemplate, name=interfaces.HIDDEN_MODE)
342

343
    # Text Field Widget
344
    zope.component.provideAdapter(
1✔
345
        widget.WidgetTemplateFactory(getPath('text_input.pt'), 'text/html'),
346
        (None, None, None, None, interfaces.ITextWidget),
347
        IPageTemplate, name=interfaces.INPUT_MODE)
348
    zope.component.provideAdapter(
1✔
349
        widget.WidgetTemplateFactory(getPath('text_display.pt'), 'text/html'),
350
        (None, None, None, None, interfaces.ITextWidget),
351
        IPageTemplate, name=interfaces.DISPLAY_MODE)
352
    zope.component.provideAdapter(
1✔
353
        widget.WidgetTemplateFactory(getPath('text_hidden.pt'), 'text/html'),
354
        (None, None, None, None, interfaces.ITextWidget),
355
        IPageTemplate, name=interfaces.HIDDEN_MODE)
356

357
    # Textarea Field Widget
358
    zope.component.provideAdapter(
1✔
359
        textarea.TextAreaFieldWidget,
360
        adapts=(zope.schema.interfaces.IASCII, interfaces.IFormLayer))
361
    zope.component.provideAdapter(
1✔
362
        textarea.TextAreaFieldWidget,
363
        adapts=(zope.schema.interfaces.IText, interfaces.IFormLayer))
364
    zope.component.provideAdapter(
1✔
365
        widget.WidgetTemplateFactory(
366
            getPath('textarea_input.pt'), 'text/html'),
367
        (None, None, None, None, interfaces.ITextAreaWidget),
368
        IPageTemplate, name=interfaces.INPUT_MODE)
369
    zope.component.provideAdapter(
1✔
370
        widget.WidgetTemplateFactory(
371
            getPath('textarea_display.pt'), 'text/html'),
372
        (None, None, None, None, interfaces.ITextAreaWidget),
373
        IPageTemplate, name=interfaces.DISPLAY_MODE)
374

375
    # Radio Field Widget
376
    zope.component.provideAdapter(radio.RadioFieldWidget)
1✔
377
    zope.component.provideAdapter(
1✔
378
        widget.WidgetTemplateFactory(getPath('radio_input.pt'), 'text/html'),
379
        (None, None, None, None, interfaces.IRadioWidget),
380
        IPageTemplate, name=interfaces.INPUT_MODE)
381
    zope.component.provideAdapter(
1✔
382
        widget.WidgetTemplateFactory(getPath('radio_display.pt'), 'text/html'),
383
        (None, None, None, None, interfaces.IRadioWidget),
384
        IPageTemplate, name=interfaces.DISPLAY_MODE)
385
    zope.component.provideAdapter(
1✔
386
        widget.WidgetTemplateFactory(getPath('radio_input_single.pt'),
387
                                     'text/html'),
388
        (None, None, None, None, interfaces.IRadioWidget),
389
        IPageTemplate, name='input_single')
390
    zope.component.provideAdapter(
1✔
391
        widget.WidgetTemplateFactory(getPath('radio_hidden_single.pt'),
392
                                     'text/html'),
393
        (None, None, None, None, interfaces.IRadioWidget),
394
        IPageTemplate, name='hidden_single')
395

396
    # Select Widget
397
    zope.component.provideAdapter(select.ChoiceWidgetDispatcher)
1✔
398
    zope.component.provideAdapter(select.SelectFieldWidget)
1✔
399
    zope.component.provideAdapter(
1✔
400
        widget.WidgetTemplateFactory(getPath('select_input.pt'), 'text/html'),
401
        (None, None, None, None, interfaces.ISelectWidget),
402
        IPageTemplate, name=interfaces.INPUT_MODE)
403
    zope.component.provideAdapter(
1✔
404
        widget.WidgetTemplateFactory(
405
            getPath('select_display.pt'), 'text/html'),
406
        (None, None, None, None, interfaces.ISelectWidget),
407
        IPageTemplate, name=interfaces.DISPLAY_MODE)
408
    zope.component.provideAdapter(
1✔
409
        widget.WidgetTemplateFactory(getPath('select_hidden.pt'), 'text/html'),
410
        (None, None, None, None, interfaces.ISelectWidget),
411
        IPageTemplate, name=interfaces.HIDDEN_MODE)
412

413
    # Checkbox Field Widget; register only templates
414
    zope.component.provideAdapter(
1✔
415
        widget.WidgetTemplateFactory(
416
            getPath('checkbox_input.pt'), 'text/html'),
417
        (None, None, None, None, interfaces.ICheckBoxWidget),
418
        IPageTemplate, name=interfaces.INPUT_MODE)
419
    zope.component.provideAdapter(
1✔
420
        widget.WidgetTemplateFactory(
421
            getPath('checkbox_display.pt'), 'text/html'),
422
        (None, None, None, None, interfaces.ICheckBoxWidget),
423
        IPageTemplate, name=interfaces.DISPLAY_MODE)
424
    # Submit Field Widget
425
    zope.component.provideAdapter(
1✔
426
        widget.WidgetTemplateFactory(getPath('submit_input.pt'), 'text/html'),
427
        (None, None, None, None, interfaces.ISubmitWidget),
428
        IPageTemplate, name=interfaces.INPUT_MODE)
429
    # selectwidget helper adapters
430
    zope.component.provideAdapter(select.CollectionSelectFieldWidget)
1✔
431
    zope.component.provideAdapter(select.CollectionChoiceSelectFieldWidget)
1✔
432
    # Adapter to  convert between field/internal and widget values
433
    zope.component.provideAdapter(converter.FieldDataConverter)
1✔
434
    zope.component.provideAdapter(converter.SequenceDataConverter)
1✔
435
    zope.component.provideAdapter(converter.CollectionSequenceDataConverter)
1✔
436
    zope.component.provideAdapter(converter.FieldWidgetDataConverter)
1✔
437
    # special data converter
438
    zope.component.provideAdapter(converter.IntegerDataConverter)
1✔
439
    zope.component.provideAdapter(converter.FloatDataConverter)
1✔
440
    zope.component.provideAdapter(converter.DecimalDataConverter)
1✔
441
    zope.component.provideAdapter(converter.DateDataConverter)
1✔
442
    zope.component.provideAdapter(converter.TimeDataConverter)
1✔
443
    zope.component.provideAdapter(converter.DatetimeDataConverter)
1✔
444
    zope.component.provideAdapter(converter.TimedeltaDataConverter)
1✔
445
    # Adapter for providing terms to radio list and other widgets
446
    zope.component.provideAdapter(term.BoolTerms)
1✔
447
    zope.component.provideAdapter(term.ChoiceTerms)
1✔
448
    zope.component.provideAdapter(term.ChoiceTermsVocabulary)
1✔
449
    zope.component.provideAdapter(term.ChoiceTermsSource)
1✔
450
    zope.component.provideAdapter(term.CollectionTerms)
1✔
451
    zope.component.provideAdapter(term.CollectionTermsVocabulary)
1✔
452
    zope.component.provideAdapter(term.CollectionTermsSource)
1✔
453
    # Adapter to create an action from a button
454
    zope.component.provideAdapter(
1✔
455
        button.ButtonAction, provides=interfaces.IButtonAction)
456
    # Adapter to use form.buttons to generate actions
457
    zope.component.provideAdapter(button.ButtonActions)
1✔
458
    # Adapter to use form.handlers to generate handle actions
459
    zope.component.provideAdapter(button.ButtonActionHandler)
1✔
460
    # Subscriber handling action execution error events
461
    zope.component.provideHandler(form.handleActionError)
1✔
462
    # Error View(s)
463
    zope.component.provideAdapter(error.ErrorViewSnippet)
1✔
464
    zope.component.provideAdapter(error.InvalidErrorViewSnippet)
1✔
465
    zope.component.provideAdapter(error.StandardErrorViewTemplate)
1✔
466

467

468
def tearDown(test):
1✔
469
    from zope.component import hooks
1✔
470
    from zope.testing import cleanup
1✔
471
    cleanup.cleanUp()
1✔
472
    hooks.resetHooks()
1✔
473
    hooks.setSite()
1✔
474

475

476
##########################
477
def render(view, xpath='.'):
1✔
478
    method = getattr(view, 'render', None)
1✔
479
    if method is None:
1!
480
        method = view.__call__
×
481

482
    string = method()
1✔
483
    if string == "":
1!
484
        return string
×
485

486
    try:
1✔
487
        root = lxml.etree.fromstring(string)
1✔
488
    except lxml.etree.XMLSyntaxError:
1✔
489
        root = lxml.html.fromstring(string)
1✔
490

491
    output = ""
1✔
492
    for node in root.xpath(
1✔
493
            xpath, namespaces={'xmlns': 'http://www.w3.org/1999/xhtml'}):
494
        s = lxml.etree.tounicode(node, pretty_print=True)
1✔
495
        s = s.replace(' xmlns="http://www.w3.org/1999/xhtml"', ' ')
1✔
496
        output += s
1✔
497

498
    if not output:
1!
499
        raise ValueError("No elements matched by %s." % repr(xpath))
×
500

501
    # let's get rid of blank lines
502
    output = output.replace('\n\n', '\n')
1✔
503

504
    # self-closing tags are more readable with a space before the
505
    # end-of-tag marker
506
    output = output.replace('"/>', '" />')
1✔
507

508
    return output
1✔
509

510

511
def textOfWithOptionalTitle(node, addTitle=False, showTooltips=False):
1✔
512
    if isinstance(node, (list, tuple)):
1!
513
        return '\n'.join(textOfWithOptionalTitle(child, addTitle, showTooltips)
×
514
                         for child in node)
515
    text = []
1✔
516
    if node is None:
1!
517
        return None
×
518

519
    if node.tag == 'br':
1!
520
        return '\n'
×
521
    if node.tag == 'input':
1✔
522
        if addTitle:
1!
523
            title = node.get('name') or ''
×
524
            title += ' '
×
525
        else:
526
            title = ''
1✔
527
        if node.get('type') == 'radio':
1✔
528
            return title + ('(O)' if node.get('checked') else '( )')
1✔
529
        if node.get('type') == 'checkbox':
1✔
530
            return title + ('[x]' if node.get('checked') else '[ ]')
1✔
531
        if node.get('type') == 'hidden':
1✔
532
            return ''
1✔
533
        else:
534
            return '{}[{}]'.format(title, node.get('value') or '')
1✔
535
    if node.tag == 'textarea':
1!
536
        if addTitle:
×
537
            title = node.get('name') or ''
×
538
            title += ' '
×
539
            text.append(title)
×
540
    if node.tag == 'select':
1✔
541
        if addTitle:
1!
542
            title = node.get('name') or ''
×
543
            title += ' '
×
544
        else:
545
            title = ''
1✔
546
        option = node.find('option[@selected]')
1✔
547
        return '{}[{}]'.format(
1✔
548
            title,
549
            option.text if option is not None else '[    ]')
550
    if node.tag == 'li':
1✔
551
        text.append('*')
1✔
552
    if node.tag == 'script':
1!
553
        return
×
554

555
    if node.text and node.text.strip():
1✔
556
        text.append(node.text.strip())
1✔
557

558
    for n, child in enumerate(node):
1✔
559
        s = textOfWithOptionalTitle(child, addTitle, showTooltips)
1✔
560
        if s:
1✔
561
            text.append(s)
1✔
562
        if child.tail and child.tail.strip():
1!
563
            text.append(child.tail)
×
564
    text = ' '.join(text).strip()
1✔
565
    # 'foo<br>bar' ends up as 'foo \nbar' due to the algorithm used above
566
    text = text.replace(' \n', '\n').replace('\n ', '\n').replace('\n\n', '\n')
1✔
567
    if '\xA0' in text:
1!
568
        # don't just .replace, that'll sprinkle my tests with u''
569
        text = text.replace('\xA0', ' ')  # nbsp -> space
×
570
    if node.tag == 'li':
1✔
571
        text += '\n'
1✔
572
    if node.tag == 'div':
1✔
573
        text += '\n'
1✔
574
    return text
1✔
575

576

577
def textOf(node):
1✔
578
    """Return the contents of an HTML node as text.
579

580
    Useful for functional tests, e.g. ::
581

582
        print map(textOf, browser.etree.xpath('.//td'))
583

584
    """
585
    return textOfWithOptionalTitle(node, False)
1✔
586

587

588
def plainText(content, xpath=None):
1✔
589
    root = lxml.html.fromstring(content)
1✔
590
    if xpath is not None:
1✔
591
        nodes = root.xpath(xpath)
1✔
592
        joinon = '\n'
1✔
593
    else:
594
        nodes = root
1✔
595
        joinon = ''
1✔
596
    text = joinon.join(map(textOf, nodes))
1✔
597
    lines = [line.strip() for line in text.splitlines()]
1✔
598
    text = '\n'.join(lines)
1✔
599
    return text
1✔
600

601

602
def getSubmitValues(content):
1✔
603
    root = lxml.html.fromstring(content)
1✔
604
    form = root.forms[0]
1✔
605
    values = dict(form.form_values())
1✔
606
    return values
1✔
607

608

609
def addTemplate(form, fname):
1✔
610
    form.template = BoundPageTemplate(
1✔
611
        ViewPageTemplateFile(
612
            fname, os.path.dirname(tests.__file__)), form)
613

614

615
def saveHtml(content, fname):
1✔
616
    path = os.path.join(os.getcwd(), 'var', 'htmls')
1✔
617
    if not os.path.exists(path):
1✔
618
        os.makedirs(path)
1✔
619
    fullfname = os.path.join(path, fname)
1✔
620
    with open(fullfname, 'wb') as fout:
1✔
621
        fout.write(content.encode('utf8'))
1✔
622

623
    fullfname = os.path.join(path, 'integration.css')
1✔
624
    cssfname = os.path.join(os.path.dirname(tests.__file__),
1✔
625
                            'integration.css')
626
    shutil.copy(cssfname, fullfname)
1✔
627

628

629
##########################################
630
# integration test interfaces and classes
631

632
class IntegrationBase:
1✔
633
    def __init__(self, **kw):
1✔
634
        for k, v in kw.items():
1✔
635
            setattr(self, k, v)
1✔
636

637
    def __repr__(self):
1✔
638
        items = sorted(self.__dict__.items())
1✔
639
        return ("<" + self.__class__.__name__ + "\n  "
1✔
640
                + "\n  ".join([f"{key}: {pprint.pformat(value)}"
641
                               for key, value in items]) + ">")
642

643

644
class IObjectWidgetSingleSubIntegration(zope.interface.Interface):
1✔
645
    singleInt = zope.schema.Int(
1✔
646
        title='Int label')
647
    singleBool = zope.schema.Bool(
1✔
648
        title='Bool label',
649
        required=True)
650
    singleChoice = zope.schema.Choice(
1✔
651
        title='Choice label',
652
        values=('one', 'two', 'three'))
653
    singleChoiceOpt = zope.schema.Choice(
1✔
654
        title='ChoiceOpt label',
655
        values=('four', 'five', 'six'),
656
        required=False)
657
    singleTextLine = zope.schema.TextLine(
1✔
658
        title='TextLine label')
659
    singleDate = zope.schema.Date(
1✔
660
        title='Date label')
661
    singleReadOnly = zope.schema.TextLine(
1✔
662
        title='ReadOnly label',
663
        readonly=True)
664

665

666
@zope.interface.implementer(IObjectWidgetSingleSubIntegration)
1✔
667
class ObjectWidgetSingleSubIntegration(IntegrationBase):
1✔
668

669
    singleInt = FieldProperty(IObjectWidgetSingleSubIntegration['singleInt'])
1✔
670
    singleBool = FieldProperty(IObjectWidgetSingleSubIntegration['singleBool'])
1✔
671
    singleChoice = FieldProperty(
1✔
672
        IObjectWidgetSingleSubIntegration['singleChoice'])
673
    singleChoiceOpt = FieldProperty(
1✔
674
        IObjectWidgetSingleSubIntegration['singleChoiceOpt'])
675
    singleTextLine = FieldProperty(
1✔
676
        IObjectWidgetSingleSubIntegration['singleTextLine'])
677
    singleDate = FieldProperty(IObjectWidgetSingleSubIntegration['singleDate'])
1✔
678
    singleReadOnly = FieldProperty(
1✔
679
        IObjectWidgetSingleSubIntegration['singleReadOnly'])
680

681

682
class IObjectWidgetSingleIntegration(zope.interface.Interface):
1✔
683
    subobj = zope.schema.Object(
1✔
684
        title='Object label',
685
        schema=IObjectWidgetSingleSubIntegration
686
    )
687

688

689
@zope.interface.implementer(IObjectWidgetSingleIntegration)
1✔
690
class ObjectWidgetSingleIntegration:
1✔
691

692
    subobj = FieldProperty(IObjectWidgetSingleIntegration['subobj'])
1✔
693

694

695
class IObjectWidgetMultiSubIntegration(zope.interface.Interface):
1✔
696
    multiInt = zope.schema.Int(
1✔
697
        title='Int label')
698
    multiBool = zope.schema.Bool(
1✔
699
        title='Bool label',
700
        required=True)
701
    multiChoice = zope.schema.Choice(
1✔
702
        title='Choice label',
703
        values=('one', 'two', 'three'))
704
    multiChoiceOpt = zope.schema.Choice(
1✔
705
        title='ChoiceOpt label',
706
        values=('four', 'five', 'six'),
707
        required=False)
708
    multiTextLine = zope.schema.TextLine(
1✔
709
        title='TextLine label')
710
    multiDate = zope.schema.Date(
1✔
711
        title='Date label')
712

713

714
@zope.interface.implementer(IObjectWidgetMultiSubIntegration)
1✔
715
class ObjectWidgetMultiSubIntegration(IntegrationBase):
1✔
716

717
    multiInt = FieldProperty(IObjectWidgetMultiSubIntegration['multiInt'])
1✔
718
    multiBool = FieldProperty(IObjectWidgetMultiSubIntegration['multiBool'])
1✔
719
    multiChoice = FieldProperty(
1✔
720
        IObjectWidgetMultiSubIntegration['multiChoice'])
721
    multiChoiceOpt = FieldProperty(
1✔
722
        IObjectWidgetMultiSubIntegration['multiChoiceOpt'])
723
    multiTextLine = FieldProperty(
1✔
724
        IObjectWidgetMultiSubIntegration['multiTextLine'])
725
    multiDate = FieldProperty(IObjectWidgetMultiSubIntegration['multiDate'])
1✔
726

727

728
class IObjectWidgetMultiIntegration(zope.interface.Interface):
1✔
729
    subobj = zope.schema.Object(
1✔
730
        title='Object label',
731
        schema=IObjectWidgetMultiSubIntegration
732
    )
733

734

735
@zope.interface.implementer(IObjectWidgetMultiIntegration)
1✔
736
class ObjectWidgetMultiIntegration:
1✔
737

738
    subobj = FieldProperty(IObjectWidgetMultiIntegration['subobj'])
1✔
739

740

741
class IMultiWidgetListIntegration(zope.interface.Interface):
1✔
742
    listOfInt = zope.schema.List(
1✔
743
        title="ListOfInt label",
744
        value_type=zope.schema.Int(
745
            title='Int label'),
746
    )
747
    listOfBool = zope.schema.List(
1✔
748
        title="ListOfBool label",
749
        value_type=zope.schema.Bool(
750
            title='Bool label',
751
            required=True),
752
    )
753
    listOfChoice = zope.schema.List(
1✔
754
        title="ListOfChoice label",
755
        value_type=zope.schema.Choice(
756
            title='Choice label',
757
            values=('one', 'two', 'three')
758
        ),
759
    )
760
    listOfTextLine = zope.schema.List(
1✔
761
        title="ListOfTextLine label",
762
        value_type=zope.schema.TextLine(
763
            title='TextLine label'),
764
    )
765
    listOfDate = zope.schema.List(
1✔
766
        title="ListOfDate label",
767
        value_type=zope.schema.Date(
768
            title='Date label'),
769
    )
770
    listOfObject = zope.schema.List(
1✔
771
        title="ListOfObject label",
772
        value_type=zope.schema.Object(
773
            title='Object label',
774
            schema=IObjectWidgetMultiSubIntegration),
775
    )
776

777

778
@zope.interface.implementer(IMultiWidgetListIntegration)
1✔
779
class MultiWidgetListIntegration(IntegrationBase):
1✔
780

781
    listOfInt = FieldProperty(IMultiWidgetListIntegration['listOfInt'])
1✔
782
    listOfBool = FieldProperty(IMultiWidgetListIntegration['listOfBool'])
1✔
783
    listOfChoice = FieldProperty(IMultiWidgetListIntegration['listOfChoice'])
1✔
784
    listOfTextLine = FieldProperty(
1✔
785
        IMultiWidgetListIntegration['listOfTextLine'])
786
    listOfDate = FieldProperty(IMultiWidgetListIntegration['listOfDate'])
1✔
787
    listOfObject = FieldProperty(IMultiWidgetListIntegration['listOfObject'])
1✔
788

789

790
class IMultiWidgetDictIntegration(zope.interface.Interface):
1✔
791
    dictOfInt = zope.schema.Dict(
1✔
792
        title="DictOfInt label",
793
        key_type=zope.schema.Int(
794
            title='Int key'),
795
        value_type=zope.schema.Int(
796
            title='Int label'),
797
    )
798
    dictOfBool = zope.schema.Dict(
1✔
799
        title="DictOfBool label",
800
        key_type=zope.schema.Bool(
801
            title='Bool key',
802
            required=True),
803
        value_type=zope.schema.Bool(
804
            title='Bool label',
805
            required=True),
806
    )
807
    dictOfChoice = zope.schema.Dict(
1✔
808
        title="DictOfChoice label",
809
        key_type=zope.schema.Choice(
810
            title='Choice key',
811
            values=('key1', 'key2', 'key3')
812
        ),
813
        value_type=zope.schema.Choice(
814
            title='Choice label',
815
            values=('one', 'two', 'three')
816
        ),
817
    )
818
    dictOfTextLine = zope.schema.Dict(
1✔
819
        title="DictOfTextLine label",
820
        key_type=zope.schema.TextLine(
821
            title='TextLine key'),
822
        value_type=zope.schema.TextLine(
823
            title='TextLine label'),
824
    )
825
    dictOfDate = zope.schema.Dict(
1✔
826
        title="DictOfDate label",
827
        key_type=zope.schema.Date(
828
            title='Date key'),
829
        value_type=zope.schema.Date(
830
            title='Date label'),
831
    )
832
    dictOfObject = zope.schema.Dict(
1✔
833
        title="DictOfObject label",
834
        key_type=zope.schema.TextLine(
835
            title='Object key'),
836
        value_type=zope.schema.Object(
837
            title='Object label',
838
            schema=IObjectWidgetMultiSubIntegration),
839
    )
840

841

842
@zope.interface.implementer(IMultiWidgetDictIntegration)
1✔
843
class MultiWidgetDictIntegration(IntegrationBase):
1✔
844

845
    dictOfInt = FieldProperty(IMultiWidgetDictIntegration['dictOfInt'])
1✔
846
    dictOfBool = FieldProperty(IMultiWidgetDictIntegration['dictOfBool'])
1✔
847
    dictOfChoice = FieldProperty(IMultiWidgetDictIntegration['dictOfChoice'])
1✔
848
    dictOfTextLine = FieldProperty(
1✔
849
        IMultiWidgetDictIntegration['dictOfTextLine'])
850
    dictOfDate = FieldProperty(IMultiWidgetDictIntegration['dictOfDate'])
1✔
851
    dictOfObject = FieldProperty(IMultiWidgetDictIntegration['dictOfObject'])
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc