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

zopefoundation / zope.formlib / 5037509110

pending completion
5037509110

push

github

Michael Howitz
Fix Manifest + isort

822 of 953 branches covered (86.25%)

Branch coverage included in aggregate %.

5126 of 5380 relevant lines covered (95.28%)

0.95 hits per line

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

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

14
import unittest
1✔
15

16
import pytz
1✔
17

18
import zope.interface.common.idatetime
1✔
19
import zope.publisher.interfaces
1✔
20
import zope.publisher.interfaces.browser
1✔
21
import zope.schema.interfaces
1✔
22
import zope.testing.renormalizing
1✔
23
import zope.traversing.adapters
1✔
24
from zope.browserpage.namedtemplate import NamedTemplateImplementation
1✔
25
from zope.component import adapter
1✔
26
from zope.component import provideAdapter
1✔
27
from zope.component import provideUtility
1✔
28
from zope.component.testing import setUp
1✔
29
from zope.component.testing import tearDown
1✔
30
from zope.configuration.xmlconfig import XMLConfig
1✔
31
from zope.i18n.testing import setUp as i18nSetUp
1✔
32

33
import zope.formlib
1✔
34
import zope.formlib.form
1✔
35
import zope.formlib.interfaces
1✔
36
from zope.formlib import exception
1✔
37
from zope.formlib.interfaces import IWidgetInputErrorView
1✔
38
from zope.formlib.widgets import DatetimeDisplayWidget
1✔
39
from zope.formlib.widgets import DatetimeWidget
1✔
40
from zope.formlib.widgets import FloatWidget
1✔
41
from zope.formlib.widgets import IntWidget
1✔
42
from zope.formlib.widgets import TextWidget
1✔
43
from zope.formlib.widgets import UnicodeDisplayWidget
1✔
44

45

46
@zope.interface.implementer(zope.interface.common.idatetime.ITZInfo)
1✔
47
@adapter(zope.publisher.interfaces.IRequest)
1✔
48
def requestToTZInfo(request):
1✔
49
    return pytz.timezone('US/Hawaii')
1✔
50

51

52
@adapter(zope.formlib.interfaces.IForm)
1✔
53
@NamedTemplateImplementation
1✔
54
def TestTemplate(self):
1✔
55
    # This "template" overrides the default page templates that is
56
    # registered for forms.
57
    status = self.status
1✔
58
    if status:
1✔
59
        status = zope.i18n.translate(status,
1✔
60
                                     context=self.request,
61
                                     default=self.status)
62
        if getattr(status, 'mapping', 0):
1!
63
            status = zope.i18n.interpolate(status, status.mapping)
×
64
        print(status)
1✔
65

66
    result = []
1✔
67

68
    if self.errors:
1✔
69
        for error in self.errors:
1✔
70
            result.append("{}: {}".format(error.__class__.__name__, error))
1✔
71

72
    for w in self.widgets:
1✔
73
        result.append(w())
1✔
74
        error = w.error()
1✔
75
        if error:
1!
76
            result.append(str(error))
×
77

78
    if self.protected:
1✔
79
        result.append(
1✔
80
            '<inut type="hidden" '
81
            'name="__csrftoken__" '
82
            'value="%s"' % self.csrftoken)
83

84
    for action in self.availableActions():
1✔
85
        result.append(action.render())
1✔
86

87
    return '\n'.join(result)
1✔
88

89

90
def formSetUp(test):
1✔
91
    setUp(test)
1✔
92
    i18nSetUp(test)
1✔
93
    provideAdapter(
1✔
94
        TextWidget,
95
        [zope.schema.interfaces.ITextLine,
96
         zope.publisher.interfaces.browser.IBrowserRequest,
97
         ],
98
        zope.formlib.interfaces.IInputWidget,
99
    )
100
    provideAdapter(
1✔
101
        FloatWidget,
102
        [zope.schema.interfaces.IFloat,
103
         zope.publisher.interfaces.browser.IBrowserRequest,
104
         ],
105
        zope.formlib.interfaces.IInputWidget,
106
    )
