feat: add basic support for wutta-continuum
and i mean *basic* - so far.. eventually will expose version history for viewing etc. unfortunately got carried away and reorganized the api docs a little while i was at it..
This commit is contained in:
parent
24f5ee1dcc
commit
71728718d8
|
@ -1,10 +0,0 @@
|
||||||
|
|
||||||
Package API
|
|
||||||
===========
|
|
||||||
|
|
||||||
This is the "raw" API documentation for the ``wuttaweb`` package.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
wuttaweb/index
|
|
6
docs/api/wuttaweb.conf.rst
Normal file
6
docs/api/wuttaweb.conf.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttaweb.conf``
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. automodule:: wuttaweb.conf
|
||||||
|
:members:
|
6
docs/api/wuttaweb.db.continuum.rst
Normal file
6
docs/api/wuttaweb.db.continuum.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttaweb.db.continuum``
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. automodule:: wuttaweb.db.continuum
|
||||||
|
:members:
|
6
docs/api/wuttaweb.db.sess.rst
Normal file
6
docs/api/wuttaweb.db.sess.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttaweb.db.sess``
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. automodule:: wuttaweb.db.sess
|
||||||
|
:members:
|
6
docs/api/wuttaweb.rst
Normal file
6
docs/api/wuttaweb.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
``wuttaweb``
|
||||||
|
============
|
||||||
|
|
||||||
|
.. automodule:: wuttaweb
|
||||||
|
:members:
|
|
@ -1,38 +0,0 @@
|
||||||
|
|
||||||
``wuttaweb``
|
|
||||||
============
|
|
||||||
|
|
||||||
.. automodule:: wuttaweb
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
app
|
|
||||||
auth
|
|
||||||
db
|
|
||||||
forms
|
|
||||||
forms.base
|
|
||||||
forms.schema
|
|
||||||
forms.widgets
|
|
||||||
grids
|
|
||||||
grids.base
|
|
||||||
grids.filters
|
|
||||||
handler
|
|
||||||
helpers
|
|
||||||
menus
|
|
||||||
progress
|
|
||||||
static
|
|
||||||
subscribers
|
|
||||||
util
|
|
||||||
views
|
|
||||||
views.auth
|
|
||||||
views.base
|
|
||||||
views.common
|
|
||||||
views.essential
|
|
||||||
views.master
|
|
||||||
views.people
|
|
||||||
views.progress
|
|
||||||
views.roles
|
|
||||||
views.settings
|
|
||||||
views.upgrades
|
|
||||||
views.users
|
|
|
@ -34,6 +34,7 @@ intersphinx_mapping = {
|
||||||
'rattail-manual': ('https://rattailproject.org/docs/rattail-manual/', None),
|
'rattail-manual': ('https://rattailproject.org/docs/rattail-manual/', None),
|
||||||
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
|
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
|
||||||
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
|
||||||
|
'wutta-continuum': ('https://rattailproject.org/docs/wutta-continuum/', None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,53 @@ project.
|
||||||
|
|
||||||
.. _test coverage: https://buildbot.rattailproject.org/coverage/wuttaweb/
|
.. _test coverage: https://buildbot.rattailproject.org/coverage/wuttaweb/
|
||||||
|
|
||||||
|
However as you can see..the API should be fairly well documented but
|
||||||
|
the narrative docs are pretty scant. That will eventually change.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
:caption: Documentation:
|
||||||
|
|
||||||
glossary
|
glossary
|
||||||
narr/index
|
narr/templates/index
|
||||||
api/index
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
:caption: Package API:
|
||||||
|
|
||||||
|
api/wuttaweb
|
||||||
|
api/wuttaweb.app
|
||||||
|
api/wuttaweb.auth
|
||||||
|
api/wuttaweb.conf
|
||||||
|
api/wuttaweb.db
|
||||||
|
api/wuttaweb.db.continuum
|
||||||
|
api/wuttaweb.db.sess
|
||||||
|
api/wuttaweb.forms
|
||||||
|
api/wuttaweb.forms.base
|
||||||
|
api/wuttaweb.forms.schema
|
||||||
|
api/wuttaweb.forms.widgets
|
||||||
|
api/wuttaweb.grids
|
||||||
|
api/wuttaweb.grids.base
|
||||||
|
api/wuttaweb.grids.filters
|
||||||
|
api/wuttaweb.handler
|
||||||
|
api/wuttaweb.helpers
|
||||||
|
api/wuttaweb.menus
|
||||||
|
api/wuttaweb.progress
|
||||||
|
api/wuttaweb.static
|
||||||
|
api/wuttaweb.subscribers
|
||||||
|
api/wuttaweb.util
|
||||||
|
api/wuttaweb.views
|
||||||
|
api/wuttaweb.views.auth
|
||||||
|
api/wuttaweb.views.base
|
||||||
|
api/wuttaweb.views.common
|
||||||
|
api/wuttaweb.views.essential
|
||||||
|
api/wuttaweb.views.master
|
||||||
|
api/wuttaweb.views.people
|
||||||
|
api/wuttaweb.views.progress
|
||||||
|
api/wuttaweb.views.roles
|
||||||
|
api/wuttaweb.views.settings
|
||||||
|
api/wuttaweb.views.upgrades
|
||||||
|
api/wuttaweb.views.users
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
Documentation
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
templates/index
|
|
|
@ -42,12 +42,13 @@ dependencies = [
|
||||||
"pyramid_tm",
|
"pyramid_tm",
|
||||||
"waitress",
|
"waitress",
|
||||||
"WebHelpers2",
|
"WebHelpers2",
|
||||||
"WuttJamaican[db,email]>=0.13.0",
|
"WuttJamaican[db,email]>=0.13.2",
|
||||||
"zope.sqlalchemy>=1.5",
|
"zope.sqlalchemy>=1.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
continuum = ["Wutta-Continuum"]
|
||||||
docs = ["Sphinx", "furo"]
|
docs = ["Sphinx", "furo"]
|
||||||
tests = ["pytest-cov", "tox"]
|
tests = ["pytest-cov", "tox"]
|
||||||
|
|
||||||
|
@ -60,6 +61,10 @@ main = "wuttaweb.app:main"
|
||||||
wuttaweb = "wuttaweb.app:WebAppProvider"
|
wuttaweb = "wuttaweb.app:WebAppProvider"
|
||||||
|
|
||||||
|
|
||||||
|
[project.entry-points."wutta.config.extensions"]
|
||||||
|
wuttaweb = "wuttaweb.conf:WuttaWebConfigExtension"
|
||||||
|
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://wuttaproject.org/"
|
Homepage = "https://wuttaproject.org/"
|
||||||
Repository = "https://forgejo.wuttaproject.org/wutta/wuttaweb"
|
Repository = "https://forgejo.wuttaproject.org/wutta/wuttaweb"
|
||||||
|
|
|
@ -98,7 +98,8 @@ class WuttaSecurityPolicy:
|
||||||
for current user
|
for current user
|
||||||
|
|
||||||
:param db_session: Optional :term:`db session` to use, instead of
|
:param db_session: Optional :term:`db session` to use, instead of
|
||||||
:class:`wuttaweb.db.Session`. Probably only useful for tests.
|
:class:`wuttaweb.db.sess.Session`. Probably only useful for
|
||||||
|
tests.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, db_session=None):
|
def __init__(self, db_session=None):
|
||||||
|
|
43
src/wuttaweb/conf.py
Normal file
43
src/wuttaweb/conf.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# wuttaweb -- Web App for Wutta Framework
|
||||||
|
# Copyright © 2024 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Wutta Framework.
|
||||||
|
#
|
||||||
|
# Wutta Framework is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
# later version.
|
||||||
|
#
|
||||||
|
# Wutta Framework is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Config Extension
|
||||||
|
"""
|
||||||
|
|
||||||
|
from wuttjamaican.conf import WuttaConfigExtension
|
||||||
|
|
||||||
|
|
||||||
|
class WuttaWebConfigExtension(WuttaConfigExtension):
|
||||||
|
"""
|
||||||
|
Config extension for WuttaWeb.
|
||||||
|
|
||||||
|
This sets the default plugin for SQLAlchemy-Continuum. Which is
|
||||||
|
only relevant if Wutta-Continuum is installed and enabled. For
|
||||||
|
more info see :doc:`wutta-continuum:index`.
|
||||||
|
"""
|
||||||
|
key = 'wuttaweb'
|
||||||
|
|
||||||
|
def configure(self, config):
|
||||||
|
""" """
|
||||||
|
config.setdefault('wutta_continuum.wutta_plugin_spec',
|
||||||
|
'wuttaweb.db.continuum:WuttaWebContinuumPlugin')
|
31
src/wuttaweb/db/__init__.py
Normal file
31
src/wuttaweb/db/__init__.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# wuttaweb -- Web App for Wutta Framework
|
||||||
|
# Copyright © 2024 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Wutta Framework.
|
||||||
|
#
|
||||||
|
# Wutta Framework is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
# later version.
|
||||||
|
#
|
||||||
|
# Wutta Framework is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Database Utilities
|
||||||
|
|
||||||
|
The following are available from this ``wuttaweb.db`` namespace:
|
||||||
|
|
||||||
|
* :class:`~wuttaweb.db.sess.Session`
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .sess import Session
|
56
src/wuttaweb/db/continuum.py
Normal file
56
src/wuttaweb/db/continuum.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# wuttaweb -- Web App for Wutta Framework
|
||||||
|
# Copyright © 2024 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Wutta Framework.
|
||||||
|
#
|
||||||
|
# Wutta Framework is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
# later version.
|
||||||
|
#
|
||||||
|
# Wutta Framework is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Wutta Framework. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
SQLAlchemy-Continuum Plugin
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pyramid.threadlocal import get_current_request
|
||||||
|
|
||||||
|
try:
|
||||||
|
from wutta_continuum.conf import WuttaContinuumPlugin
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
|
||||||
|
class WuttaWebContinuumPlugin(WuttaContinuumPlugin):
|
||||||
|
"""
|
||||||
|
SQLAlchemy-Continuum manager plugin for WuttaWeb.
|
||||||
|
|
||||||
|
This tries to use the current request to obtain user and IP
|
||||||
|
address for the transaction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# TODO: should find a better way, threadlocals are bad?
|
||||||
|
# https://docs.pylonsproject.org/projects/pyramid/en/latest/api/threadlocal.html#pyramid.threadlocal.get_current_request
|
||||||
|
|
||||||
|
def get_remote_addr(self, uow, session):
|
||||||
|
""" """
|
||||||
|
request = get_current_request()
|
||||||
|
if request:
|
||||||
|
return request.client_addr
|
||||||
|
|
||||||
|
def get_user_id(self, uow, session):
|
||||||
|
""" """
|
||||||
|
request = get_current_request()
|
||||||
|
if request and request.user:
|
||||||
|
return request.user.uuid
|
|
@ -128,7 +128,7 @@ class WuttaSet(colander.Set):
|
||||||
:param request: Current :term:`request` object.
|
:param request: Current :term:`request` object.
|
||||||
|
|
||||||
:param session: Optional :term:`db session` to use instead of
|
:param session: Optional :term:`db session` to use instead of
|
||||||
:class:`wuttaweb.db.Session`.
|
:class:`wuttaweb.db.sess.Session`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, request, session=None):
|
def __init__(self, request, session=None):
|
||||||
|
|
|
@ -99,8 +99,11 @@ class ObjectRefWidget(SelectWidget):
|
||||||
""" """
|
""" """
|
||||||
values = super().get_template_values(field, cstruct, kw)
|
values = super().get_template_values(field, cstruct, kw)
|
||||||
|
|
||||||
if 'url' not in values and self.url and field.schema.model_instance:
|
# add url, only if rendering readonly
|
||||||
values['url'] = self.url(field.schema.model_instance)
|
readonly = kw.get('readonly', self.readonly)
|
||||||
|
if readonly:
|
||||||
|
if 'url' not in values and self.url and field.schema.model_instance:
|
||||||
|
values['url'] = self.url(field.schema.model_instance)
|
||||||
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
@ -134,7 +137,7 @@ class WuttaCheckboxChoiceWidget(CheckboxChoiceWidget):
|
||||||
:param request: Current :term:`request` object.
|
:param request: Current :term:`request` object.
|
||||||
|
|
||||||
:param session: Optional :term:`db session` to use instead of
|
:param session: Optional :term:`db session` to use instead of
|
||||||
:class:`wuttaweb.db.Session`.
|
:class:`wuttaweb.db.sess.Session`.
|
||||||
|
|
||||||
It uses these Deform templates:
|
It uses these Deform templates:
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ def new_request_set_user(
|
||||||
from database, instead of :func:`default_user_getter()`.
|
from database, instead of :func:`default_user_getter()`.
|
||||||
|
|
||||||
:param db_session: Optional :term:`db session` to use,
|
:param db_session: Optional :term:`db session` to use,
|
||||||
instead of :class:`wuttaweb.db.Session`.
|
instead of :class:`wuttaweb.db.sess.Session`.
|
||||||
|
|
||||||
This will add to the request object:
|
This will add to the request object:
|
||||||
|
|
||||||
|
|
|
@ -491,10 +491,16 @@ def get_model_fields(config, model_class=None):
|
||||||
try:
|
try:
|
||||||
mapper = sa.inspect(model_class)
|
mapper = sa.inspect(model_class)
|
||||||
except sa.exc.NoInspectionAvailable:
|
except sa.exc.NoInspectionAvailable:
|
||||||
pass
|
return
|
||||||
else:
|
|
||||||
fields = [prop.key for prop in mapper.iterate_properties]
|
fields = [prop.key for prop in mapper.iterate_properties]
|
||||||
return fields
|
|
||||||
|
# nb. we never want the continuum 'versions' prop
|
||||||
|
app = config.get_app()
|
||||||
|
if app.continuum_is_enabled() and 'versions' in fields:
|
||||||
|
fields.remove('versions')
|
||||||
|
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
def make_json_safe(value, key=None, warn=True):
|
def make_json_safe(value, key=None, warn=True):
|
||||||
|
|
0
tests/db/__init__.py
Normal file
0
tests/db/__init__.py
Normal file
38
tests/db/test_continuum.py
Normal file
38
tests/db/test_continuum.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from tests.util import WebTestCase
|
||||||
|
|
||||||
|
from wuttaweb.db import continuum as mod
|
||||||
|
|
||||||
|
|
||||||
|
class TestWuttaWebContinuumPlugin(WebTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
if not hasattr(mod, 'WuttaWebContinuumPlugin'):
|
||||||
|
pytest.skip("test not relevant without sqlalchemy-continuum")
|
||||||
|
self.setup_web()
|
||||||
|
|
||||||
|
def make_plugin(self):
|
||||||
|
return mod.WuttaWebContinuumPlugin()
|
||||||
|
|
||||||
|
def test_get_remote_addr(self):
|
||||||
|
plugin = self.make_plugin()
|
||||||
|
|
||||||
|
with patch.object(mod, 'get_current_request', return_value=None):
|
||||||
|
self.assertIsNone(plugin.get_remote_addr(None, self.session))
|
||||||
|
|
||||||
|
self.request.client_addr = '127.0.0.1'
|
||||||
|
self.assertEqual(plugin.get_remote_addr(None, self.session), '127.0.0.1')
|
||||||
|
|
||||||
|
def test_get_user_id(self):
|
||||||
|
plugin = self.make_plugin()
|
||||||
|
|
||||||
|
with patch.object(mod, 'get_current_request', return_value=None):
|
||||||
|
self.assertIsNone(plugin.get_user_id(None, self.session))
|
||||||
|
|
||||||
|
self.request.user = MagicMock(uuid='some-random-uuid')
|
||||||
|
self.assertEqual(plugin.get_user_id(None, self.session), 'some-random-uuid')
|
|
@ -19,6 +19,9 @@ class TestObjectRefWidget(WebTestCase):
|
||||||
kwargs.setdefault('renderer', deform.Form.default_renderer)
|
kwargs.setdefault('renderer', deform.Form.default_renderer)
|
||||||
return deform.Field(node, **kwargs)
|
return deform.Field(node, **kwargs)
|
||||||
|
|
||||||
|
def make_widget(self, **kwargs):
|
||||||
|
return mod.ObjectRefWidget(self.request, **kwargs)
|
||||||
|
|
||||||
def test_serialize(self):
|
def test_serialize(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
person = model.Person(full_name="Betty Boop")
|
person = model.Person(full_name="Betty Boop")
|
||||||
|
@ -27,7 +30,7 @@ class TestObjectRefWidget(WebTestCase):
|
||||||
|
|
||||||
# standard (editable)
|
# standard (editable)
|
||||||
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
||||||
widget = mod.ObjectRefWidget(self.request)
|
widget = self.make_widget()
|
||||||
field = self.make_field(node)
|
field = self.make_field(node)
|
||||||
html = widget.serialize(field, person.uuid)
|
html = widget.serialize(field, person.uuid)
|
||||||
self.assertIn('<b-select ', html)
|
self.assertIn('<b-select ', html)
|
||||||
|
@ -35,7 +38,7 @@ class TestObjectRefWidget(WebTestCase):
|
||||||
# readonly
|
# readonly
|
||||||
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
||||||
node.model_instance = person
|
node.model_instance = person
|
||||||
widget = mod.ObjectRefWidget(self.request)
|
widget = self.make_widget()
|
||||||
field = self.make_field(node)
|
field = self.make_field(node)
|
||||||
html = widget.serialize(field, person.uuid, readonly=True)
|
html = widget.serialize(field, person.uuid, readonly=True)
|
||||||
self.assertIn('Betty Boop', html)
|
self.assertIn('Betty Boop', html)
|
||||||
|
@ -44,13 +47,26 @@ class TestObjectRefWidget(WebTestCase):
|
||||||
# with hyperlink
|
# with hyperlink
|
||||||
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
||||||
node.model_instance = person
|
node.model_instance = person
|
||||||
widget = mod.ObjectRefWidget(self.request, url=lambda p: '/foo')
|
widget = self.make_widget(url=lambda p: '/foo')
|
||||||
field = self.make_field(node)
|
field = self.make_field(node)
|
||||||
html = widget.serialize(field, person.uuid, readonly=True)
|
html = widget.serialize(field, person.uuid, readonly=True)
|
||||||
self.assertIn('Betty Boop', html)
|
self.assertIn('Betty Boop', html)
|
||||||
self.assertIn('<a', html)
|
self.assertIn('<a', html)
|
||||||
self.assertIn('href="/foo"', html)
|
self.assertIn('href="/foo"', html)
|
||||||
|
|
||||||
|
def test_get_template_values(self):
|
||||||
|
model = self.app.model
|
||||||
|
person = model.Person(full_name="Betty Boop")
|
||||||
|
self.session.add(person)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
node = colander.SchemaNode(PersonRef(self.request, session=self.session))
|
||||||
|
widget = self.make_widget()
|
||||||
|
field = self.make_field(node)
|
||||||
|
values = widget.get_template_values(field, person.uuid, {})
|
||||||
|
self.assertIn('cstruct', values)
|
||||||
|
self.assertNotIn('url', values)
|
||||||
|
|
||||||
|
|
||||||
class TestFileDownloadWidget(WebTestCase):
|
class TestFileDownloadWidget(WebTestCase):
|
||||||
|
|
||||||
|
|
|
@ -480,6 +480,27 @@ class TestGetModelFields(TestCase):
|
||||||
fields = mod.get_model_fields(self.config, model.Setting)
|
fields = mod.get_model_fields(self.config, model.Setting)
|
||||||
self.assertEqual(fields, ['name', 'value'])
|
self.assertEqual(fields, ['name', 'value'])
|
||||||
|
|
||||||
|
def test_avoid_versions(self):
|
||||||
|
model = self.app.model
|
||||||
|
|
||||||
|
mapper = MagicMock(iterate_properties = [
|
||||||
|
MagicMock(key='uuid'),
|
||||||
|
MagicMock(key='full_name'),
|
||||||
|
MagicMock(key='first_name'),
|
||||||
|
MagicMock(key='middle_name'),
|
||||||
|
MagicMock(key='last_name'),
|
||||||
|
MagicMock(key='versions'),
|
||||||
|
])
|
||||||
|
|
||||||
|
with patch.object(mod, 'sa') as sa:
|
||||||
|
sa.inspect.return_value = mapper
|
||||||
|
|
||||||
|
with patch.object(self.app, 'continuum_is_enabled', return_value=True):
|
||||||
|
fields = mod.get_model_fields(self.config, model.Person)
|
||||||
|
# nb. no versions field
|
||||||
|
self.assertEqual(set(fields), set(['uuid', 'full_name', 'first_name',
|
||||||
|
'middle_name', 'last_name']))
|
||||||
|
|
||||||
|
|
||||||
class TestGetCsrfToken(TestCase):
|
class TestGetCsrfToken(TestCase):
|
||||||
|
|
||||||
|
|
7
tox.ini
7
tox.ini
|
@ -1,11 +1,14 @@
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py38, py39, py310, py311
|
envlist = py38, py39, py310, py311, nox
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
extras = tests
|
extras = continuum,tests
|
||||||
commands = pytest {posargs}
|
commands = pytest {posargs}
|
||||||
|
|
||||||
|
[testenv:nox]
|
||||||
|
extras = tests
|
||||||
|
|
||||||
[testenv:coverage]
|
[testenv:coverage]
|
||||||
basepython = python3.11
|
basepython = python3.11
|
||||||
commands = pytest --cov=wuttaweb --cov-report=html --cov-fail-under=100
|
commands = pytest --cov=wuttaweb --cov-report=html --cov-fail-under=100
|
||||||
|
|
Loading…
Reference in a new issue