# -*- 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('