107
    provideAdapter(
1✔
108
        UnicodeDisplayWidget,
109
        [zope.schema.interfaces.IInt,
110
         zope.publisher.interfaces.browser.IBrowserRequest,
111
         ],
112
        zope.formlib.interfaces.IDisplayWidget,
113
    )
114
    provideAdapter(
1✔
115
        IntWidget,
116
        [zope.schema.interfaces.IInt,
117
         zope.publisher.interfaces.browser.IBrowserRequest,
118
         ],
119
        zope.formlib.interfaces.IInputWidget,
120
    )
121
    provideAdapter(
1✔
122
        UnicodeDisplayWidget,
123
        [zope.schema.interfaces.IFloat,
124
         zope.publisher.interfaces.browser.IBrowserRequest,
125
         ],
126
        zope.formlib.interfaces.IDisplayWidget,
127
    )
128
    provideAdapter(
1✔
129
        UnicodeDisplayWidget,
130
        [zope.schema.interfaces.ITextLine,
131
         zope.publisher.interfaces.browser.IBrowserRequest,
132
         ],
133
        zope.formlib.interfaces.IDisplayWidget,
134
    )
135
    provideAdapter(
1✔
136
        DatetimeDisplayWidget,
137
        [zope.schema.interfaces.IDatetime,
138
         zope.publisher.interfaces.browser.IBrowserRequest,
139
         ],
140
        zope.formlib.interfaces.IDisplayWidget,
141
    )
142
    provideAdapter(
1✔
143
        DatetimeWidget,
144
        [zope.schema.interfaces.IDatetime,
145
         zope.publisher.interfaces.browser.IBrowserRequest,
146
         ],
147
        zope.formlib.interfaces.IInputWidget,
148
    )
149
    provideAdapter(
1✔
150
        exception.WidgetInputErrorView,
151
        [zope.formlib.interfaces.IWidgetInputError,
152
         zope.publisher.interfaces.browser.IBrowserRequest,
153
         ],
154
        IWidgetInputErrorView,
155
    )
156
    provideAdapter(
1✔
157
        zope.formlib.errors.InvalidErrorView,
158
        [zope.interface.Invalid,
159
         zope.publisher.interfaces.browser.IBrowserRequest,
160
         ],
161
        IWidgetInputErrorView,
162
    )
163
    provideAdapter(TestTemplate, name='default')
1✔
164
    provideAdapter(requestToTZInfo)
1✔
165
    provideAdapter(
1✔
166
        zope.formlib.form.render_submit_button, name='render')
167

168
    XMLConfig('ftesting.zcml', zope.formlib)
1✔
169

170
# Classes used in tests
171

172

173
class IOrder(zope.interface.Interface):
1✔
174
    identifier = zope.schema.Int(title="Identifier", readonly=True)
1✔
175
    name = zope.schema.TextLine(title="Name")
1✔
176
    min_size = zope.schema.Float(title="Minimum size")
1✔
177
    max_size = zope.schema.Float(title="Maximum size")
1✔
178
    now = zope.schema.Datetime(title="Now", readonly=True)
1✔
179

180

181
class IDescriptive(zope.interface.Interface):
1✔
182
    title = zope.schema.TextLine(title="Title")
1✔
183
    description = zope.schema.TextLine(title="Description")
1✔
184

185

186
@zope.interface.implementer(IOrder)
1✔
187
class Order:
1✔
188
    identifier = 1
1✔
189
    name = 'unknown'
1✔
190
    min_size = 1.0
1✔
191
    max_size = 10.0
1✔
192

193

194
@adapter(IOrder)
1✔
195
@zope.interface.implementer(IDescriptive)
1✔
196
class Descriptive:
1✔
197
    def __init__(self, context):
1✔
198
        self.context = context
×
199

200
    def title():
1✔
201
        def get(self):
1✔
202
            try:
×
203
                return self.context.__title
×
204
            except AttributeError:
×
205
                return ''
×
206

207
        def set(self, v):
1✔
208
            self.context.__title = v
×
209
        return property(get, set)
1✔
210
    title = title()
1✔
211

212
    def description():
1✔
213
        def get(self):
1✔
214
            try:
×
215
                return self.context.__description
×
216
            except AttributeError:
×
217
                return ''
×
218

219
        def set(self, v):
1✔
220
            self.context.__description = v
