# -*- coding: utf-8; -*-
import os
from unittest.mock import MagicMock, patch
import sqlalchemy as sa
import colander
import deform
from pyramid.renderers import get_renderer
from wuttaweb.forms import base as mod, widgets
from wuttaweb.grids import Grid
from wuttaweb.testing import WebTestCase
here = os.path.dirname(__file__)
class TestForm(WebTestCase):
mako_directories = ["wuttaweb:templates", here]
def make_form(self, **kwargs):
return mod.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)
# can also just specify widget pseudo-type (invalid)
self.assertNotIn("bar", form.widgets)
self.assertRaises(ValueError, form.set_widget, "bar", "ldjfadjfadj")
# can also just specify widget pseudo-type (valid)
self.assertNotIn("bar", form.widgets)
form.set_widget("bar", "notes")
self.assertIsInstance(form.widgets["bar"], widgets.NotesWidget)
def test_make_widget(self):
form = self.make_form(fields=["foo", "bar"])
# notes
widget = form.make_widget("notes")
self.assertIsInstance(widget, widgets.NotesWidget)
# invalid
widget = form.make_widget("fdajvdafjjf")
self.assertIsNone(widget)
def test_set_default_widgets(self):
model = self.app.model
# no defaults for "plain" schema
form = self.make_form(fields=["foo", "bar"])
self.assertEqual(form.widgets, {})
# no defaults for "plain" mapped class
form = self.make_form(model_class=model.Setting)
self.assertEqual(form.widgets, {})
class MyWidget(widgets.Widget):
pass
# widget set for datetime mapped field
form = self.make_form(model_class=model.Upgrade)
self.assertIn("created", form.widgets)
self.assertIsNot(form.widgets["created"], MyWidget)
self.assertNotIsInstance(form.widgets["created"], MyWidget)
# widget *not* set for datetime, if override present
form = self.make_form(
model_class=model.Upgrade, widgets={"created": MyWidget()}
)
self.assertIn("created", form.widgets)
self.assertIsInstance(form.widgets["created"], MyWidget)
# mock up a table with all relevant column types
class Whatever(model.Base):
__tablename__ = "whatever"
id = sa.Column(sa.Integer(), primary_key=True)
date = sa.Column(sa.Date())
date_time = sa.Column(sa.DateTime())
# widget set for all known types
form = self.make_form(model_class=Whatever)
self.assertIsInstance(form.widgets["date"], widgets.WuttaDateWidget)
self.assertIsInstance(form.widgets["date_time"], widgets.WuttaDateTimeWidget)
def test_set_grid(self):
form = self.make_form(fields=["foo", "bar"])
self.assertNotIn("foo", form.widgets)
self.assertNotIn("foogrid", form.grid_vue_context)
grid = Grid(
self.request,
key="foogrid",
columns=["a", "b"],
data=[{"a": 1, "b": 2}, {"a": 3, "b": 4}],
)
form.set_grid("foo", grid)
self.assertIn("foo", form.widgets)
self.assertIsInstance(form.widgets["foo"], widgets.GridWidget)
self.assertIn("foogrid", form.grid_vue_context)
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(ValueError, 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.assertIsNone(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('