# -*- coding: utf-8; -*- import json from unittest import TestCase from unittest.mock import patch, MagicMock import colander from fanstatic import Library, Resource from pyramid import testing from wuttjamaican.conf import WuttaConfig from wuttaweb import util as mod class TestFieldList(TestCase): def test_insert_before(self): fields = mod.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 = mod.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']) def test_set_sequence(self): fields = mod.FieldList(['f5', 'f1', 'f3', 'f4', 'f2']) # setting sequence will only "sort" for explicit fields. # other fields remain in original order, but at the end. fields.set_sequence(['f1', 'f2', 'f3']) self.assertEqual(fields, ['f1', 'f2', 'f3', 'f5', 'f4']) class TestGetLibVer(TestCase): def setUp(self): self.config = WuttaConfig() self.request = testing.DummyRequest() self.request.wutta_config = self.config def test_buefy_default(self): version = mod.get_libver(self.request, 'buefy') self.assertEqual(version, 'latest') def test_buefy_custom_old(self): self.config.setdefault('wuttaweb.buefy_version', '0.9.29') version = mod.get_libver(self.request, 'buefy') self.assertEqual(version, '0.9.29') def test_buefy_custom_old_tailbone(self): self.config.setdefault('tailbone.libver.buefy', '0.9.28') version = mod.get_libver(self.request, 'buefy', prefix='tailbone') self.assertEqual(version, '0.9.28') def test_buefy_custom_new(self): self.config.setdefault('wuttaweb.libver.buefy', '0.9.29') version = mod.get_libver(self.request, 'buefy') self.assertEqual(version, '0.9.29') def test_buefy_configured_only(self): version = mod.get_libver(self.request, 'buefy', configured_only=True) self.assertIsNone(version) def test_buefy_default_only(self): self.config.setdefault('wuttaweb.libver.buefy', '0.9.29') version = mod.get_libver(self.request, 'buefy', default_only=True) self.assertEqual(version, 'latest') def test_buefy_css_default(self): version = mod.get_libver(self.request, 'buefy.css') self.assertEqual(version, 'latest') def test_buefy_css_custom_old(self): # nb. this uses same setting as buefy (js) self.config.setdefault('wuttaweb.buefy_version', '0.9.29') version = mod.get_libver(self.request, 'buefy.css') self.assertEqual(version, '0.9.29') def test_buefy_css_custom_new(self): # nb. this uses same setting as buefy (js) self.config.setdefault('wuttaweb.libver.buefy', '0.9.29') version = mod.get_libver(self.request, 'buefy.css') self.assertEqual(version, '0.9.29') def test_buefy_css_configured_only(self): version = mod.get_libver(self.request, 'buefy.css', configured_only=True) self.assertIsNone(version) def test_buefy_css_default_only(self): self.config.setdefault('wuttaweb.libver.buefy', '0.9.29') version = mod.get_libver(self.request, 'buefy.css', default_only=True) self.assertEqual(version, 'latest') def test_vue_default(self): version = mod.get_libver(self.request, 'vue') self.assertEqual(version, '2.6.14') def test_vue_custom_old(self): self.config.setdefault('wuttaweb.vue_version', '3.4.31') version = mod.get_libver(self.request, 'vue') self.assertEqual(version, '3.4.31') def test_vue_custom_new(self): self.config.setdefault('wuttaweb.libver.vue', '3.4.31') version = mod.get_libver(self.request, 'vue') self.assertEqual(version, '3.4.31') def test_vue_configured_only(self): version = mod.get_libver(self.request, 'vue', configured_only=True) self.assertIsNone(version) def test_vue_default_only(self): self.config.setdefault('wuttaweb.libver.vue', '3.4.31') version = mod.get_libver(self.request, 'vue', default_only=True) self.assertEqual(version, '2.6.14') def test_vue_resource_default(self): version = mod.get_libver(self.request, 'vue_resource') self.assertEqual(version, 'latest') def test_vue_resource_custom(self): self.config.setdefault('wuttaweb.libver.vue_resource', '1.5.3') version = mod.get_libver(self.request, 'vue_resource') self.assertEqual(version, '1.5.3') def test_fontawesome_default(self): version = mod.get_libver(self.request, 'fontawesome') self.assertEqual(version, '5.3.1') def test_fontawesome_custom(self): self.config.setdefault('wuttaweb.libver.fontawesome', '5.6.3') version = mod.get_libver(self.request, 'fontawesome') self.assertEqual(version, '5.6.3') def test_bb_vue_default(self): version = mod.get_libver(self.request, 'bb_vue') self.assertEqual(version, '3.4.31') def test_bb_vue_custom(self): self.config.setdefault('wuttaweb.libver.bb_vue', '3.4.30') version = mod.get_libver(self.request, 'bb_vue') self.assertEqual(version, '3.4.30') def test_bb_oruga_default(self): version = mod.get_libver(self.request, 'bb_oruga') self.assertEqual(version, '0.8.12') def test_bb_oruga_custom(self): self.config.setdefault('wuttaweb.libver.bb_oruga', '0.8.11') version = mod.get_libver(self.request, 'bb_oruga') self.assertEqual(version, '0.8.11') def test_bb_oruga_bulma_default(self): version = mod.get_libver(self.request, 'bb_oruga_bulma') self.assertEqual(version, '0.3.0') version = mod.get_libver(self.request, 'bb_oruga_bulma_css') self.assertEqual(version, '0.3.0') def test_bb_oruga_bulma_custom(self): self.config.setdefault('wuttaweb.libver.bb_oruga_bulma', '0.2.11') version = mod.get_libver(self.request, 'bb_oruga_bulma') self.assertEqual(version, '0.2.11') def test_bb_fontawesome_svg_core_default(self): version = mod.get_libver(self.request, 'bb_fontawesome_svg_core') self.assertEqual(version, '6.5.2') def test_bb_fontawesome_svg_core_custom(self): self.config.setdefault('wuttaweb.libver.bb_fontawesome_svg_core', '6.5.1') version = mod.get_libver(self.request, 'bb_fontawesome_svg_core') self.assertEqual(version, '6.5.1') def test_bb_free_solid_svg_icons_default(self): version = mod.get_libver(self.request, 'bb_free_solid_svg_icons') self.assertEqual(version, '6.5.2') def test_bb_free_solid_svg_icons_custom(self): self.config.setdefault('wuttaweb.libver.bb_free_solid_svg_icons', '6.5.1') version = mod.get_libver(self.request, 'bb_free_solid_svg_icons') self.assertEqual(version, '6.5.1') def test_bb_vue_fontawesome_default(self): version = mod.get_libver(self.request, 'bb_vue_fontawesome') self.assertEqual(version, '3.0.6') def test_bb_vue_fontawesome_custom(self): self.config.setdefault('wuttaweb.libver.bb_vue_fontawesome', '3.0.8') version = mod.get_libver(self.request, 'bb_vue_fontawesome') self.assertEqual(version, '3.0.8') libcache = Library('testing', 'libcache') vue_js = Resource(libcache, 'vue.js') vue_resource_js = Resource(libcache, 'vue_resource.js') buefy_js = Resource(libcache, 'buefy.js') buefy_css = Resource(libcache, 'buefy.css') fontawesome_js = Resource(libcache, 'fontawesome.js') bb_vue_js = Resource(libcache, 'bb_vue.js') bb_oruga_js = Resource(libcache, 'bb_oruga.js') bb_oruga_bulma_js = Resource(libcache, 'bb_oruga_bulma.js') bb_oruga_bulma_css = Resource(libcache, 'bb_oruga_bulma.css') bb_fontawesome_svg_core_js = Resource(libcache, 'bb_fontawesome_svg_core.js') bb_free_solid_svg_icons_js = Resource(libcache, 'bb_free_solid_svg_icons.js') bb_vue_fontawesome_js = Resource(libcache, 'bb_vue_fontawesome.js') class TestGetLibUrl(TestCase): def setUp(self): self.config = WuttaConfig() self.request = testing.DummyRequest(wutta_config=self.config) self.pyramid_config = testing.setUp(request=self.request) def tearDown(self): testing.tearDown() def setup_fanstatic(self, register=True): self.pyramid_config.include('pyramid_fanstatic') if register: self.config.setdefault('wuttaweb.static_libcache.module', 'tests.test_util') needed = MagicMock() needed.library_url = MagicMock(return_value='/fanstatic') self.request.environ['fanstatic.needed'] = needed self.request.script_name = '/wutta' def test_buefy_default(self): url = mod.get_liburl(self.request, 'buefy') self.assertEqual(url, 'https://unpkg.com/buefy@latest/dist/buefy.min.js') def test_buefy_custom(self): self.config.setdefault('wuttaweb.liburl.buefy', '/lib/buefy.js') url = mod.get_liburl(self.request, 'buefy') self.assertEqual(url, '/lib/buefy.js') def test_buefy_custom_tailbone(self): self.config.setdefault('tailbone.liburl.buefy', '/tailbone/buefy.js') url = mod.get_liburl(self.request, 'buefy', prefix='tailbone') self.assertEqual(url, '/tailbone/buefy.js') def test_buefy_default_only(self): self.config.setdefault('wuttaweb.liburl.buefy', '/lib/buefy.js') url = mod.get_liburl(self.request, 'buefy', default_only=True) self.assertEqual(url, 'https://unpkg.com/buefy@latest/dist/buefy.min.js') def test_buefy_configured_only(self): url = mod.get_liburl(self.request, 'buefy', configured_only=True) self.assertIsNone(url) def test_buefy_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'buefy') self.assertEqual(url, '/wutta/fanstatic/buefy.js') def test_buefy_fanstatic_tailbone(self): self.setup_fanstatic(register=False) self.config.setdefault('tailbone.static_libcache.module', 'tests.test_util') url = mod.get_liburl(self.request, 'buefy', prefix='tailbone') self.assertEqual(url, '/wutta/fanstatic/buefy.js') def test_buefy_css_default(self): url = mod.get_liburl(self.request, 'buefy.css') self.assertEqual(url, 'https://unpkg.com/buefy@latest/dist/buefy.min.css') def test_buefy_css_custom(self): self.config.setdefault('wuttaweb.liburl.buefy.css', '/lib/buefy.css') url = mod.get_liburl(self.request, 'buefy.css') self.assertEqual(url, '/lib/buefy.css') def test_buefy_css_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'buefy.css') self.assertEqual(url, '/wutta/fanstatic/buefy.css') def test_vue_default(self): url = mod.get_liburl(self.request, 'vue') self.assertEqual(url, 'https://unpkg.com/vue@2.6.14/dist/vue.min.js') def test_vue_custom(self): self.config.setdefault('wuttaweb.liburl.vue', '/lib/vue.js') url = mod.get_liburl(self.request, 'vue') self.assertEqual(url, '/lib/vue.js') def test_vue_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'vue') self.assertEqual(url, '/wutta/fanstatic/vue.js') def test_vue_resource_default(self): url = mod.get_liburl(self.request, 'vue_resource') self.assertEqual(url, 'https://cdn.jsdelivr.net/npm/vue-resource@latest') def test_vue_resource_custom(self): self.config.setdefault('wuttaweb.liburl.vue_resource', '/lib/vue-resource.js') url = mod.get_liburl(self.request, 'vue_resource') self.assertEqual(url, '/lib/vue-resource.js') def test_vue_resource_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'vue_resource') self.assertEqual(url, '/wutta/fanstatic/vue_resource.js') def test_fontawesome_default(self): url = mod.get_liburl(self.request, 'fontawesome') self.assertEqual(url, 'https://use.fontawesome.com/releases/v5.3.1/js/all.js') def test_fontawesome_custom(self): self.config.setdefault('wuttaweb.liburl.fontawesome', '/lib/fontawesome.js') url = mod.get_liburl(self.request, 'fontawesome') self.assertEqual(url, '/lib/fontawesome.js') def test_fontawesome_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'fontawesome') self.assertEqual(url, '/wutta/fanstatic/fontawesome.js') def test_bb_vue_default(self): url = mod.get_liburl(self.request, 'bb_vue') self.assertEqual(url, 'https://unpkg.com/vue@3.4.31/dist/vue.esm-browser.prod.js') def test_bb_vue_custom(self): self.config.setdefault('wuttaweb.liburl.bb_vue', '/lib/vue.js') url = mod.get_liburl(self.request, 'bb_vue') self.assertEqual(url, '/lib/vue.js') def test_bb_vue_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_vue') self.assertEqual(url, '/wutta/fanstatic/bb_vue.js') def test_bb_oruga_default(self): url = mod.get_liburl(self.request, 'bb_oruga') self.assertEqual(url, 'https://unpkg.com/@oruga-ui/oruga-next@0.8.12/dist/oruga.mjs') def test_bb_oruga_custom(self): self.config.setdefault('wuttaweb.liburl.bb_oruga', '/lib/oruga.js') url = mod.get_liburl(self.request, 'bb_oruga') self.assertEqual(url, '/lib/oruga.js') def test_bb_oruga_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_oruga') self.assertEqual(url, '/wutta/fanstatic/bb_oruga.js') def test_bb_oruga_bulma_default(self): url = mod.get_liburl(self.request, 'bb_oruga_bulma') self.assertEqual(url, 'https://unpkg.com/@oruga-ui/theme-bulma@0.3.0/dist/bulma.mjs') def test_bb_oruga_bulma_custom(self): self.config.setdefault('wuttaweb.liburl.bb_oruga_bulma', '/lib/oruga_bulma.js') url = mod.get_liburl(self.request, 'bb_oruga_bulma') self.assertEqual(url, '/lib/oruga_bulma.js') def test_bb_oruga_bulma_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_oruga_bulma') self.assertEqual(url, '/wutta/fanstatic/bb_oruga_bulma.js') def test_bb_oruga_bulma_css_default(self): url = mod.get_liburl(self.request, 'bb_oruga_bulma_css') self.assertEqual(url, 'https://unpkg.com/@oruga-ui/theme-bulma@0.3.0/dist/bulma.css') def test_bb_oruga_bulma_css_custom(self): self.config.setdefault('wuttaweb.liburl.bb_oruga_bulma_css', '/lib/oruga-bulma.css') url = mod.get_liburl(self.request, 'bb_oruga_bulma_css') self.assertEqual(url, '/lib/oruga-bulma.css') def test_bb_oruga_bulma_css_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_oruga_bulma_css') self.assertEqual(url, '/wutta/fanstatic/bb_oruga_bulma.css') def test_bb_fontawesome_svg_core_default(self): url = mod.get_liburl(self.request, 'bb_fontawesome_svg_core') self.assertEqual(url, 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-svg-core@6.5.2/+esm') def test_bb_fontawesome_svg_core_custom(self): self.config.setdefault('wuttaweb.liburl.bb_fontawesome_svg_core', '/lib/fontawesome-svg-core.js') url = mod.get_liburl(self.request, 'bb_fontawesome_svg_core') self.assertEqual(url, '/lib/fontawesome-svg-core.js') def test_bb_fontawesome_svg_core_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_fontawesome_svg_core') self.assertEqual(url, '/wutta/fanstatic/bb_fontawesome_svg_core.js') def test_bb_free_solid_svg_icons_default(self): url = mod.get_liburl(self.request, 'bb_free_solid_svg_icons') self.assertEqual(url, 'https://cdn.jsdelivr.net/npm/@fortawesome/free-solid-svg-icons@6.5.2/+esm') def test_bb_free_solid_svg_icons_custom(self): self.config.setdefault('wuttaweb.liburl.bb_free_solid_svg_icons', '/lib/free-solid-svg-icons.js') url = mod.get_liburl(self.request, 'bb_free_solid_svg_icons') self.assertEqual(url, '/lib/free-solid-svg-icons.js') def test_bb_free_solid_svg_icons_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_free_solid_svg_icons') self.assertEqual(url, '/wutta/fanstatic/bb_free_solid_svg_icons.js') def test_bb_vue_fontawesome_default(self): url = mod.get_liburl(self.request, 'bb_vue_fontawesome') self.assertEqual(url, 'https://cdn.jsdelivr.net/npm/@fortawesome/vue-fontawesome@3.0.6/+esm') def test_bb_vue_fontawesome_custom(self): self.config.setdefault('wuttaweb.liburl.bb_vue_fontawesome', '/lib/vue-fontawesome.js') url = mod.get_liburl(self.request, 'bb_vue_fontawesome') self.assertEqual(url, '/lib/vue-fontawesome.js') def test_bb_vue_fontawesome_fanstatic(self): self.setup_fanstatic() url = mod.get_liburl(self.request, 'bb_vue_fontawesome') self.assertEqual(url, '/wutta/fanstatic/bb_vue_fontawesome.js') class TestGetFormData(TestCase): def setUp(self): self.config = WuttaConfig() def make_request(self, **kwargs): kwargs.setdefault('wutta_config', self.config) kwargs.setdefault('POST', {'foo1': 'bar'}) kwargs.setdefault('json_body', {'foo2': 'baz'}) return testing.DummyRequest(**kwargs) def test_default(self): request = self.make_request() data = mod.get_form_data(request) self.assertEqual(data, {'foo1': 'bar'}) def test_is_xhr(self): request = self.make_request(POST=None, is_xhr=True) data = mod.get_form_data(request) self.assertEqual(data, {'foo2': 'baz'}) def test_content_type(self): request = self.make_request(POST=None, content_type='application/json') data = mod.get_form_data(request) self.assertEqual(data, {'foo2': 'baz'}) class TestGetModelFields(TestCase): def setUp(self): self.config = WuttaConfig() self.app = self.config.get_app() def test_empty_model_class(self): fields = mod.get_model_fields(self.config) self.assertIsNone(fields) def test_unknown_model_class(self): fields = mod.get_model_fields(self.config, TestCase) self.assertIsNone(fields) def test_basic(self): model = self.app.model fields = mod.get_model_fields(self.config, model.Setting) 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): def setUp(self): self.config = WuttaConfig() self.request = testing.DummyRequest(wutta_config=self.config) def test_same_token(self): # same token returned for same request # TODO: dummy request is always returning same token! # so this isn't really testing anything.. :( first = mod.get_csrf_token(self.request) self.assertIsNotNone(first) second = mod.get_csrf_token(self.request) self.assertEqual(first, second) # TODO: ideally would make a new request here and confirm it # gets a different token, but see note above.. def test_new_token(self): # nb. dummy request always returns same token, so must # trick it into thinking it doesn't have one yet with patch.object(self.request.session, 'get_csrf_token', return_value=None): token = mod.get_csrf_token(self.request) self.assertIsNotNone(token) class TestRenderCsrfToken(TestCase): def setUp(self): self.config = WuttaConfig() self.request = testing.DummyRequest(wutta_config=self.config) def test_basics(self): html = mod.render_csrf_token(self.request) self.assertIn('type="hidden"', html) self.assertIn('name="_csrf"', html) token = mod.get_csrf_token(self.request) self.assertIn(f'value="{token}"', html) class TestMakeJsonSafe(TestCase): def setUp(self): self.config = WuttaConfig() self.app = self.config.get_app() def test_null(self): value = mod.make_json_safe(colander.null) self.assertIsNone(value) value = mod.make_json_safe(None) self.assertIsNone(value) def test_invalid(self): model = self.app.model person = model.Person(full_name="Betty Boop") self.assertRaises(TypeError, json.dumps, person) value = mod.make_json_safe(person, key='person') self.assertEqual(value, "Betty Boop") def test_dict(self): model = self.app.model person = model.Person(full_name="Betty Boop") data = { 'foo': 'bar', 'person': person, } self.assertRaises(TypeError, json.dumps, data) value = mod.make_json_safe(data) self.assertEqual(value, { 'foo': 'bar', 'person': "Betty Boop", })