×
221
        return property(get, set)
1✔
222
    description = description()
1✔
223

224

225
def makeSureRenderCanBeCalledWithoutCallingUpdate():
1✔
226
    """\
227

228
    >>> class MyForm(zope.formlib.form.EditForm):
229
    ...     form_fields = zope.formlib.form.fields(
230
    ...         IOrder, keep_readonly=['identifier'])
231

232
    >>> from zope.publisher.browser import TestRequest
233
    >>> myform = MyForm(Order(), TestRequest())
234
    >>> print(myform.render()) # doctest: +NORMALIZE_WHITESPACE
235
    1
236
    <input class="textType" id="form.name" name="form.name"
237
           size="20" type="text" value="unknown"  />
238
    <input class="textType" id="form.min_size" name="form.min_size"
239
           size="10" type="text" value="1.0"  />
240
    <input class="textType" id="form.max_size" name="form.max_size"
241
           size="10" type="text" value="10.0"  />
242
    <input type="submit" id="form.actions.apply" name="form.actions.apply"
243
           value="Apply" class="button" />
244

245
"""
246

247

248
def make_sure_i18n_is_called_correctly_for_actions():
1✔
249
    """\
250

251
We want to make sure that i18n is called correctly.  This is in
252
response to a bug that occurred because actions called i18n.translate
253
with incorrect positional arguments.
254

255
We'll start by setting up an action:
256

257
    >>> import zope.i18nmessageid
258
    >>> _ = zope.i18nmessageid.MessageFactory('my.domain')
259
    >>> action = zope.formlib.form.Action(_("MyAction"))
260

261
Actions get bound to forms.  We'll set up a test request, create a
262
form for it and bind the action to the form:
263

264
    >>> myform = zope.formlib.form.FormBase(None, 42)
265
    >>> action = action.__get__(myform)
266

267
Button labels are rendered by form.render_submit_button, passing the
268
bound action.  Before we call this however, we need to set up a dummy
269
translation domain.  We'll create one for our needs:
270

271
    >>> import zope.i18n.interfaces
272
    >>> @zope.interface.implementer(zope.i18n.interfaces.ITranslationDomain)
273
    ... class MyDomain:
274
    ...
275
    ...     def translate(self, msgid, mapping=None, context=None,
276
    ...                   target_language=None, default=None,
277
    ...                   msgid_plural=None, default_plural=None, number=None):
278
    ...         print(msgid)
279
    ...         print(mapping)
280
    ...         print(context)
281
    ...         print(target_language)
282
    ...         print(default)
283
    ...         return msgid
284

285
    >>> provideUtility(MyDomain(), name='my.domain')
286

287
Now, if we call render_submit_button, we should be able to verify the
288
data passed to translate:
289

290
    >>> print(zope.formlib.form.render_submit_button(action)())
291
    ...     # doctest: +NORMALIZE_WHITESPACE
292
    MyAction
293
    None
294
    42
295
    None
296
    MyAction
297
    <input type="submit" id="form.actions.myaction"
298
       name="form.actions.myaction" value="MyAction" class="button" />
299

300

301
"""
302

303

