# -*- coding: utf-8; -*-
from unittest import TestCase
from unittest.mock import MagicMock, patch
import colander
import deform
from pyramid import testing
from wuttjamaican.conf import WuttaConfig
from wuttaweb.forms import base, widgets
from wuttaweb import helpers
from wuttaweb.grids import Grid
class TestForm(TestCase):
def setUp(self):
self.config = WuttaConfig(defaults={
'wutta.web.menus.handler_spec': 'tests.util:NullMenuHandler',
})
self.app = self.config.get_app()
self.request = testing.DummyRequest(wutta_config=self.config, use_oruga=False)
self.pyramid_config = testing.setUp(request=self.request, settings={
'mako.directories': ['wuttaweb:templates'],
'pyramid_deform.template_search_path': 'wuttaweb:templates/deform',
})
def tearDown(self):
testing.tearDown()
def make_form(self, **kwargs):
return base.Form(self.request, **kwargs)
def make_schema(self):
schema = colander.Schema(children=[
colander.SchemaNode(colander.String(),
name='foo'),
colander.SchemaNode(colander.String(),
name='bar'),
])
return schema
def test_init_with_none(self):
form = self.make_form()
self.assertEqual(form.fields, [])
def test_init_with_fields(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.fields, ['foo', 'bar'])
def test_init_with_schema(self):
schema = self.make_schema()
form = self.make_form(schema=schema)
self.assertEqual(form.fields, ['foo', 'bar'])
def test_vue_tagname(self):
form = self.make_form()
self.assertEqual(form.vue_tagname, 'wutta-form')
def test_vue_component(self):
form = self.make_form()
self.assertEqual(form.vue_component, 'WuttaForm')
def test_contains(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertIn('foo', form)
self.assertNotIn('baz', form)
def test_iter(self):
form = self.make_form(fields=['foo', 'bar'])
fields = list(iter(form))
self.assertEqual(fields, ['foo', 'bar'])
fields = []
for field in form:
fields.append(field)
self.assertEqual(fields, ['foo', 'bar'])
def test_set_fields(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.fields, ['foo', 'bar'])
form.set_fields(['baz'])
self.assertEqual(form.fields, ['baz'])
def test_append(self):
form = self.make_form(fields=['one', 'two'])
self.assertEqual(form.fields, ['one', 'two'])
form.append('one', 'two', 'three')
self.assertEqual(form.fields, ['one', 'two', 'three'])
def test_remove(self):
form = self.make_form(fields=['one', 'two', 'three', 'four'])
self.assertEqual(form.fields, ['one', 'two', 'three', 'four'])
form.remove('two', 'three')
self.assertEqual(form.fields, ['one', 'four'])
def test_set_node(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.nodes, {})
# complete node
node = colander.SchemaNode(colander.Bool(), name='foo')
form.set_node('foo', node)
self.assertIs(form.nodes['foo'], node)
# type only
typ = colander.Bool()
form.set_node('foo', typ)
node = form.nodes['foo']
self.assertIsInstance(node, colander.SchemaNode)
self.assertIsInstance(node.typ, colander.Bool)
self.assertEqual(node.name, 'foo')
# schema is updated if already present
schema = form.get_schema()
self.assertIsNotNone(schema)
typ = colander.Date()
form.set_node('foo', typ)
node = form.nodes['foo']
self.assertIsInstance(node, colander.SchemaNode)
self.assertIsInstance(node.typ, colander.Date)
self.assertEqual(node.name, 'foo')
def test_set_widget(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.widgets, {})
# basic
widget = widgets.SelectWidget()
form.set_widget('foo', widget)
self.assertIs(form.widgets['foo'], widget)
# schema is updated if already present
schema = form.get_schema()
self.assertIsNotNone(schema)
self.assertIs(schema['foo'].widget, widget)
new_widget = widgets.TextInputWidget()
form.set_widget('foo', new_widget)
self.assertIs(form.widgets['foo'], new_widget)
self.assertIs(schema['foo'].widget, new_widget)
def test_set_validator(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.validators, {})
def validate1(node, value):
pass
# basic
form.set_validator('foo', validate1)
self.assertIs(form.validators['foo'], validate1)
def validate2(node, value):
pass
# schema is updated if already present
schema = form.get_schema()
self.assertIsNotNone(schema)
self.assertIs(schema['foo'].validator, validate1)
form.set_validator('foo', validate2)
self.assertIs(form.validators['foo'], validate2)
self.assertIs(schema['foo'].validator, validate2)
def test_set_default(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.defaults, {})
# basic
form.set_default('foo', 42)
self.assertEqual(form.defaults['foo'], 42)
def test_get_schema(self):
model = self.app.model
form = self.make_form()
self.assertIsNone(form.schema)
# provided schema is returned
schema = self.make_schema()
form = self.make_form(schema=schema)
self.assertIs(form.schema, schema)
self.assertIs(form.get_schema(), schema)
# schema is auto-generated if fields provided
form = self.make_form(fields=['foo', 'bar'])
schema = form.get_schema()
self.assertEqual(len(schema.children), 2)
self.assertEqual(schema['foo'].name, 'foo')
# but auto-generating without fields is not supported
form = self.make_form()
self.assertIsNone(form.schema)
self.assertRaises(NotImplementedError, form.get_schema)
# schema is auto-generated if model_class provided
form = self.make_form(model_class=model.Setting)
schema = form.get_schema()
self.assertEqual(len(schema.children), 2)
self.assertIn('name', schema)
self.assertIn('value', schema)
# but node overrides are honored when auto-generating
form = self.make_form(model_class=model.Setting)
value_node = colander.SchemaNode(colander.Bool(), name='value')
form.set_node('value', value_node)
schema = form.get_schema()
self.assertIs(schema['value'], value_node)
# schema is auto-generated if model_instance provided
form = self.make_form(model_instance=model.Setting(name='uhoh'))
self.assertEqual(form.fields, ['name', 'value'])
self.assertIsNone(form.schema)
# nb. force method to get new fields
del form.fields
schema = form.get_schema()
self.assertEqual(len(schema.children), 2)
self.assertIn('name', schema)
self.assertIn('value', schema)
# ColanderAlchemy schema still has *all* requested fields
form = self.make_form(model_instance=model.Setting(name='uhoh'),
fields=['name', 'value', 'foo', 'bar'])
self.assertEqual(form.fields, ['name', 'value', 'foo', 'bar'])
self.assertIsNone(form.schema)
schema = form.get_schema()
self.assertEqual(len(schema.children), 4)
self.assertIn('name', schema)
self.assertIn('value', schema)
self.assertIn('foo', schema)
self.assertIn('bar', schema)
# schema nodes are required by default
form = self.make_form(fields=['foo', 'bar'])
schema = form.get_schema()
self.assertIs(schema['foo'].missing, colander.required)
self.assertIs(schema['bar'].missing, colander.required)
# but fields can be marked *not* required
form = self.make_form(fields=['foo', 'bar'])
form.set_required('bar', False)
schema = form.get_schema()
self.assertIs(schema['foo'].missing, colander.required)
self.assertIs(schema['bar'].missing, colander.null)
# validator overrides are honored
def validate(node, value): pass
form = self.make_form(model_class=model.Setting)
form.set_validator('name', validate)
schema = form.get_schema()
self.assertIs(schema['name'].validator, validate)
# validator can be set for whole form
form = self.make_form(model_class=model.Setting)
schema = form.get_schema()
self.assertIsNone(schema.validator)
form = self.make_form(model_class=model.Setting)
form.set_validator(None, validate)
schema = form.get_schema()
self.assertIs(schema.validator, validate)
# default value overrides are honored
form = self.make_form(model_class=model.Setting)
form.set_default('name', 'foo')
schema = form.get_schema()
self.assertEqual(schema['name'].default, 'foo')
def test_get_deform(self):
model = self.app.model
schema = self.make_schema()
# basic
form = self.make_form(schema=schema)
self.assertFalse(hasattr(form, 'deform_form'))
dform = form.get_deform()
self.assertIsInstance(dform, deform.Form)
self.assertIs(form.deform_form, dform)
# with model instance as dict
myobj = {'foo': 'one', 'bar': 'two'}
form = self.make_form(schema=schema, model_instance=myobj)
dform = form.get_deform()
self.assertEqual(dform.cstruct, myobj)
# with sqlalchemy model instance
myobj = model.Setting(name='foo', value='bar')
form = self.make_form(model_instance=myobj)
dform = form.get_deform()
self.assertEqual(dform.cstruct, {'name': 'foo', 'value': 'bar'})
# sqlalchemy instance with null value
myobj = model.Setting(name='foo', value=None)
form = self.make_form(model_instance=myobj)
dform = form.get_deform()
self.assertEqual(dform.cstruct, {'name': 'foo', 'value': colander.null})
def test_get_cancel_url(self):
# is referrer by default
form = self.make_form()
self.request.get_referrer = MagicMock(return_value='/cancel-default')
self.assertEqual(form.get_cancel_url(), '/cancel-default')
del self.request.get_referrer
# or can be static URL
form = self.make_form(cancel_url='/cancel-static')
self.assertEqual(form.get_cancel_url(), '/cancel-static')
# or can be fallback URL (nb. 'NOPE' indicates no referrer)
form = self.make_form(cancel_url_fallback='/cancel-fallback')
self.request.get_referrer = MagicMock(return_value='NOPE')
self.assertEqual(form.get_cancel_url(), '/cancel-fallback')
del self.request.get_referrer
# or can be referrer fallback, i.e. home page
form = self.make_form()
def get_referrer(default=None):
if default == 'NOPE':
return 'NOPE'
return '/home-page'
self.request.get_referrer = get_referrer
self.assertEqual(form.get_cancel_url(), '/home-page')
del self.request.get_referrer
def test_get_label(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.get_label('foo'), "Foo")
form.set_label('foo', "Baz")
self.assertEqual(form.get_label('foo'), "Baz")
def test_set_label(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.get_label('foo'), "Foo")
form.set_label('foo', "Baz")
self.assertEqual(form.get_label('foo'), "Baz")
# schema should be updated when setting label
schema = self.make_schema()
form = self.make_form(schema=schema)
form.set_label('foo', "Woohoo")
self.assertEqual(form.get_label('foo'), "Woohoo")
self.assertEqual(schema['foo'].title, "Woohoo")
def test_readonly_fields(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.readonly_fields, set())
self.assertFalse(form.is_readonly('foo'))
form.set_readonly('foo')
self.assertEqual(form.readonly_fields, {'foo'})
self.assertTrue(form.is_readonly('foo'))
self.assertFalse(form.is_readonly('bar'))
form.set_readonly('bar')
self.assertEqual(form.readonly_fields, {'foo', 'bar'})
self.assertTrue(form.is_readonly('foo'))
self.assertTrue(form.is_readonly('bar'))
form.set_readonly('foo', False)
self.assertEqual(form.readonly_fields, {'bar'})
self.assertFalse(form.is_readonly('foo'))
self.assertTrue(form.is_readonly('bar'))
def test_required_fields(self):
form = self.make_form(fields=['foo', 'bar'])
self.assertEqual(form.required_fields, {})
self.assertIsNone(form.is_required('foo'))
form.set_required('foo')
self.assertEqual(form.required_fields, {'foo': True})
self.assertTrue(form.is_required('foo'))
self.assertIsNone(form.is_required('bar'))
form.set_required('bar')
self.assertEqual(form.required_fields, {'foo': True, 'bar': True})
self.assertTrue(form.is_required('foo'))
self.assertTrue(form.is_required('bar'))
form.set_required('foo', False)
self.assertEqual(form.required_fields, {'foo': False, 'bar': True})
self.assertFalse(form.is_required('foo'))
self.assertTrue(form.is_required('bar'))
def test_render_vue_tag(self):
schema = self.make_schema()
form = self.make_form(schema=schema)
html = form.render_vue_tag()
self.assertEqual(html, '')
def test_render_vue_template(self):
self.pyramid_config.include('pyramid_mako')
self.pyramid_config.add_subscriber('wuttaweb.subscribers.before_render',
'pyramid.events.BeforeRender')
# form button is disabled on @submit by default
schema = self.make_schema()
form = self.make_form(schema=schema, cancel_url='/')
html = form.render_vue_template()
self.assertIn('