feat: add Users view; improve CRUD master for SQLAlchemy models
This commit is contained in:
parent
33589f1cd8
commit
eac3b81918
33 changed files with 1510 additions and 253 deletions
|
@ -8,46 +8,15 @@ import deform
|
|||
from pyramid import testing
|
||||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
from wuttaweb.forms import base
|
||||
from wuttaweb.forms import base, widgets
|
||||
from wuttaweb import helpers
|
||||
|
||||
|
||||
class TestFieldList(TestCase):
|
||||
|
||||
def test_insert_before(self):
|
||||
fields = base.FieldList(['f1', 'f2'])
|
||||
self.assertEqual(fields, ['f1', 'f2'])
|
||||
|
||||
# typical
|
||||
fields.insert_before('f1', 'XXX')
|
||||
self.assertEqual(fields, ['XXX', 'f1', 'f2'])
|
||||
fields.insert_before('f2', 'YYY')
|
||||
self.assertEqual(fields, ['XXX', 'f1', 'YYY', 'f2'])
|
||||
|
||||
# appends new field if reference field is invalid
|
||||
fields.insert_before('f3', 'ZZZ')
|
||||
self.assertEqual(fields, ['XXX', 'f1', 'YYY', 'f2', 'ZZZ'])
|
||||
|
||||
def test_insert_after(self):
|
||||
fields = base.FieldList(['f1', 'f2'])
|
||||
self.assertEqual(fields, ['f1', 'f2'])
|
||||
|
||||
# typical
|
||||
fields.insert_after('f1', 'XXX')
|
||||
self.assertEqual(fields, ['f1', 'XXX', 'f2'])
|
||||
fields.insert_after('XXX', 'YYY')
|
||||
self.assertEqual(fields, ['f1', 'XXX', 'YYY', 'f2'])
|
||||
|
||||
# appends new field if reference field is invalid
|
||||
fields.insert_after('f3', 'ZZZ')
|
||||
self.assertEqual(fields, ['f1', 'XXX', 'YYY', 'f2', 'ZZZ'])
|
||||
|
||||
|
||||
class TestForm(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.config = WuttaConfig(defaults={
|
||||
'wutta.web.menus.handler_spec': 'tests.utils:NullMenuHandler',
|
||||
'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)
|
||||
|
@ -74,7 +43,7 @@ class TestForm(TestCase):
|
|||
|
||||
def test_init_with_none(self):
|
||||
form = self.make_form()
|
||||
self.assertIsNone(form.fields)
|
||||
self.assertEqual(form.fields, [])
|
||||
|
||||
def test_init_with_fields(self):
|
||||
form = self.make_form(fields=['foo', 'bar'])
|
||||
|
@ -115,6 +84,79 @@ class TestForm(TestCase):
|
|||
form.set_fields(['baz'])
|
||||
self.assertEqual(form.fields, ['baz'])
|
||||
|
||||
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_get_schema(self):
|
||||
model = self.app.model
|
||||
form = self.make_form()
|
||||
|
@ -144,6 +186,13 @@ class TestForm(TestCase):
|
|||
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'])
|
||||
|
@ -166,7 +215,23 @@ class TestForm(TestCase):
|
|||
form.set_required('bar', False)
|
||||
schema = form.get_schema()
|
||||
self.assertIs(schema['foo'].missing, colander.required)
|
||||
self.assertIsNone(schema['bar'].missing)
|
||||
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)
|
||||
|
||||
def test_get_deform(self):
|
||||
model = self.app.model
|
||||
|
@ -372,34 +437,6 @@ class TestForm(TestCase):
|
|||
self.assertEqual(len(errors), 1)
|
||||
self.assertEqual(errors[0], "something is wrong")
|
||||
|
||||
def test_get_vue_field_value(self):
|
||||
schema = self.make_schema()
|
||||
form = self.make_form(schema=schema)
|
||||
|
||||
# null field value
|
||||
value = form.get_vue_field_value('foo')
|
||||
self.assertEqual(value, 'null')
|
||||
|
||||
# non-default / explicit value
|
||||
# TODO: surely need a different approach to set value
|
||||
dform = form.get_deform()
|
||||
dform['foo'].cstruct = 'blarg'
|
||||
value = form.get_vue_field_value('foo')
|
||||
self.assertEqual(value, '"blarg"')
|
||||
|
||||
def test_jsonify_value(self):
|
||||
form = self.make_form()
|
||||
|
||||
# null field value
|
||||
value = form.jsonify_value(colander.null)
|
||||
self.assertEqual(value, 'null')
|
||||
value = form.jsonify_value(None)
|
||||
self.assertEqual(value, 'null')
|
||||
|
||||
# string value
|
||||
value = form.jsonify_value('blarg')
|
||||
self.assertEqual(value, '"blarg"')
|
||||
|
||||
def test_validate(self):
|
||||
schema = self.make_schema()
|
||||
form = self.make_form(schema=schema)
|
||||
|
|
199
tests/forms/test_schema.py
Normal file
199
tests/forms/test_schema.py
Normal file
|
@ -0,0 +1,199 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import colander
|
||||
from pyramid import testing
|
||||
|
||||
from sqlalchemy import orm
|
||||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
from wuttaweb.forms import schema as mod
|
||||
from tests.util import DataTestCase
|
||||
|
||||
|
||||
class TestObjectNode(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.request = testing.DummyRequest(wutta_config=self.config)
|
||||
|
||||
def test_dictify(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
|
||||
# unsupported type raises error
|
||||
node = mod.ObjectNode(colander.String())
|
||||
self.assertRaises(NotImplementedError, node.dictify, person)
|
||||
|
||||
# but supported type can dictify
|
||||
node = mod.ObjectNode(mod.PersonRef(self.request))
|
||||
value = node.dictify(person)
|
||||
self.assertIs(value, person)
|
||||
|
||||
def test_objectify(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
|
||||
# unsupported type raises error
|
||||
node = mod.ObjectNode(colander.String())
|
||||
self.assertRaises(NotImplementedError, node.objectify, person)
|
||||
|
||||
# but supported type can objectify
|
||||
node = mod.ObjectNode(mod.PersonRef(self.request))
|
||||
value = node.objectify(person)
|
||||
self.assertIs(value, person)
|
||||
|
||||
|
||||
class TestObjectRef(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.request = testing.DummyRequest(wutta_config=self.config)
|
||||
|
||||
def test_empty_option(self):
|
||||
|
||||
# null by default
|
||||
typ = mod.ObjectRef(self.request)
|
||||
self.assertIsNone(typ.empty_option)
|
||||
|
||||
# passing true yields default empty option
|
||||
typ = mod.ObjectRef(self.request, empty_option=True)
|
||||
self.assertEqual(typ.empty_option, ('', "(none)"))
|
||||
|
||||
# can set explicitly
|
||||
typ = mod.ObjectRef(self.request, empty_option=('foo', 'bar'))
|
||||
self.assertEqual(typ.empty_option, ('foo', 'bar'))
|
||||
|
||||
# can set just a label
|
||||
typ = mod.ObjectRef(self.request, empty_option="(empty)")
|
||||
self.assertEqual(typ.empty_option, ('', "(empty)"))
|
||||
|
||||
def test_model_class(self):
|
||||
typ = mod.ObjectRef(self.request)
|
||||
self.assertRaises(NotImplementedError, getattr, typ, 'model_class')
|
||||
|
||||
def test_serialize(self):
|
||||
model = self.app.model
|
||||
node = colander.SchemaNode(colander.String())
|
||||
|
||||
# null
|
||||
typ = mod.ObjectRef(self.request)
|
||||
value = typ.serialize(node, colander.null)
|
||||
self.assertIs(value, colander.null)
|
||||
|
||||
# model instance
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
self.assertIsNotNone(person.uuid)
|
||||
typ = mod.ObjectRef(self.request)
|
||||
value = typ.serialize(node, person)
|
||||
self.assertEqual(value, person.uuid)
|
||||
|
||||
def test_deserialize(self):
|
||||
model = self.app.model
|
||||
node = colander.SchemaNode(colander.String())
|
||||
|
||||
# null
|
||||
typ = mod.ObjectRef(self.request)
|
||||
value = typ.deserialize(node, colander.null)
|
||||
self.assertIs(value, colander.null)
|
||||
|
||||
# model instance
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
self.assertIsNotNone(person.uuid)
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session)
|
||||
value = typ.deserialize(node, person.uuid)
|
||||
self.assertIs(value, person)
|
||||
|
||||
def test_dictify(self):
|
||||
model = self.app.model
|
||||
node = colander.SchemaNode(colander.String())
|
||||
|
||||
# model instance
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
self.assertIsNotNone(person.uuid)
|
||||
typ = mod.ObjectRef(self.request)
|
||||
value = typ.dictify(person)
|
||||
self.assertIs(value, person)
|
||||
|
||||
def test_objectify(self):
|
||||
model = self.app.model
|
||||
node = colander.SchemaNode(colander.String())
|
||||
|
||||
# null
|
||||
typ = mod.ObjectRef(self.request)
|
||||
value = typ.objectify(None)
|
||||
self.assertIsNone(value)
|
||||
|
||||
# model instance
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
self.assertIsNotNone(person.uuid)
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session)
|
||||
value = typ.objectify(person.uuid)
|
||||
self.assertIs(value, person)
|
||||
|
||||
# error if not found
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session)
|
||||
self.assertRaises(ValueError, typ.objectify, 'WRONG-UUID')
|
||||
|
||||
def test_get_query(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session)
|
||||
query = typ.get_query()
|
||||
self.assertIsInstance(query, orm.Query)
|
||||
|
||||
def test_sort_query(self):
|
||||
model = self.app.model
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session)
|
||||
query = typ.get_query()
|
||||
sorted_query = typ.sort_query(query)
|
||||
self.assertIs(sorted_query, query)
|
||||
|
||||
def test_widget_maker(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
|
||||
# basic
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session)
|
||||
widget = typ.widget_maker()
|
||||
self.assertEqual(len(widget.values), 1)
|
||||
self.assertEqual(widget.values[0][1], "Betty Boop")
|
||||
|
||||
# empty option
|
||||
with patch.object(mod.ObjectRef, 'model_class', new=model.Person):
|
||||
typ = mod.ObjectRef(self.request, session=self.session, empty_option=True)
|
||||
widget = typ.widget_maker()
|
||||
self.assertEqual(len(widget.values), 2)
|
||||
self.assertEqual(widget.values[0][1], "(none)")
|
||||
self.assertEqual(widget.values[1][1], "Betty Boop")
|
||||
|
||||
|
||||
class TestPersonRef(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.request = testing.DummyRequest(wutta_config=self.config)
|
||||
|
||||
def test_sort_query(self):
|
||||
typ = mod.PersonRef(self.request, session=self.session)
|
||||
query = typ.get_query()
|
||||
self.assertIsInstance(query, orm.Query)
|
||||
sorted_query = typ.sort_query(query)
|
||||
self.assertIsInstance(sorted_query, orm.Query)
|
||||
self.assertIsNot(sorted_query, query)
|
32
tests/forms/test_widgets.py
Normal file
32
tests/forms/test_widgets.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
import colander
|
||||
import deform
|
||||
from pyramid import testing
|
||||
|
||||
from wuttaweb.forms import widgets
|
||||
from wuttaweb.forms.schema import PersonRef
|
||||
from tests.util import WebTestCase
|
||||
|
||||
class TestObjectRefWidget(WebTestCase):
|
||||
|
||||
def test_serialize(self):
|
||||
model = self.app.model
|
||||
person = model.Person(full_name="Betty Boop")
|
||||
self.session.add(person)
|
||||
self.session.commit()
|
||||
|
||||
# standard (editable)
|
||||
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
||||
widget = widgets.ObjectRefWidget(self.request)
|
||||
field = deform.Field(node)
|
||||
html = widget.serialize(field, person.uuid)
|
||||
self.assertIn('<select ', html)
|
||||
|
||||
# readonly
|
||||
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
||||
node.model_instance = person
|
||||
widget = widgets.ObjectRefWidget(self.request)
|
||||
field = deform.Field(node)
|
||||
html = widget.serialize(field, person.uuid, readonly=True)
|
||||
self.assertEqual(html, '<span>Betty Boop</span>')
|
Loading…
Add table
Add a link
Reference in a new issue