304
def test_error_handling():
1✔
305
    """\
306

307
Let's test the getWidgetsData method which is responsible for handling widget
308
erros raised by the widgets getInputValue method.
309

310
    >>> import zope.formlib.interfaces
311
    >>> @zope.interface.implementer(zope.formlib.interfaces.IInputWidget)
312
    ... class Widget(object):
313
    ...     def __init__(self):
314
    ...         self.name = 'form.summary'
315
    ...         self.label = 'Summary'
316
    ...     def hasInput(self):
317
    ...         return True
318
    ...     def getInputValue(self):
319
    ...         raise zope.formlib.interfaces.WidgetInputError(
320
    ...         field_name='summary',
321
    ...         widget_title='Summary')
322
    >>> widget = Widget()
323
    >>> inputs = [(True, widget)]
324
    >>> widgets = zope.formlib.form.Widgets(inputs, 5)
325
    >>> errors = zope.formlib.form.getWidgetsData(
326
    ...     widgets, 'form', {'summary':'value'})
327
    >>> print(errors[0].__class__.__name__, errors[0])
328
    WidgetInputError ('summary', 'Summary', None)
329

330
Let's see what happens if a widget doesn't convert a ValidationError
331
raised by a field to a WidgetInputError. This should not happen if a widget
332
converts ValidationErrors to WidgetInputErrors. But since I just fixed
333
yesterday the sequence input widget, I decided to catch ValidationError also
334
in the formlib as a fallback if some widget doen't handle errors correct. (ri)
335

336
    >>> @zope.interface.implementer(zope.formlib.interfaces.IInputWidget)
337
    ... class Widget(object):
338
    ...     def __init__(self):
339
    ...         self.name = 'form.summary'
340
    ...         self.label = 'summary'
341
    ...     def hasInput(self):
342
    ...         return True
343
    ...     def getInputValue(self):
344
    ...         raise zope.schema.interfaces.ValidationError('A error message')
345
    >>> widget = Widget()
346
    >>> inputs = [(True, widget)]
347
    >>> widgets = zope.formlib.form.Widgets(inputs, 5)
348
    >>> errors = zope.formlib.form.getWidgetsData(
349
    ...     widgets, 'form', {'summary':'value'})
350
    >>> errors #doctest: +ELLIPSIS
351
    [WidgetInputError(...))]
352

353
"""
354

355

356
def test_form_template_i18n():
1✔
357
    """\
358
Let's try to check that the formlib templates handle i18n correctly.
359
We'll define a simple form:
360

361
    >>> from zope.browserpage import ViewPageTemplateFile
362
    >>> import zope.i18nmessageid
363
    >>> _ = zope.i18nmessageid.MessageFactory('my.domain')
364

365
    >>> class MyForm(zope.formlib.form.Form):
366
    ...     label = _('The label')
367
    ...     status = _('Success!')
368
    ...     form_fields = zope.formlib.form.Fields(
369
    ...         zope.schema.TextLine(__name__='name',
370
    ...                              title=_("Name"),
371
    ...                              description=_("Enter your name"),
372
    ...                             ),
373
    ...         )
374
    ...     @zope.formlib.form.action(_('Ok'))
375
    ...     def ok(self, action, data):
376
    ...         pass
377
    ...     page = ViewPageTemplateFile("../pageform.pt")
378
    ...     subpage = ViewPageTemplateFile("../subpageform.pt")
379

380
Now, we should be able to create a form instance:
381

382
    >>> from zope.publisher.browser import TestRequest
383
    >>> request = TestRequest()
384
    >>> form = MyForm(object(), request)
385

386
Unfortunately, the "page" template uses a page macro. We need to
387
provide a template that it can get one from.  Here, we'll set up a
388
view that provides the necessary macros:
389

390
    >>> from zope.pagetemplate.pagetemplate import PageTemplate
391
    >>> macro_template = PageTemplate()
392
    >>> macro_template.write('''\
393
    ... <html metal:define-macro="view">
394
    ... <body metal:define-slot="body" />
395
    ... </html>
396
    ... ''')
397

398
We also need to provide a traversal adapter for the view namespace
399
that lets us look up the macros.
400

401
    >>> import zope.traversing.interfaces
402
    >>> @adapter(None, None)
403
    ... @zope.interface.implementer(zope.traversing.interfaces.ITraversable)
404
    ... class view:
405
    ...     def __init__(self, ob, r=None):
406
    ...         pass
407
    ...     def traverse(*args):
408
    ...         return macro_template.macros
409

410
    >>> provideAdapter(view, name='view')
411

412
And we have to register the default traversable adapter (I wish we had
413
push templates):
414

415
    >>> from zope.traversing.adapters import DefaultTraversable
416
    >>> provideAdapter(DefaultTraversable, [None])
417

418
We need to set up the translation framework. We'll just provide a
419
negotiator that always decides to use the test language:
420

421
    >>> import zope.i18n.interfaces
422
    >>> @zope.interface.implementer(zope.i18n.interfaces.INegotiator)
423
    ... class Negotiator:
424
    ...     def getLanguage(*ignored):
425
    ...         return 'test'
426

427
    >>> provideUtility(Negotiator())
428

429
And we'll set up the fallback-domain factory, which provides the test
430
language for all domains:
431

432
    >>> from zope.i18n.testmessagecatalog import TestMessageFallbackDomain
433
    >>> provideUtility(TestMessageFallbackDomain)
434

435
OK, so let's see what the page form looks like. First, we'll compute
436
the page:
437

438
    >>> form.update()
439
    >>> page = form.page()
440

441
We want to make sure that the page has the translations we expect and
442
that it doesn't double translate anything.  We'll write a generator
443
that extracts the translations, complaining if any are nested:
444

445
    >>> def find_translations(text):
446
    ...     l = 0
447
    ...     while 1:
448
    ...         lopen = text.find('[[', l)
449
    ...         lclose = text.find(']]', l)
450
    ...         if lclose >= 0 and lclose < lopen:
451
    ...             raise ValueError(lopen, lclose, text)
452
    ...         if lopen < 0:
453
    ...             break
454
    ...         l = lopen + 2
455
    ...         lopen = text.find('[[', l)
456
    ...         lclose = text.find(']]', l)
457
    ...         if lopen >= 0 and lopen < lclose:
458
    ...             raise ValueError(lopen, lclose, text)
459
    ...         if lclose < 0:
460
    ...             raise ValueError(l, text)
461
    ...         yield text[l-2:lclose+2]
462
    ...         l = lclose + 2
463

464
    >>> for t in find_translations(page):
465
    ...     print(t)
466
    [[my.domain][The label]]
467
    [[my.domain][Success!]]
468
    [[my.domain][Name]]
469
    [[my.domain][Enter your name]]
470
    [[my.domain][Ok]]
471

472
Now, let's try the same thing with the sub-page form:
473

474
    >>> for t in find_translations(form.subpage()):
475
    ...     print(t)
476
    [[my.domain][The label]]
477
    [[my.domain][Success!]]
478
    [[my.domain][Name]]
479
    [[my.domain][Enter your name]]
480
    [[my.domain][Ok]]
481

482
"""
483

484

485
def test_setUpWidgets_prefix():
1✔
486
    """This is a regression test for field prefix handling in setUp*Widgets.
487

488
    Let's set up fields with some interface and a prefix on fields:
489

490
        >>> from zope.formlib import form
491
        >>> from zope import interface, schema
492

493
        >>> class ITrivial(interface.Interface):
494
        ...     name = schema.TextLine(title=u"Name")
495

496
        >>> form_fields = form.Fields(ITrivial, prefix='one')
497
        >>> form_fields += form.Fields(ITrivial, prefix='two')
498
        >>> form_fields += form.Fields(ITrivial, prefix='three')
499

500
    Let's call setUpDataWidgets and see their names:
501

502
        >>> @interface.implementer(ITrivial)
503
        ... class Trivial(object):
504
        ...     name = 'foo'
505
        >>> context = Trivial()
506

507
        >>> from zope.publisher.browser import TestRequest
508
        >>> request = TestRequest()
509

510
        >>> widgets = form.setUpDataWidgets(form_fields, 'form', context,
511
        ...                                 request, {})
512
        >>> [w.name for w in widgets]
513
        ['form.one.name', 'form.two.name', 'form.three.name']
514

515
    Let's try the same with setUpEditWidgets:
516

517
        >>> widgets = form.setUpEditWidgets(form_fields, 'form', context,
518
        ...                                  request)
519
        >>> [w.name for w in widgets]
520
        ['form.one.name', 'form.two.name', 'form.three.name']
521

522
    And setUpInputWidgets:
523

524
        >>> widgets = form.setUpInputWidgets(form_fields, 'form', context,
525
        ...                                  request)
526
        >>> [w.name for w in widgets]
527
        ['form.one.name', 'form.two.name', 'form.three.name']
528

529
    And setUpWidgets:
530

531
        >>> widgets = form.setUpWidgets(form_fields, 'form', context, request)
532
        >>> [w.name for w in widgets]
533
        ['form.one.name', 'form.two.name', 'form.three.name']
534

535
    """
536

537

538
def check_action_name():
1✔
539
    """
540
We want to make sure that Action name setting adheres to the specification.
541

542
With just label, with increasing complexity:
543

544
    >>> action = zope.formlib.form.Action("MyAction")
545
    >>> action.name
546
    'myaction'
547

548
    >>> action = zope.formlib.form.Action("8 Balls")
549
    >>> action.name
550
    '382042616c6c73'
551

552
    >>> action = zope.formlib.form.Action(u"MyAction")
553
    >>> action.name
554
    'myaction'
555

556
    >>> action = zope.formlib.form.Action(u"8 Balls")
557
    >>> action.name
558
    '382042616c6c73'
559

560
    >>> action = zope.formlib.form.Action('\u9001\u4fe1')
561
    >>> action.name
562
    'e98081e4bfa1'
563

564
    >>> import zope.i18nmessageid
565
    >>> _ = zope.i18nmessageid.MessageFactory('my.domain')
566

567
    >>> action = zope.formlib.form.Action(_(u"MyAction"))
568
    >>> action.name
569
    'myaction'
570

571
    >>> action = zope.formlib.form.Action(_(u"8 Balls"))
572
    >>> action.name
573
    '382042616c6c73'
574

575
    >>> action = zope.formlib.form.Action(_('\u9001\u4fe1'))
576
    >>> action.name
577
    'e98081e4bfa1'
578

579
With all lowercase name:
580

581
    >>> action = zope.formlib.form.Action("MyAction", name='foobar')
582
    >>> action.name
583
    'foobar'
584

585
    >>> action = zope.formlib.form.Action("8 Balls", name='foobar')
586
    >>> action.name
587
    'foobar'
588

589
    >>> action = zope.formlib.form.Action(u"MyAction", name='foobar')
590
    >>> action.name
591
    'foobar'
592

593
    >>> action = zope.formlib.form.Action(u"8 Balls", name='foobar')
594
    >>> action.name
595
    'foobar'
596

597
    >>> action = zope.formlib.form.Action('\u9001\u4fe1', name='foobar')
598
    >>> action.name
599
    'foobar'
600

601
    >>> action = zope.formlib.form.Action(_(u"MyAction"), name='foobar')
602
    >>> action.name
603
    'foobar'
604

605
    >>> action = zope.formlib.form.Action(_(u"8 Balls"), name='foobar')
606
    >>> action.name
607
    'foobar'
608

609
    >>> action = zope.formlib.form.Action(_('\u9001\u4fe1'), name='foobar')
610
    >>> action.name
611
    'foobar'
612

613
With some uppercase name:
614

615
    >>> action = zope.formlib.form.Action("MyAction", name='FooBar')
616
    >>> action.name
617
    'FooBar'
618

619
    >>> action = zope.formlib.form.Action("8 Balls", name='FooBar')
620
    >>> action.name
621
    'FooBar'
622

623
    >>> action = zope.formlib.form.Action(u"MyAction", name='FooBar')
624
    >>> action.name
625
    'FooBar'
626

627
    >>> action = zope.formlib.form.Action(u"8 Balls", name='FooBar')
628
    >>> action.name
629
    'FooBar'
630

631
    >>> action = zope.formlib.form.Action('\u9001\u4fe1', name='FooBar')
632
    >>> action.name
633
    'FooBar'
634

635
    >>> action = zope.formlib.form.Action(_(u"MyAction"), name='FooBar')
636
    >>> action.name
637
    'FooBar'
638

639
    >>> action = zope.formlib.form.Action(_(u"8 Balls"), name='FooBar')
640
    >>> action.name
641
    'FooBar'
642

643
    >>> action = zope.formlib.form.Action(_('\u9001\u4fe1'), name='FooBar')
644
    >>> action.name
645
    'FooBar'
646

647
"""
648

649

650
def validate_respects_ignoreContext_setting_on_form_when_checking_invariants():
1✔
651
    """
652
The `validate` method of the form respects the value of
653
``self.ignoreContext`` when calling `checkInvariants`. `checkInvariants` is
654
able to access the values from the form and the context (if not ignored) to
655
make sure invariants are not violated:
656

657
    >>> class IFlexMaximum(zope.interface.Interface):
658
    ...     max = zope.schema.Int(title=u"Maximum")
659
    ...     value = zope.schema.Int(title=u"Value")
660
    ...
661
    ...     @zope.interface.invariant
662
    ...     def value_not_bigger_than_max(data):
663
    ...         if data.value > data.max:
664
    ...             raise zope.interface.Invalid('value bigger than max')
665

666
    >>> @zope.interface.implementer(IFlexMaximum)
667
    ... class Content(object):
668
    ...     max = 10
669
    ...     value = 7
670

671
    >>> class ValueForm(zope.formlib.form.FormBase):
672
    ...     ignoreContext = False
673
    ...     form_fields = zope.formlib.form.FormFields(
674
    ...         IFlexMaximum).omit('max')
675
    ...
676
    ...     @zope.formlib.form.action("Apply")
677
    ...     def handle_apply(self, action, data):
678
    ...         pass
679

680
`checkInvariants` is able to access the value on the context, so the
681
interface invariant triggers an error message:
682

683
    >>> from zope.publisher.browser import TestRequest
684
    >>> request = TestRequest(
685
    ...     form={'form.value': 42, 'form.actions.apply': '1'})
686
    >>> form = ValueForm(Content(), request)
687
    >>> form.update()
688
    >>> form.errors
689
    (Invalid('value bigger than max'),)
690

691
`checkInvariants` if the entered value is small enough, the error message is
692
not triggered:
693

694
    >>> from zope.publisher.browser import TestRequest
695
    >>> request = TestRequest(
696
    ...     form={'form.value': 8, 'form.actions.apply': '1'})
697
    >>> form = ValueForm(Content(), request)
698
    >>> form.update()
699
    >>> form.errors
700
    ()
701

702
The error is not triggered, too,  if ``ignoreContext`` is set to ``True`` as
703
`checkInvariants` does not access the context then:
704

705
    >>> request = TestRequest(
706
    ...     form={'form.value': 42, 'form.actions.apply': '1'})
707
    >>> form = ValueForm(Content(), request)
708
    >>> form.ignoreContext = True
709
    >>> form.update()
710
    >>> form.errors
711
    ()
712
"""
713

714

715
def FormData___getattr___handles_zope_interrface_attributes_correctly():
1✔
716
    """
717
`FormData.__getattr__` reads objects defined as zope.interface.Attribute in
718
interface correctly from context:
719

720
    >>> class IStaticMaximum(zope.interface.Interface):
721
    ...     max = zope.interface.Attribute("Predefined maximum")
722

723
    >>> @zope.interface.implementer(IStaticMaximum)
724
    ... class Content(object):
725
    ...     max = 10
726

727
    >>> formdata = zope.formlib.form.FormData(IStaticMaximum, {}, Content())
728
    >>> formdata.max
729
    10
730
"""
731

732

733
def FormData___getattr___raises_NoInputData_if_unknown_how_to_access_value():
1✔
734
    """
735
`FormData.__getattr__` raises an exception if it cannot determine how to
736
read the object from context:
737

738
    >>> class IStaticMaximum(zope.interface.Interface):
739
    ...     def max(): pass
740

741
    >>> @zope.interface.implementer(IStaticMaximum)
742
    ... class Content(object):
743
    ...     pass
744

745
    >>> formdata = zope.formlib.form.FormData(IStaticMaximum, {}, Content())
746
    >>> formdata.max #doctest: +IGNORE_EXCEPTION_DETAIL
747
    Traceback (most recent call last):
748
    NoInputData: max
749
"""
750

751

752
def test_suite():
1✔
753
    import doctest
1✔
754
    return unittest.TestSuite((
1✔
755
        doctest.DocFileSuite(
756
            '../errors.rst',
757
            setUp=formSetUp, tearDown=tearDown,
758
            optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,
759
        ),
760
        # The following test needs some zope.security test setup
761
        # doctest.DocFileSuite(
762
        #     'bugs.txt',
763
        #     setUp=formSetUp, tearDown=tearDown,
764
        #     optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
765
        #     ),
766
        doctest.DocFileSuite(
767
            '../form.rst',
768
            setUp=formSetUp, tearDown=tearDown,
769
            optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,
770
        ),
771
        doctest.DocTestSuite(setUp=formSetUp, tearDown=tearDown),
772
        doctest.DocTestSuite('zope.formlib.errors'),
773
    ))
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