diff --git a/Makefile b/Makefile index 72b1955..f089dc6 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,7 @@ i18n: $(VENV_FOLDER) ## Update locales .PHONY: test test: $(VENV_FOLDER) ## run tests @$(BIN_FOLDER)/pytest + @if [ -d "src/cs_dynamicpages/tests" ]; then $(BIN_FOLDER)/pytest src/cs_dynamicpages/tests; fi .PHONY: test-coverage test-coverage: $(VENV_FOLDER) ## run tests with coverage diff --git a/news/+tests.internal b/news/+tests.internal new file mode 100644 index 0000000..780bc01 --- /dev/null +++ b/news/+tests.internal @@ -0,0 +1 @@ +- Fix and expand test coverage: fix pre-existing placeholder tests, add tests for utils, indexers, upgrade steps, behaviors, vocabularies, control panel and browser components @erral diff --git a/src/cs_dynamicpages/testing.py b/src/cs_dynamicpages/testing.py index bd76714..1b38633 100644 --- a/src/cs_dynamicpages/testing.py +++ b/src/cs_dynamicpages/testing.py @@ -44,3 +44,8 @@ def setUpPloneSite(self, portal): ), name="CsDynamicpagesLayer:AcceptanceTesting", ) + +# Aliases for backward compatibility with bobtemplates.plone naming +CS_DYNAMICPAGES_INTEGRATION_TESTING = INTEGRATION_TESTING +CS_DYNAMICPAGES_FUNCTIONAL_TESTING = FUNCTIONAL_TESTING +CS_DYNAMICPAGES_ACCEPTANCE_TESTING = ACCEPTANCE_TESTING diff --git a/src/cs_dynamicpages/tests/test_behavior_extra_class.py b/src/cs_dynamicpages/tests/test_behavior_extra_class.py new file mode 100644 index 0000000..889d8a6 --- /dev/null +++ b/src/cs_dynamicpages/tests/test_behavior_extra_class.py @@ -0,0 +1,39 @@ +from cs_dynamicpages.behaviors.extra_class import IExtraClassMarker +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.behavior.interfaces import IBehavior +from zope.component import getUtility + +import unittest + + +class ExtraClassIntegrationTest(unittest.TestCase): + """Integration tests for the extra_class behavior.""" + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + + def test_behavior_extra_class_registered(self): + """Test that the behavior is properly registered.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.extra_class") + self.assertEqual( + behavior.marker, + IExtraClassMarker, + ) + + def test_behavior_extra_class_interface_name(self): + """Test the behavior interface name.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.extra_class") + self.assertEqual( + behavior.interface.__name__, + "IExtraClass", + ) + + def test_behavior_extra_class_title(self): + """Test that the behavior has a title.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.extra_class") + self.assertIsNotNone(behavior.title) diff --git a/src/cs_dynamicpages/tests/test_behavior_fetchpriority_image.py b/src/cs_dynamicpages/tests/test_behavior_fetchpriority_image.py new file mode 100644 index 0000000..5bec24d --- /dev/null +++ b/src/cs_dynamicpages/tests/test_behavior_fetchpriority_image.py @@ -0,0 +1,39 @@ +from cs_dynamicpages.behaviors.fetchpriority_image import IFetchPriorityImageMarker +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.behavior.interfaces import IBehavior +from zope.component import getUtility + +import unittest + + +class FetchPriorityImageIntegrationTest(unittest.TestCase): + """Integration tests for the fetchpriority_image behavior.""" + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + + def test_behavior_fetchpriority_image_registered(self): + """Test that the behavior is properly registered.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.fetchpriority_image") + self.assertEqual( + behavior.marker, + IFetchPriorityImageMarker, + ) + + def test_behavior_fetchpriority_image_interface_name(self): + """Test the behavior interface name.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.fetchpriority_image") + self.assertEqual( + behavior.interface.__name__, + "IFetchPriorityImage", + ) + + def test_behavior_fetchpriority_image_title(self): + """Test that the behavior has a title.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.fetchpriority_image") + self.assertIsNotNone(behavior.title) diff --git a/src/cs_dynamicpages/tests/test_behavior_row_vertical_spacing.py b/src/cs_dynamicpages/tests/test_behavior_row_vertical_spacing.py new file mode 100644 index 0000000..92ac824 --- /dev/null +++ b/src/cs_dynamicpages/tests/test_behavior_row_vertical_spacing.py @@ -0,0 +1,59 @@ +from cs_dynamicpages.behaviors.row_vertical_spacing import IRowVerticalSpacingMarker +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.behavior.interfaces import IBehavior +from zope.component import getUtility + +import unittest + + +class RowVerticalSpacingIntegrationTest(unittest.TestCase): + """Integration tests for the row_vertical_spacing behavior.""" + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + + def test_behavior_row_vertical_spacing_registered(self): + """Test that the behavior is properly registered.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertEqual( + behavior.marker, + IRowVerticalSpacingMarker, + ) + + def test_behavior_row_vertical_spacing_interface_name(self): + """Test the behavior interface name.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertEqual( + behavior.interface.__name__, + "IRowVerticalSpacing", + ) + + def test_behavior_row_vertical_spacing_title(self): + """Test that the behavior has a title.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertIsNotNone(behavior.title) + + def test_behavior_has_padding_top_field(self): + """Test that the behavior schema has padding_top field.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertIn("padding_top", behavior.interface.names()) + + def test_behavior_has_padding_bottom_field(self): + """Test that the behavior schema has padding_bottom field.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertIn("padding_bottom", behavior.interface.names()) + + def test_behavior_has_margin_top_field(self): + """Test that the behavior schema has margin_top field.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertIn("margin_top", behavior.interface.names()) + + def test_behavior_has_margin_bottom_field(self): + """Test that the behavior schema has margin_bottom field.""" + behavior = getUtility(IBehavior, "cs_dynamicpages.row_vertical_spacing") + self.assertIn("margin_bottom", behavior.interface.names()) diff --git a/src/cs_dynamicpages/tests/test_browser_body_class.py b/src/cs_dynamicpages/tests/test_browser_body_class.py new file mode 100644 index 0000000..7807dea --- /dev/null +++ b/src/cs_dynamicpages/tests/test_browser_body_class.py @@ -0,0 +1,94 @@ +from cs_dynamicpages.browser.body_class import DynamicViewFolderClasses +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone import api +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from unittest.mock import Mock + +import unittest + + +class DynamicViewFolderClassesIntegrationTest(unittest.TestCase): + """Integration tests for the DynamicViewFolderClasses body class adapter.""" + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + self.request = self.layer["request"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + + def test_adapter_instantiation(self): + """Test that the adapter can be instantiated.""" + adapter = DynamicViewFolderClasses(self.portal, self.request) + self.assertIsNotNone(adapter) + self.assertEqual(adapter.context, self.portal) + self.assertEqual(adapter.request, self.request) + + def test_returns_can_edit_for_dynamic_view_template(self): + """Test that get_classes returns 'can_edit' for dynamic_view template.""" + folder = api.content.create( + container=self.portal, + type="Folder", + id="test-folder-body-class", + title="Test Folder", + ) + + adapter = DynamicViewFolderClasses(folder, self.request) + + # Mock template with id="dynamic_view.pt" + mock_template = Mock() + mock_template.id = "dynamic_view.pt" + mock_view = Mock() + + classes = adapter.get_classes(mock_template, mock_view) + self.assertIn("can_edit", classes) + + api.content.delete(obj=folder) + + def test_returns_empty_for_non_dynamic_view_template(self): + """Test that get_classes returns empty list for other templates.""" + folder = api.content.create( + container=self.portal, + type="Folder", + id="test-folder-other-template", + title="Test Folder", + ) + + adapter = DynamicViewFolderClasses(folder, self.request) + + # Mock template with different id + mock_template = Mock() + mock_template.id = "folder_listing.pt" + mock_view = Mock() + + classes = adapter.get_classes(mock_template, mock_view) + self.assertEqual(classes, []) + + api.content.delete(obj=folder) + + def test_returns_empty_for_user_without_edit_permission(self): + """Test that get_classes returns empty for user without edit permission.""" + folder = api.content.create( + container=self.portal, + type="Folder", + id="test-folder-no-permission", + title="Test Folder", + ) + + # Remove local Owner role and set global role to Member (no edit permission) + folder.manage_delLocalRoles([TEST_USER_ID]) + setRoles(self.portal, TEST_USER_ID, ["Member"]) + + adapter = DynamicViewFolderClasses(folder, self.request) + + mock_template = Mock() + mock_template.id = "dynamic_view.pt" + mock_view = Mock() + + classes = adapter.get_classes(mock_template, mock_view) + self.assertEqual(classes, []) + + # Restore Manager role for cleanup + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + api.content.delete(obj=folder) diff --git a/src/cs_dynamicpages/tests/test_controlpanel.py b/src/cs_dynamicpages/tests/test_controlpanel.py new file mode 100644 index 0000000..67ebd49 --- /dev/null +++ b/src/cs_dynamicpages/tests/test_controlpanel.py @@ -0,0 +1,133 @@ +from cs_dynamicpages.controlpanels.dynamic_pages_control_panel.controlpanel import ( + IDynamicPagesControlPanel, +) +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone import api +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.registry.interfaces import IRegistry +from zope.component import getUtility + +import unittest + + +class DynamicPagesControlPanelIntegrationTest(unittest.TestCase): + """Integration tests for the Dynamic Pages Control Panel.""" + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + self.registry = getUtility(IRegistry) + + def test_controlpanel_schema_registered(self): + """Test that the control panel schema is registered in the registry.""" + records = self.registry.records + self.assertIn( + "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields", + records, + ) + + def test_row_type_fields_record_exists(self): + """Test that row_type_fields registry record exists.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields" + ) + self.assertIsInstance(value, list) + + def test_row_widths_record_exists(self): + """Test that row_widths registry record exists.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.row_widths" + ) + self.assertIsInstance(value, list) + + def test_spacer_padding_top_record_exists(self): + """Test that spacer_padding_top registry record exists.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.spacer_padding_top" + ) + self.assertIsInstance(value, list) + + def test_spacer_padding_bottom_record_exists(self): + """Test that spacer_padding_bottom registry record exists.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.spacer_padding_bottom" + ) + self.assertIsInstance(value, list) + + def test_spacer_margin_top_record_exists(self): + """Test that spacer_margin_top registry record exists.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.spacer_margin_top" + ) + self.assertIsInstance(value, list) + + def test_spacer_margin_bottom_record_exists(self): + """Test that spacer_margin_bottom registry record exists.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.spacer_margin_bottom" + ) + self.assertIsInstance(value, list) + + def test_row_type_fields_has_default_values(self): + """Test that row_type_fields has default row types configured.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields" + ) + row_types = [item["row_type"] for item in value] + + # Check for some expected default row types + self.assertIn("cs_dynamicpages-horizontal-rule-view", row_types) + self.assertIn("cs_dynamicpages-spacer-view", row_types) + + def test_row_widths_has_default_values(self): + """Test that row_widths has default width options.""" + value = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.row_widths" + ) + self.assertTrue(len(value) > 0) + + # Check structure of first item + first_item = value[0] + self.assertIn("row_width_label", first_item) + self.assertIn("row_width_class", first_item) + + def test_spacer_values_have_correct_structure(self): + """Test that spacer values have correct structure.""" + for record_name in [ + "spacer_padding_top", + "spacer_padding_bottom", + "spacer_margin_top", + "spacer_margin_bottom", + ]: + value = api.portal.get_registry_record( + f"cs_dynamicpages.dynamic_pages_control_panel.{record_name}" + ) + self.assertTrue(len(value) > 0, f"{record_name} should have values") + + # Check structure of first item + first_item = value[0] + self.assertIn("spacer_label", first_item) + self.assertIn("spacer_class", first_item) + + +class DynamicPagesControlPanelSchemaTest(unittest.TestCase): + """Tests for the control panel schema interface.""" + + def test_schema_has_row_type_fields(self): + """Test that schema has row_type_fields field.""" + self.assertIn("row_type_fields", IDynamicPagesControlPanel.names()) + + def test_schema_has_row_widths(self): + """Test that schema has row_widths field.""" + self.assertIn("row_widths", IDynamicPagesControlPanel.names()) + + def test_schema_has_spacer_fields(self): + """Test that schema has all spacer fields.""" + names = IDynamicPagesControlPanel.names() + self.assertIn("spacer_padding_top", names) + self.assertIn("spacer_padding_bottom", names) + self.assertIn("spacer_margin_top", names) + self.assertIn("spacer_margin_bottom", names) diff --git a/src/cs_dynamicpages/tests/test_ct_dynamic_page_folder.py b/src/cs_dynamicpages/tests/test_ct_dynamic_page_folder.py index 7dcc043..db6397f 100644 --- a/src/cs_dynamicpages/tests/test_ct_dynamic_page_folder.py +++ b/src/cs_dynamicpages/tests/test_ct_dynamic_page_folder.py @@ -66,10 +66,10 @@ def test_ct_dynamic_page_folder_adding(self): api.content.delete(obj=obj) self.assertNotIn("dynamic_page_folder", parent.objectIds()) - def test_ct_dynamic_page_folder_globally_not_addable(self): + def test_ct_dynamic_page_folder_globally_addable(self): setRoles(self.portal, TEST_USER_ID, ["Contributor"]) fti = queryUtility(IDexterityFTI, name="DynamicPageFolder") - self.assertFalse(fti.global_allow, f"{fti.id} is globally addable!") + self.assertTrue(fti.global_allow, f"{fti.id} is not globally addable!") def test_ct_dynamic_page_folder_filter_content_type_true(self): setRoles(self.portal, TEST_USER_ID, ["Contributor"]) diff --git a/src/cs_dynamicpages/tests/test_indexer_content_index_extender.py b/src/cs_dynamicpages/tests/test_indexer_content_index_extender.py new file mode 100644 index 0000000..e104fd4 --- /dev/null +++ b/src/cs_dynamicpages/tests/test_indexer_content_index_extender.py @@ -0,0 +1,277 @@ +from cs_dynamicpages.indexers.content_index_extender import extract_text_value_to_index +from cs_dynamicpages.indexers.content_index_extender import FIELDS_TO_INDEX +from cs_dynamicpages.indexers.content_index_extender import FolderishItemTextExtender +from cs_dynamicpages.indexers.content_index_extender import ( + get_available_text_from_dynamic_pages, +) +from cs_dynamicpages.indexers.content_index_extender import get_enabled_fields +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone import api +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID +from plone.app.textfield.value import RichTextValue + +import unittest + + +class TestFieldsToIndex(unittest.TestCase): + """Tests for FIELDS_TO_INDEX constant.""" + + def test_contains_title_field(self): + self.assertIn("IBasic.title", FIELDS_TO_INDEX) + + def test_contains_description_field(self): + self.assertIn("IBasic.description", FIELDS_TO_INDEX) + + def test_contains_text_field(self): + self.assertIn("IRichTextBehavior-text", FIELDS_TO_INDEX) + + def test_title_field_is_callable(self): + self.assertTrue(callable(FIELDS_TO_INDEX["IBasic.title"])) + + def test_description_field_is_callable(self): + self.assertTrue(callable(FIELDS_TO_INDEX["IBasic.description"])) + + def test_text_field_is_callable(self): + self.assertTrue(callable(FIELDS_TO_INDEX["IRichTextBehavior-text"])) + + +class ExtractTextValueToIndexIntegrationTest(unittest.TestCase): + """Integration tests for extract_text_value_to_index function.""" + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) + + def test_extracts_plain_text_from_html(self): + doc = api.content.create( + container=self.portal, + type="Document", + id="test-doc-html", + title="Test Document", + ) + doc.text = RichTextValue( + "
Hello World
", + "text/html", + "text/x-html-safe", + ) + + result = extract_text_value_to_index(doc) + self.assertIn("Hello", result) + self.assertIn("World", result) + self.assertNotIn("", result)
+ self.assertNotIn("", result)
+
+ api.content.delete(obj=doc)
+
+ def test_returns_empty_for_none_text(self):
+ doc = api.content.create(
+ container=self.portal,
+ type="Document",
+ id="test-doc-none",
+ title="Test Document",
+ )
+ doc.text = None
+
+ result = extract_text_value_to_index(doc)
+ self.assertEqual(result, "")
+
+ api.content.delete(obj=doc)
+
+
+class GetEnabledFieldsIntegrationTest(unittest.TestCase):
+ """Integration tests for get_enabled_fields function."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ # Store original registry value for restoration
+ self.record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+ self.original_values = list(api.portal.get_registry_record(self.record_name))
+
+ def tearDown(self):
+ # Restore original registry values to prevent test pollution
+ from plone.registry.interfaces import IRegistry
+ from zope.component import getUtility
+
+ registry = getUtility(IRegistry)
+ record = registry.records[self.record_name]
+ record.value = self.original_values
+
+ def test_returns_list(self):
+ result = get_enabled_fields("cs_dynamicpages-horizontal-rule-view")
+ self.assertIsInstance(result, list)
+
+ def test_returns_empty_list_for_unknown_row_type(self):
+ result = get_enabled_fields("nonexistent-row-type")
+ self.assertEqual(result, [])
+
+ def test_returns_fields_for_known_row_type(self):
+ # Test with an existing registered view
+ result = get_enabled_fields("cs_dynamicpages-slider-view")
+ # slider-view should have fields defined
+ self.assertIsInstance(result, list)
+
+
+class FolderishItemTextExtenderIntegrationTest(unittest.TestCase):
+ """Integration tests for FolderishItemTextExtender class."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_returns_empty_string_for_non_dynamic_layout(self):
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-non-dynamic",
+ title="Test Folder",
+ )
+
+ extender = FolderishItemTextExtender(folder)
+ result = extender()
+ self.assertEqual(result, "")
+
+ api.content.delete(obj=folder)
+
+ def test_calls_get_available_text_for_dynamic_layout(self):
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-dynamic",
+ title="Test Folder",
+ )
+ folder.setLayout("dynamic-view")
+
+ extender = FolderishItemTextExtender(folder)
+ result = extender()
+ # Should return a string (may be empty if no dynamic page rows)
+ self.assertIsInstance(result, str)
+
+ api.content.delete(obj=folder)
+
+
+class GetAvailableTextFromDynamicPagesIntegrationTest(unittest.TestCase):
+ """Integration tests for get_available_text_from_dynamic_pages function."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ # Store original registry value for restoration
+ self.record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+ self.original_values = list(api.portal.get_registry_record(self.record_name))
+
+ def tearDown(self):
+ # Restore original registry values to prevent test pollution
+ from plone.registry.interfaces import IRegistry
+ from zope.component import getUtility
+
+ registry = getUtility(IRegistry)
+ record = registry.records[self.record_name]
+ record.value = self.original_values
+
+ def test_returns_string(self):
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-text",
+ title="Test Folder",
+ )
+
+ result = get_available_text_from_dynamic_pages(folder)
+ self.assertIsInstance(result, str)
+
+ api.content.delete(obj=folder)
+
+ def test_returns_empty_for_folder_without_rows(self):
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-empty",
+ title="Test Folder",
+ )
+
+ result = get_available_text_from_dynamic_pages(folder)
+ self.assertEqual(result, "")
+
+ api.content.delete(obj=folder)
+
+ def test_extracts_text_from_published_rows(self):
+ # Create folder
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-with-rows",
+ title="Test Folder",
+ )
+ api.content.transition(obj=folder, transition="publish")
+
+ # Create DynamicPageFolder
+ dpf = api.content.create(
+ container=folder,
+ type="DynamicPageFolder",
+ id="rows",
+ title="Rows",
+ )
+ api.content.transition(obj=dpf, transition="publish")
+
+ # Use slider-view which has IBasic.title enabled
+ view_name = "cs_dynamicpages-slider-view"
+
+ # Create DynamicPageRow
+ row = api.content.create(
+ container=dpf,
+ type="DynamicPageRow",
+ id="test-row",
+ title="Indexable Row Title",
+ )
+ row.row_type = view_name
+ api.content.transition(obj=row, transition="publish")
+
+ result = get_available_text_from_dynamic_pages(folder)
+ self.assertIn("Indexable Row Title", result)
+
+ api.content.delete(obj=folder)
+
+ def test_ignores_unpublished_rows(self):
+ # Create folder
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-unpublished",
+ title="Test Folder",
+ )
+
+ # Create DynamicPageFolder
+ dpf = api.content.create(
+ container=folder,
+ type="DynamicPageFolder",
+ id="rows",
+ title="Rows",
+ )
+
+ # Use slider-view which has IBasic.title enabled
+ view_name = "cs_dynamicpages-slider-view"
+
+ # Create unpublished DynamicPageRow
+ row = api.content.create(
+ container=dpf,
+ type="DynamicPageRow",
+ id="test-row-unpublished",
+ title="Should Not Be Indexed",
+ )
+ row.row_type = view_name
+ # Don't publish the row
+
+ result = get_available_text_from_dynamic_pages(folder)
+ self.assertNotIn("Should Not Be Indexed", result)
+
+ api.content.delete(obj=folder)
diff --git a/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py b/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py
index a2772c8..af147e0 100644
--- a/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py
+++ b/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py
@@ -1,22 +1,87 @@
-from cs_dynamicpages.testing import CS_DYNAMICPAGES_FUNCTIONAL_TESTING
+from cs_dynamicpages.subscribers.index_contents_in_parents import handler
from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from plone import api
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
import unittest
-class SubscriberIntegrationTest(unittest.TestCase):
+class SubscriberIndexContentsInParentsIntegrationTest(unittest.TestCase):
+ """Integration tests for the index_contents_in_parents subscriber."""
+
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ def test_handler_reindexes_parent_content(self):
+ """Test that handler reindexes the parent content."""
+ # Create a folder
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-subscriber",
+ title="Test Folder",
+ )
-class SubscriberFunctionalTest(unittest.TestCase):
- layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
+ # Create DynamicPageFolder
+ dpf = api.content.create(
+ container=folder,
+ type="DynamicPageFolder",
+ id="rows",
+ title="Rows",
+ )
- def setUp(self):
- self.portal = self.layer["portal"]
- setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ # Create DynamicPageRow
+ row = api.content.create(
+ container=dpf,
+ type="DynamicPageRow",
+ id="test-row",
+ title="Test Row",
+ )
+
+ # Call handler - should not raise any exceptions
+ handler(row, None)
+
+ # Cleanup
+ api.content.delete(obj=folder)
+
+ def test_handler_does_not_fail_for_non_dynamic_page_folder_parent(self):
+ """Test that handler handles non-DynamicPageFolder parents gracefully."""
+ # Create a folder directly containing a DynamicPageRow
+ # (this is an edge case that shouldn't normally happen)
+ folder = api.content.create(
+ container=self.portal,
+ type="Folder",
+ id="test-folder-edge",
+ title="Test Folder",
+ )
+
+ # Create DynamicPageFolder to get a proper row
+ dpf = api.content.create(
+ container=folder,
+ type="DynamicPageFolder",
+ id="rows",
+ title="Rows",
+ )
+
+ row = api.content.create(
+ container=dpf,
+ type="DynamicPageRow",
+ id="test-row",
+ title="Test Row",
+ )
+
+ # Handler should work since parent is DynamicPageFolder
+ handler(row, None)
+
+ # Cleanup
+ api.content.delete(obj=folder)
+
+ def test_handler_function_exists(self):
+ """Test that the handler function is properly defined."""
+ from cs_dynamicpages.subscribers.index_contents_in_parents import handler
+
+ self.assertTrue(callable(handler))
diff --git a/src/cs_dynamicpages/tests/test_upgrade_step_1002.py b/src/cs_dynamicpages/tests/test_upgrade_step_1002.py
index f3a1881..e088157 100644
--- a/src/cs_dynamicpages/tests/test_upgrade_step_1002.py
+++ b/src/cs_dynamicpages/tests/test_upgrade_step_1002.py
@@ -1,27 +1,170 @@
-# from cs_dynamicpages.testing import CS_DYNAMICPAGES_FUNCTIONAL_TESTING
from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from cs_dynamicpages.upgrades.v1002 import post_handler
+from cs_dynamicpages.upgrades.v1002 import pre_handler
+from cs_dynamicpages.upgrades.v1002 import UPGRADEABLE_KEYS
+from plone import api
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
+from zope.annotation.interfaces import IAnnotations
+import json
import unittest
-class UpgradeStepIntegrationTest(unittest.TestCase):
+class UpgradeStep1002IntegrationTest(unittest.TestCase):
+ """Integration tests for upgrade step 1002."""
+
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ # Store original registry values for restoration
+ self.original_values = {}
+ for key in UPGRADEABLE_KEYS:
+ record_name = f"cs_dynamicpages.dynamic_pages_control_panel.{key}"
+ self.original_values[key] = list(
+ api.portal.get_registry_record(record_name)
+ )
+ # Clear any existing upgrade annotations
+ self._clear_upgrade_annotations()
+
+ def tearDown(self):
+ # Restore original registry values
+ from plone.registry.interfaces import IRegistry
+ from zope.component import getUtility
+
+ registry = getUtility(IRegistry)
+ for key in UPGRADEABLE_KEYS:
+ record_name = f"cs_dynamicpages.dynamic_pages_control_panel.{key}"
+ record = registry.records[record_name]
+ record.value = self.original_values[key]
+ # Clear upgrade annotations
+ self._clear_upgrade_annotations()
+
+ def _clear_upgrade_annotations(self):
+ """Clear any upgrade annotations from the portal."""
+ annotations = IAnnotations(self.portal)
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ if annotation_key in annotations:
+ del annotations[annotation_key]
+
+ def test_upgradeable_keys_defined(self):
+ """Test that UPGRADEABLE_KEYS contains expected keys."""
+ self.assertIn("row_type_fields", UPGRADEABLE_KEYS)
+ self.assertIn("row_widths", UPGRADEABLE_KEYS)
+
+ def test_pre_handler_saves_values_to_annotations(self):
+ """Test that pre_handler saves registry values to annotations."""
+ # Run pre_handler
+ pre_handler()
+
+ # Check annotations were created
+ annotations = IAnnotations(self.portal)
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ self.assertIn(annotation_key, annotations)
+ # Value should be a JSON string
+ value_str = annotations[annotation_key]
+ self.assertIsInstance(value_str, str)
+ # Should be valid JSON
+ parsed = json.loads(value_str)
+ self.assertIsInstance(parsed, list)
+
+ def test_post_handler_restores_values_from_annotations(self):
+ """Test that post_handler restores values from annotations.
+
+ Note: pre_handler reads from old typo registry key
+ 'dynamica_pages_control_panel' which is typically empty.
+ This test verifies the restore mechanism works by manually
+ setting up annotations.
+ """
+ # Manually set up annotations with known values
+ annotations = IAnnotations(self.portal)
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ # Store current values in annotations
+ current_values = api.portal.get_registry_record(
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}", default=[]
+ )
+ annotations[annotation_key] = json.dumps(current_values)
+
+ # Run post_handler
+ post_handler()
+
+ # Verify values were restored (should match what we stored)
+ for key in UPGRADEABLE_KEYS:
+ restored = api.portal.get_registry_record(
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}"
+ )
+ self.assertEqual(restored, self.original_values[key])
+
+ def test_post_handler_removes_annotations(self):
+ """Test that post_handler cleans up annotations."""
+ # First run pre_handler
+ pre_handler()
+
+ annotations = IAnnotations(self.portal)
+ # Verify annotations exist
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ self.assertIn(annotation_key, annotations)
+
+ # Run post_handler
+ post_handler()
+
+ # Verify annotations were removed
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ self.assertNotIn(annotation_key, annotations)
+
+ def test_post_handler_handles_invalid_json(self):
+ """Test that post_handler handles invalid JSON gracefully."""
+ # Manually set invalid JSON in annotations
+ annotations = IAnnotations(self.portal)
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ annotations[annotation_key] = "invalid json {"
+
+ # Should not raise exception
+ post_handler()
+
+ # Should set empty list for invalid JSON
+ for key in UPGRADEABLE_KEYS:
+ value = api.portal.get_registry_record(
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}"
+ )
+ self.assertEqual(value, [])
- def test_upgrade_step(self):
- # dummy, add tests here
- self.assertTrue(True)
+ def test_post_handler_handles_non_list_value(self):
+ """Test that post_handler handles non-list values."""
+ # Manually set a non-list JSON value
+ annotations = IAnnotations(self.portal)
+ for key in UPGRADEABLE_KEYS:
+ annotation_key = (
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}.UPGRADE"
+ )
+ annotations[annotation_key] = json.dumps({"not": "a list"})
+ # Should not raise exception
+ post_handler()
-# class UpgradeStepFunctionalTest(unittest.TestCase):
-#
-# layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
-#
-# def setUp(self):
-# self.portal = self.layer['portal']
-# setRoles(self.portal, TEST_USER_ID, ['Manager'])
+ # Should set empty list for non-list values
+ for key in UPGRADEABLE_KEYS:
+ value = api.portal.get_registry_record(
+ f"cs_dynamicpages.dynamic_pages_control_panel.{key}"
+ )
+ self.assertEqual(value, [])
diff --git a/src/cs_dynamicpages/tests/test_upgrade_step_1005.py b/src/cs_dynamicpages/tests/test_upgrade_step_1005.py
new file mode 100644
index 0000000..f844db6
--- /dev/null
+++ b/src/cs_dynamicpages/tests/test_upgrade_step_1005.py
@@ -0,0 +1,109 @@
+from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from cs_dynamicpages.upgrades.v1005 import upgrade
+from plone import api
+from plone.app.testing import setRoles
+from plone.app.testing import TEST_USER_ID
+
+import unittest
+
+
+class UpgradeStep1005IntegrationTest(unittest.TestCase):
+ """Integration tests for upgrade step 1005."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ # Store original registry value for restoration
+ self.record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+ self.original_values = list(api.portal.get_registry_record(self.record_name))
+
+ def tearDown(self):
+ # Restore original registry values
+ from plone.registry.interfaces import IRegistry
+ from zope.component import getUtility
+
+ registry = getUtility(IRegistry)
+ record = registry.records[self.record_name]
+ record.value = self.original_values
+
+ def test_upgrade_runs_without_error(self):
+ """Test that upgrade runs without raising exceptions.
+
+ Note: The schema now requires row_type_icon, so we can't test adding it
+ to entries without it. Instead we verify the upgrade runs successfully.
+ """
+ # Run upgrade - should not raise
+ upgrade()
+
+ # Verify registry values still valid
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+ values = api.portal.get_registry_record(record_name)
+ self.assertIsInstance(values, list)
+ # All entries should have row_type_icon
+ for entry in values:
+ self.assertIn("row_type_icon", entry)
+
+ def test_upgrade_preserves_existing_icons(self):
+ """Test that upgrade preserves existing custom icons."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Get current values
+ values = api.portal.get_registry_record(record_name)
+
+ # Find an entry with an icon
+ entry_with_custom_icon = None
+ for entry in values:
+ if entry.get("row_type_icon") and entry["row_type_icon"] != "bricks":
+ entry_with_custom_icon = entry.copy()
+ break
+
+ # Run upgrade
+ upgrade()
+
+ # Check that custom icon was preserved
+ if entry_with_custom_icon:
+ updated_values = api.portal.get_registry_record(record_name)
+ for updated_entry in updated_values:
+ if updated_entry["row_type"] == entry_with_custom_icon["row_type"]:
+ self.assertEqual(
+ updated_entry["row_type_icon"],
+ entry_with_custom_icon["row_type_icon"],
+ )
+ break
+
+ def test_upgrade_adds_title_description_view_if_missing(self):
+ """Test that upgrade adds title-description-view if not present."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Run upgrade
+ upgrade()
+
+ # Check updated values
+ updated_values = api.portal.get_registry_record(record_name)
+ updated_view_names = [v["row_type"] for v in updated_values]
+
+ # View should now exist
+ self.assertIn("cs_dynamicpages-title-description-view", updated_view_names)
+
+ def test_title_description_view_has_correct_fields(self):
+ """Test that title-description-view has correct fields configured."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Run upgrade to ensure view exists
+ upgrade()
+
+ # Find the title-description-view
+ values = api.portal.get_registry_record(record_name)
+ view_config = None
+ for value in values:
+ if value["row_type"] == "cs_dynamicpages-title-description-view":
+ view_config = value
+ break
+
+ self.assertIsNotNone(view_config)
+ self.assertIn("IBasic.title", view_config["each_row_type_fields"])
+ self.assertIn("IBasic.description", view_config["each_row_type_fields"])
+ self.assertEqual(view_config["row_type_icon"], "fonts")
+ self.assertFalse(view_config["row_type_has_featured_add_button"])
diff --git a/src/cs_dynamicpages/tests/test_upgrade_step_1006.py b/src/cs_dynamicpages/tests/test_upgrade_step_1006.py
new file mode 100644
index 0000000..b2179ad
--- /dev/null
+++ b/src/cs_dynamicpages/tests/test_upgrade_step_1006.py
@@ -0,0 +1,74 @@
+from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from cs_dynamicpages.upgrades.v1006 import upgrade
+from plone import api
+from plone.app.testing import setRoles
+from plone.app.testing import TEST_USER_ID
+
+import unittest
+
+
+class UpgradeStep1006IntegrationTest(unittest.TestCase):
+ """Integration tests for upgrade step 1006."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_upgrade_enables_row_vertical_spacing_behavior(self):
+ """Test that upgrade enables the row_vertical_spacing behavior."""
+ portal_types = api.portal.get_tool("portal_types")
+ fti = getattr(portal_types, "DynamicPageRow", None)
+
+ # Run upgrade
+ upgrade()
+
+ # Check behavior is enabled
+ self.assertIn("cs_dynamicpages.row_vertical_spacing", fti.behaviors)
+
+ def test_upgrade_adds_vertical_spacing_fields_to_all_row_types(self):
+ """Test that upgrade adds vertical spacing fields to all row types."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Run upgrade
+ upgrade()
+
+ # Check all row types have the new fields
+ values = api.portal.get_registry_record(record_name)
+ expected_fields = [
+ "IRowVerticalSpacing.padding_top",
+ "IRowVerticalSpacing.padding_bottom",
+ "IRowVerticalSpacing.margin_top",
+ "IRowVerticalSpacing.margin_bottom",
+ ]
+
+ for value in values:
+ fields = value["each_row_type_fields"]
+ for expected_field in expected_fields:
+ self.assertIn(
+ expected_field,
+ fields,
+ f"Field {expected_field} not found in row type {value['row_type']}",
+ )
+
+ def test_upgrade_preserves_existing_fields(self):
+ """Test that upgrade preserves existing fields in row types."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Get original fields for first row type
+ values = api.portal.get_registry_record(record_name)
+ if values:
+ original_fields = values[0]["each_row_type_fields"].copy()
+ original_row_type = values[0]["row_type"]
+
+ # Run upgrade
+ upgrade()
+
+ # Check original fields are preserved
+ updated_values = api.portal.get_registry_record(record_name)
+ for updated in updated_values:
+ if updated["row_type"] == original_row_type:
+ for field in original_fields:
+ self.assertIn(field, updated["each_row_type_fields"])
+ break
diff --git a/src/cs_dynamicpages/tests/test_upgrade_step_1007.py b/src/cs_dynamicpages/tests/test_upgrade_step_1007.py
new file mode 100644
index 0000000..8322cd6
--- /dev/null
+++ b/src/cs_dynamicpages/tests/test_upgrade_step_1007.py
@@ -0,0 +1,116 @@
+from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from cs_dynamicpages.upgrades.v1007 import upgrade
+from plone import api
+from plone.app.testing import setRoles
+from plone.app.testing import TEST_USER_ID
+from typing import ClassVar
+
+import unittest
+
+
+class UpgradeStep1007IntegrationTest(unittest.TestCase):
+ """Integration tests for upgrade step 1007."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ # Row types that should get the fetchpriority_image field
+ TARGET_ROW_TYPES: ClassVar[list[str]] = [
+ "cs_dynamicpages-slider-view",
+ "cs_dynamicpages-features-view",
+ "cs_dynamicpages-query-columns-view",
+ "cs_dynamicpages-featured-overlay-view",
+ "cs_dynamicpages-featured-view",
+ ]
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_upgrade_enables_fetchpriority_image_behavior(self):
+ """Test that upgrade enables the fetchpriority_image behavior."""
+ portal_types = api.portal.get_tool("portal_types")
+ fti = getattr(portal_types, "DynamicPageRow", None)
+
+ # Run upgrade
+ upgrade()
+
+ # Check behavior is enabled
+ self.assertIn("cs_dynamicpages.fetchpriority_image", fti.behaviors)
+
+ def test_upgrade_adds_fetchpriority_field_to_target_row_types(self):
+ """Test that upgrade adds fetchpriority field to specific row types."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Run upgrade
+ upgrade()
+
+ # Check target row types have the new field
+ values = api.portal.get_registry_record(record_name)
+
+ for value in values:
+ if value["row_type"] in self.TARGET_ROW_TYPES:
+ self.assertIn(
+ "IFetchPriorityImage.fetchpriority_image",
+ value["each_row_type_fields"],
+ f"Field not found in target row type {value['row_type']}",
+ )
+
+ def test_upgrade_does_not_add_field_to_other_row_types(self):
+ """Test that upgrade does not add field to non-target row types."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Get original state
+ original_values = api.portal.get_registry_record(record_name)
+ non_target_types_fields = {}
+ for value in original_values:
+ if value["row_type"] not in self.TARGET_ROW_TYPES:
+ # Check if fetchpriority field is already present
+ has_field = "IFetchPriorityImage.fetchpriority_image" in value.get(
+ "each_row_type_fields", []
+ )
+ non_target_types_fields[value["row_type"]] = has_field
+
+ # Run upgrade
+ upgrade()
+
+ # Check non-target row types do not have the field (unless they had it before)
+ updated_values = api.portal.get_registry_record(record_name)
+ for value in updated_values:
+ row_type = value["row_type"]
+ is_non_target = row_type not in self.TARGET_ROW_TYPES
+ had_field_before = non_target_types_fields.get(row_type, False)
+ if is_non_target and not had_field_before:
+ self.assertNotIn(
+ "IFetchPriorityImage.fetchpriority_image",
+ value["each_row_type_fields"],
+ f"Field unexpectedly added to non-target: {row_type}",
+ )
+
+ def test_upgrade_preserves_existing_fields_in_target_types(self):
+ """Test that upgrade preserves existing fields in target row types."""
+ record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+
+ # Get original fields for a target row type
+ values = api.portal.get_registry_record(record_name)
+ target_type_fields = None
+ target_row_type = None
+
+ for value in values:
+ if value["row_type"] in self.TARGET_ROW_TYPES:
+ target_row_type = value["row_type"]
+ target_type_fields = value["each_row_type_fields"].copy()
+ break
+
+ if target_type_fields is None:
+ self.skipTest("No target row types found in registry")
+
+ # Run upgrade
+ upgrade()
+
+ # Check original fields are preserved
+ updated_values = api.portal.get_registry_record(record_name)
+ for updated in updated_values:
+ if updated["row_type"] == target_row_type:
+ for field in target_type_fields:
+ self.assertIn(field, updated["each_row_type_fields"])
+ break
diff --git a/src/cs_dynamicpages/tests/test_utils.py b/src/cs_dynamicpages/tests/test_utils.py
new file mode 100644
index 0000000..0ff5417
--- /dev/null
+++ b/src/cs_dynamicpages/tests/test_utils.py
@@ -0,0 +1,334 @@
+from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from cs_dynamicpages.utils import _url_uses_scheme
+from cs_dynamicpages.utils import absolute_target_url
+from cs_dynamicpages.utils import add_custom_view
+from cs_dynamicpages.utils import enable_behavior
+from cs_dynamicpages.utils import get_available_views_for_row
+from cs_dynamicpages.utils import NON_REDIRECTABLE_URL_SCHEMES
+from cs_dynamicpages.utils import NON_RESOLVABLE_URL_SCHEMES
+from cs_dynamicpages.utils import normalize_uid_from_path
+from cs_dynamicpages.utils import VIEW_PREFIX
+from plone import api
+from plone.app.testing import setRoles
+from plone.app.testing import TEST_USER_ID
+
+import unittest
+
+
+class TestNormalizeUidFromPath(unittest.TestCase):
+ """Tests for normalize_uid_from_path function."""
+
+ def test_returns_none_tuple_for_empty_url(self):
+ uid, fragment = normalize_uid_from_path(None)
+ self.assertIsNone(uid)
+ self.assertIsNone(fragment)
+
+ def test_returns_none_tuple_for_empty_string(self):
+ uid, fragment = normalize_uid_from_path("")
+ self.assertIsNone(uid)
+ self.assertIsNone(fragment)
+
+ def test_extracts_uid_from_resolveuid_path(self):
+ uid, fragment = normalize_uid_from_path("/resolveuid/abc123")
+ self.assertEqual(uid, "abc123")
+ self.assertIsNone(fragment)
+
+ def test_extracts_uid_case_insensitive(self):
+ uid, fragment = normalize_uid_from_path("/ResolveUid/abc123")
+ self.assertEqual(uid, "abc123")
+ self.assertIsNone(fragment)
+
+ def test_extracts_uid_and_fragment(self):
+ uid, fragment = normalize_uid_from_path("/resolveuid/abc123#section1")
+ self.assertEqual(uid, "abc123")
+ self.assertEqual(fragment, "#section1")
+
+ def test_returns_none_when_resolveuid_at_end(self):
+ uid, fragment = normalize_uid_from_path("/resolveuid/")
+ self.assertIsNone(uid)
+ self.assertIsNone(fragment)
+
+ def test_returns_none_for_url_without_resolveuid(self):
+ uid, fragment = normalize_uid_from_path("/some/path/to/content")
+ self.assertIsNone(uid)
+ self.assertIsNone(fragment)
+
+ def test_handles_full_url_with_resolveuid(self):
+ uid, fragment = normalize_uid_from_path(
+ "http://example.com/resolveuid/abc123#anchor"
+ )
+ self.assertEqual(uid, "abc123")
+ self.assertEqual(fragment, "#anchor")
+
+
+class TestUrlUsesScheme(unittest.TestCase):
+ """Tests for _url_uses_scheme function."""
+
+ def test_returns_true_for_mailto(self):
+ self.assertTrue(
+ _url_uses_scheme(NON_REDIRECTABLE_URL_SCHEMES, "mailto:test@example.com")
+ )
+
+ def test_returns_true_for_tel(self):
+ self.assertTrue(
+ _url_uses_scheme(NON_REDIRECTABLE_URL_SCHEMES, "tel:+1234567890")
+ )
+
+ def test_returns_true_for_callto(self):
+ self.assertTrue(
+ _url_uses_scheme(NON_REDIRECTABLE_URL_SCHEMES, "callto:username")
+ )
+
+ def test_returns_false_for_http(self):
+ self.assertFalse(
+ _url_uses_scheme(NON_REDIRECTABLE_URL_SCHEMES, "http://example.com")
+ )
+
+ def test_returns_false_for_https(self):
+ self.assertFalse(
+ _url_uses_scheme(NON_REDIRECTABLE_URL_SCHEMES, "https://example.com")
+ )
+
+ def test_returns_true_for_file_in_non_resolvable(self):
+ self.assertTrue(
+ _url_uses_scheme(NON_RESOLVABLE_URL_SCHEMES, "file:///path/to/file")
+ )
+
+ def test_returns_true_for_ftp_in_non_resolvable(self):
+ self.assertTrue(
+ _url_uses_scheme(NON_RESOLVABLE_URL_SCHEMES, "ftp://server/path")
+ )
+
+ def test_returns_false_for_empty_schemes_list(self):
+ self.assertFalse(_url_uses_scheme([], "mailto:test@example.com"))
+
+
+class TestViewPrefix(unittest.TestCase):
+ """Tests for VIEW_PREFIX constant."""
+
+ def test_view_prefix_value(self):
+ self.assertEqual(VIEW_PREFIX, "cs_dynamicpages-")
+
+
+class AddCustomViewIntegrationTest(unittest.TestCase):
+ """Integration tests for add_custom_view function.
+
+ Note: add_custom_view modifies registry with values that may not satisfy
+ schema constraints. These tests verify the function works but we cannot
+ easily test the actual registry modification due to schema validation.
+ """
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+ # Store original registry value for restoration
+ self.record_name = "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields"
+ self.original_values = list(api.portal.get_registry_record(self.record_name))
+
+ def tearDown(self):
+ # Restore original registry values to prevent test pollution
+ from plone.registry.interfaces import IRegistry
+ from zope.component import getUtility
+
+ registry = getUtility(IRegistry)
+ # Direct assignment to bypass validation for cleanup
+ record = registry.records[self.record_name]
+ record.value = self.original_values
+
+ def test_add_custom_view_returns_true(self):
+ # Use an existing view name to avoid schema constraint issues
+ existing_views = get_available_views_for_row()
+ if existing_views:
+ # Test with existing view - function should still work
+ result = add_custom_view(
+ existing_views[0]["row_type"],
+ ["title", "description"],
+ has_button=False,
+ icon="star",
+ )
+ # Function returns True even if view already exists
+ self.assertTrue(result)
+ else:
+ self.skipTest("No existing views to test with")
+
+ def test_add_custom_view_structure(self):
+ """Test that add_custom_view creates correct structure."""
+ # Test using slider-view which is registered
+ view_name = "cs_dynamicpages-slider-view"
+ original_len = len(api.portal.get_registry_record(self.record_name))
+
+ add_custom_view(view_name, ["title"], has_button=True, icon="heart")
+
+ values = api.portal.get_registry_record(self.record_name)
+ # Should have added one entry
+ self.assertEqual(len(values), original_len + 1)
+
+ # Last entry should be our new one
+ matching = [
+ v
+ for v in values
+ if v["row_type"] == view_name and v["row_type_icon"] == "heart"
+ ]
+ self.assertTrue(len(matching) >= 1)
+
+ def test_add_custom_view_default_icon_is_bricks(self):
+ """Test that default icon is 'bricks'."""
+ view_name = "cs_dynamicpages-featured-view"
+
+ add_custom_view(view_name, ["title"])
+
+ values = api.portal.get_registry_record(self.record_name)
+ matching = [v for v in values if v["row_type"] == view_name]
+ # Should find at least one entry (original + new)
+ last_match = matching[-1] # Get the last added one
+ self.assertEqual(last_match["row_type_icon"], "bricks")
+
+
+class EnableBehaviorIntegrationTest(unittest.TestCase):
+ """Integration tests for enable_behavior function."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_enable_behavior_adds_to_fti(self):
+ behavior_name = "plone.basic"
+ portal_types = api.portal.get_tool("portal_types")
+ fti = getattr(portal_types, "DynamicPageRow", None)
+
+ # Store original behaviors
+ original_behaviors = fti.behaviors
+
+ # Only test if behavior not already present
+ if behavior_name not in fti.behaviors:
+ enable_behavior(behavior_name)
+ self.assertIn(behavior_name, fti.behaviors)
+
+ # Restore original behaviors
+ fti.behaviors = original_behaviors
+
+ def test_enable_behavior_does_not_duplicate(self):
+ portal_types = api.portal.get_tool("portal_types")
+ fti = getattr(portal_types, "DynamicPageRow", None)
+
+ # Get a behavior that's already enabled
+ if fti.behaviors:
+ existing_behavior = fti.behaviors[0]
+ original_count = fti.behaviors.count(existing_behavior)
+
+ enable_behavior(existing_behavior)
+
+ # Count should remain the same
+ self.assertEqual(fti.behaviors.count(existing_behavior), original_count)
+
+
+class GetAvailableViewsForRowIntegrationTest(unittest.TestCase):
+ """Integration tests for get_available_views_for_row function."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_returns_list(self):
+ result = get_available_views_for_row()
+ self.assertIsInstance(result, list)
+
+ def test_all_items_have_required_keys(self):
+ result = get_available_views_for_row()
+ required_keys = [
+ "row_type",
+ "each_row_type_fields",
+ "row_type_has_featured_add_button",
+ "row_type_icon",
+ ]
+ for item in result:
+ for key in required_keys:
+ self.assertIn(key, item)
+
+ def test_all_row_types_start_with_prefix(self):
+ result = get_available_views_for_row()
+ for item in result:
+ self.assertTrue(
+ item["row_type"].startswith(VIEW_PREFIX),
+ f"Row type {item['row_type']} does not start with {VIEW_PREFIX}",
+ )
+
+
+class AbsoluteTargetUrlIntegrationTest(unittest.TestCase):
+ """Integration tests for absolute_target_url function."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_returns_mailto_unchanged(self):
+ url = "mailto:test@example.com"
+ result = absolute_target_url(url)
+ self.assertEqual(result, url)
+
+ def test_returns_tel_unchanged(self):
+ url = "tel:+1234567890"
+ result = absolute_target_url(url)
+ self.assertEqual(result, url)
+
+ def test_returns_file_unchanged(self):
+ url = "file:///path/to/file"
+ result = absolute_target_url(url)
+ self.assertEqual(result, url)
+
+ def test_returns_ftp_unchanged(self):
+ url = "ftp://server/path"
+ result = absolute_target_url(url)
+ self.assertEqual(result, url)
+
+ def test_returns_url_unchanged_when_no_resolveuid(self):
+ url = "http://example.com/page"
+ result = absolute_target_url(url)
+ self.assertEqual(result, url)
+
+ def test_returns_original_url_for_invalid_uid(self):
+ url = "/resolveuid/nonexistent-uid-12345"
+ result = absolute_target_url(url)
+ self.assertEqual(result, url)
+
+ def test_resolves_valid_uid_to_url(self):
+ # Create a document to resolve
+ doc = api.content.create(
+ container=self.portal,
+ type="Document",
+ id="test-doc-for-url",
+ title="Test Document",
+ )
+ uid = doc.UID()
+
+ url = f"/resolveuid/{uid}"
+ result = absolute_target_url(url)
+ self.assertEqual(result, doc.absolute_url())
+
+ # Cleanup
+ api.content.delete(obj=doc)
+
+ def test_resolves_uid_with_fragment(self):
+ # Create a document to resolve
+ doc = api.content.create(
+ container=self.portal,
+ type="Document",
+ id="test-doc-for-fragment",
+ title="Test Document",
+ )
+ uid = doc.UID()
+
+ url = f"/resolveuid/{uid}#section1"
+ result = absolute_target_url(url)
+ self.assertEqual(result, f"{doc.absolute_url()}#section1")
+
+ # Cleanup
+ api.content.delete(obj=doc)
diff --git a/src/cs_dynamicpages/tests/test_view_dynamic_page_folder_view.py b/src/cs_dynamicpages/tests/test_view_dynamic_page_folder_view.py
index caf7d16..3e75719 100644
--- a/src/cs_dynamicpages/tests/test_view_dynamic_page_folder_view.py
+++ b/src/cs_dynamicpages/tests/test_view_dynamic_page_folder_view.py
@@ -10,39 +10,61 @@
import unittest
-class ViewsIntegrationTest(unittest.TestCase):
+class DynamicPageFolderViewsIntegrationTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
- api.content.create(self.portal, "Folder", "other-folder")
- api.content.create(self.portal, "Document", "front-page")
+
+ # Create a DynamicPageFolder
+ self.folder = api.content.create(self.portal, "Folder", "test-folder")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
def test_dynamic_page_folder_view_is_registered(self):
+ """Test that view is registered for DynamicPageFolder."""
view = getMultiAdapter(
- (self.portal["other-folder"], self.portal.REQUEST),
- name="dynamic-page-folder-view",
+ (self.dpf, self.portal.REQUEST),
+ name="view",
)
self.assertTrue(IDynamicPageFolderView.providedBy(view))
- def test_dynamic_page_folder_view_not_matching_interface(self):
+ def test_dynamic_page_folder_view_not_found_for_document(self):
+ """Test that view is not registered for Document."""
+ doc = api.content.create(self.portal, "Document", "front-page")
view_found = True
try:
view = getMultiAdapter(
- (self.portal["front-page"], self.portal.REQUEST),
- name="dynamic-page-folder-view",
+ (doc, self.portal.REQUEST),
+ name="view",
)
+ view_found = IDynamicPageFolderView.providedBy(view)
except ComponentLookupError:
view_found = False
- else:
- view_found = IDynamicPageFolderView.providedBy(view)
self.assertFalse(view_found)
-class ViewsFunctionalTest(unittest.TestCase):
+class DynamicPageFolderViewsFunctionalTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
def setUp(self):
self.portal = self.layer["portal"]
+ self.request = self.layer["request"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ # Create a DynamicPageFolder
+ self.folder = api.content.create(self.portal, "Folder", "test-folder-dpf-func")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+
+ def test_dynamic_page_folder_view_renders_without_error(self):
+ """Test that DynamicPageFolder view renders without raising an error."""
+ view = getMultiAdapter(
+ (self.dpf, self.request),
+ name="view",
+ )
+ html = view()
+ self.assertIsInstance(html, str)
diff --git a/src/cs_dynamicpages/tests/test_view_dynamic_page_row_featured_view.py b/src/cs_dynamicpages/tests/test_view_dynamic_page_row_featured_view.py
index e8d71fa..ea4375a 100644
--- a/src/cs_dynamicpages/tests/test_view_dynamic_page_row_featured_view.py
+++ b/src/cs_dynamicpages/tests/test_view_dynamic_page_row_featured_view.py
@@ -12,39 +12,87 @@
import unittest
-class ViewsIntegrationTest(unittest.TestCase):
+class DynamicPageRowFeaturedViewsIntegrationTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
- api.content.create(self.portal, "Folder", "other-folder")
- api.content.create(self.portal, "Document", "front-page")
+
+ # Create a DynamicPageFolder with a DynamicPageRow and Featured item
+ self.folder = api.content.create(self.portal, "Folder", "test-folder")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
+ self.featured = api.content.create(
+ self.row,
+ "DynamicPageRowFeatured",
+ "test-featured",
+ title="Test Featured",
+ )
def test_dynamic_page_row_featured_view_is_registered(self):
+ """Test that view is registered for DynamicPageRowFeatured."""
view = getMultiAdapter(
- (self.portal["other-folder"], self.portal.REQUEST),
- name="dynamic-page-row-featured-view",
+ (self.featured, self.portal.REQUEST),
+ name="view",
)
self.assertTrue(IDynamicPageRowFeaturedView.providedBy(view))
- def test_dynamic_page_row_featured_view_not_matching_interface(self):
+ def test_dynamic_page_row_featured_view_not_found_for_document(self):
+ """Test that view is not registered for Document."""
+ doc = api.content.create(self.portal, "Document", "front-page")
view_found = True
try:
view = getMultiAdapter(
- (self.portal["front-page"], self.portal.REQUEST),
- name="dynamic-page-row-featured-view",
+ (doc, self.portal.REQUEST),
+ name="view",
)
+ view_found = IDynamicPageRowFeaturedView.providedBy(view)
except ComponentLookupError:
view_found = False
- else:
- view_found = IDynamicPageRowFeaturedView.providedBy(view)
self.assertFalse(view_found)
-class ViewsFunctionalTest(unittest.TestCase):
+class DynamicPageRowFeaturedViewsFunctionalTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
def setUp(self):
self.portal = self.layer["portal"]
+ self.request = self.layer["request"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ # Create full content structure
+ self.folder = api.content.create(
+ self.portal, "Folder", "test-folder-featured-func"
+ )
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
+ self.featured = api.content.create(
+ self.row,
+ "DynamicPageRowFeatured",
+ "test-featured-func",
+ title="Test Featured",
+ )
+
+ def test_dynamic_page_row_featured_view_renders_without_error(self):
+ """Test that DynamicPageRowFeatured view renders without error."""
+ view = getMultiAdapter(
+ (self.featured, self.request),
+ name="view",
+ )
+ html = view()
+ self.assertIsInstance(html, str)
diff --git a/src/cs_dynamicpages/tests/test_view_dynamic_page_row_view.py b/src/cs_dynamicpages/tests/test_view_dynamic_page_row_view.py
index 927aded..b49f7e7 100644
--- a/src/cs_dynamicpages/tests/test_view_dynamic_page_row_view.py
+++ b/src/cs_dynamicpages/tests/test_view_dynamic_page_row_view.py
@@ -10,39 +10,73 @@
import unittest
-class ViewsIntegrationTest(unittest.TestCase):
+class DynamicPageRowViewsIntegrationTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
- api.content.create(self.portal, "Folder", "other-folder")
- api.content.create(self.portal, "Document", "front-page")
+
+ # Create a DynamicPageFolder with a DynamicPageRow
+ self.folder = api.content.create(self.portal, "Folder", "test-folder")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
def test_dynamic_page_row_view_is_registered(self):
+ """Test that view is registered for DynamicPageRow."""
view = getMultiAdapter(
- (self.portal["other-folder"], self.portal.REQUEST),
- name="dynamic-page-row-view",
+ (self.row, self.portal.REQUEST),
+ name="view",
)
self.assertTrue(IDynamicPageRowView.providedBy(view))
- def test_dynamic_page_row_view_not_matching_interface(self):
+ def test_dynamic_page_row_view_not_found_for_document(self):
+ """Test that view is not registered for Document."""
+ doc = api.content.create(self.portal, "Document", "front-page")
view_found = True
try:
view = getMultiAdapter(
- (self.portal["front-page"], self.portal.REQUEST),
- name="dynamic-page-row-view",
+ (doc, self.portal.REQUEST),
+ name="view",
)
+ view_found = IDynamicPageRowView.providedBy(view)
except ComponentLookupError:
view_found = False
- else:
- view_found = IDynamicPageRowView.providedBy(view)
self.assertFalse(view_found)
-class ViewsFunctionalTest(unittest.TestCase):
+class DynamicPageRowViewsFunctionalTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
def setUp(self):
self.portal = self.layer["portal"]
+ self.request = self.layer["request"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ # Create a DynamicPageFolder with a DynamicPageRow
+ self.folder = api.content.create(self.portal, "Folder", "test-folder-row-func")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row-func",
+ title="Test Row",
+ )
+
+ def test_dynamic_page_row_view_renders_without_error(self):
+ """Test that DynamicPageRow view renders without raising an error."""
+ view = getMultiAdapter(
+ (self.row, self.request),
+ name="view",
+ )
+ html = view()
+ self.assertIsInstance(html, str)
diff --git a/src/cs_dynamicpages/tests/test_view_dynamic_view.py b/src/cs_dynamicpages/tests/test_view_dynamic_view.py
index c20855c..9f39d17 100644
--- a/src/cs_dynamicpages/tests/test_view_dynamic_view.py
+++ b/src/cs_dynamicpages/tests/test_view_dynamic_view.py
@@ -43,4 +43,58 @@ class ViewsFunctionalTest(unittest.TestCase):
def setUp(self):
self.portal = self.layer["portal"]
+ self.request = self.layer["request"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_dynamic_view_renders_without_error(self):
+ """Test that dynamic view renders without raising an error."""
+ folder = api.content.create(self.portal, "Folder", "test-folder-dynamic")
+ view = getMultiAdapter(
+ (folder, self.request),
+ name="dynamic-view",
+ )
+ html = view()
+ self.assertIsInstance(html, str)
+
+ def test_dynamic_view_renders_with_rows(self):
+ """Test that dynamic view renders rows correctly."""
+ folder = api.content.create(self.portal, "Folder", "test-folder-with-rows")
+ # Create DynamicPageFolder with a row
+ dpf = api.content.create(folder, "DynamicPageFolder", "rows", title="Rows")
+ api.content.transition(obj=dpf, transition="publish")
+
+ row = api.content.create(
+ dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
+ row.row_type = "cs_dynamicpages-horizontal-rule-view"
+ api.content.transition(obj=row, transition="publish")
+
+ view = getMultiAdapter(
+ (folder, self.request),
+ name="dynamic-view",
+ )
+ html = view()
+ # Should contain main content area
+ self.assertIn("content", html)
+
+ def test_dynamic_view_rows_method_returns_rows(self):
+ """Test that dynamic view rows() method returns created rows."""
+ folder = api.content.create(self.portal, "Folder", "test-folder-rows-method")
+ dpf = api.content.create(folder, "DynamicPageFolder", "rows", title="Rows")
+ row = api.content.create(
+ dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
+ row.row_type = "cs_dynamicpages-horizontal-rule-view"
+
+ view = getMultiAdapter(
+ (folder, self.request),
+ name="dynamic-view",
+ )
+ rows = view.rows()
+ self.assertEqual(len(rows), 1)
diff --git a/src/cs_dynamicpages/tests/test_view_featured_view.py b/src/cs_dynamicpages/tests/test_view_featured_view.py
index bb0257b..8394303 100644
--- a/src/cs_dynamicpages/tests/test_view_featured_view.py
+++ b/src/cs_dynamicpages/tests/test_view_featured_view.py
@@ -10,37 +10,106 @@
import unittest
-class ViewsIntegrationTest(unittest.TestCase):
+class FeaturedViewsIntegrationTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
- api.content.create(self.portal, "Folder", "other-folder")
- api.content.create(self.portal, "Document", "front-page")
+
+ # Create a DynamicPageFolder with a DynamicPageRow
+ self.folder = api.content.create(self.portal, "Folder", "test-folder")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
+ self.row.row_type = "cs_dynamicpages-featured-view"
def test_featured_view_is_registered(self):
+ """Test that featured view is registered for DynamicPageRow."""
view = getMultiAdapter(
- (self.portal["other-folder"], self.portal.REQUEST), name="featured-view"
+ (self.row, self.portal.REQUEST),
+ name="cs_dynamicpages-featured-view",
)
self.assertTrue(IFeaturedView.providedBy(view))
- def test_featured_view_not_matching_interface(self):
+ def test_featured_view_not_found_for_document(self):
+ """Test that featured view is not registered for Document."""
+ doc = api.content.create(self.portal, "Document", "front-page")
view_found = True
try:
- view = getMultiAdapter(
- (self.portal["front-page"], self.portal.REQUEST), name="featured-view"
+ getMultiAdapter(
+ (doc, self.portal.REQUEST),
+ name="cs_dynamicpages-featured-view",
)
except ComponentLookupError:
view_found = False
- else:
- view_found = IFeaturedView.providedBy(view)
self.assertFalse(view_found)
-class ViewsFunctionalTest(unittest.TestCase):
+class FeaturedViewsFunctionalTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
def setUp(self):
self.portal = self.layer["portal"]
+ self.request = self.layer["request"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ # Create content structure
+ self.folder = api.content.create(self.portal, "Folder", "test-folder-featured")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row-featured",
+ title="Test Row",
+ )
+ self.row.row_type = "cs_dynamicpages-featured-view"
+
+ def test_featured_view_renders_without_error(self):
+ """Test that featured view renders without raising an error."""
+ # Set required attributes to avoid None errors
+ self.row.link_url = ""
+ self.row.link_text = ""
+ self.row.image_position = "left"
+
+ view = getMultiAdapter(
+ (self.row, self.request),
+ name="cs_dynamicpages-featured-view",
+ )
+ html = view()
+ self.assertIsInstance(html, str)
+
+ def test_featured_view_renders_row_structure(self):
+ """Test that featured view renders Bootstrap row structure."""
+ self.row.link_url = ""
+ self.row.link_text = ""
+ self.row.image_position = "left"
+
+ view = getMultiAdapter(
+ (self.row, self.request),
+ name="cs_dynamicpages-featured-view",
+ )
+ html = view()
+ self.assertIn('class="row"', html)
+ self.assertIn("col-md-6", html)
+
+ def test_featured_view_renders_title(self):
+ """Test that featured view renders the row title."""
+ self.row.link_url = ""
+ self.row.link_text = ""
+ self.row.image_position = "left"
+
+ view = getMultiAdapter(
+ (self.row, self.request),
+ name="cs_dynamicpages-featured-view",
+ )
+ html = view()
+ self.assertIn("Test Row", html)
diff --git a/src/cs_dynamicpages/tests/test_view_horizontal_rule_view.py b/src/cs_dynamicpages/tests/test_view_horizontal_rule_view.py
index 85618ba..f2c8000 100644
--- a/src/cs_dynamicpages/tests/test_view_horizontal_rule_view.py
+++ b/src/cs_dynamicpages/tests/test_view_horizontal_rule_view.py
@@ -1,6 +1,5 @@
from cs_dynamicpages.testing import CS_DYNAMICPAGES_FUNCTIONAL_TESTING
from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
-from cs_dynamicpages.views.horizontal_rule_view import IHorizontalRuleView
from plone import api
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
@@ -10,39 +9,83 @@
import unittest
-class ViewsIntegrationTest(unittest.TestCase):
+class HorizontalRuleViewsIntegrationTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
- api.content.create(self.portal, "Folder", "other-folder")
- api.content.create(self.portal, "Document", "front-page")
+
+ # Create a DynamicPageFolder with a DynamicPageRow
+ self.folder = api.content.create(self.portal, "Folder", "test-folder")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row",
+ title="Test Row",
+ )
+ self.row.row_type = "cs_dynamicpages-horizontal-rule-view"
def test_horizontal_rule_view_is_registered(self):
+ """Test that horizontal-rule-view is registered for DynamicPageRow."""
view = getMultiAdapter(
- (self.portal["other-folder"], self.portal.REQUEST),
- name="horizontal-rule-view",
+ (self.row, self.portal.REQUEST),
+ name="cs_dynamicpages-horizontal-rule-view",
)
- self.assertTrue(IHorizontalRuleView.providedBy(view))
+ self.assertIsNotNone(view)
- def test_horizontal_rule_view_not_matching_interface(self):
+ def test_horizontal_rule_view_not_found_for_document(self):
+ """Test that horizontal-rule-view is not registered for Document."""
+ doc = api.content.create(self.portal, "Document", "front-page")
view_found = True
try:
- view = getMultiAdapter(
- (self.portal["front-page"], self.portal.REQUEST),
- name="horizontal-rule-view",
+ getMultiAdapter(
+ (doc, self.portal.REQUEST),
+ name="cs_dynamicpages-horizontal-rule-view",
)
except ComponentLookupError:
view_found = False
- else:
- view_found = IHorizontalRuleView.providedBy(view)
self.assertFalse(view_found)
-class ViewsFunctionalTest(unittest.TestCase):
+class HorizontalRuleViewsFunctionalTest(unittest.TestCase):
layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING
def setUp(self):
self.portal = self.layer["portal"]
+ self.request = self.layer["request"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ # Create content structure
+ self.folder = api.content.create(self.portal, "Folder", "test-folder-hr")
+ self.dpf = api.content.create(
+ self.folder, "DynamicPageFolder", "rows", title="Rows"
+ )
+ self.row = api.content.create(
+ self.dpf,
+ "DynamicPageRow",
+ "test-row-hr",
+ title="Test Row",
+ )
+ self.row.row_type = "cs_dynamicpages-horizontal-rule-view"
+
+ def test_horizontal_rule_view_renders_without_error(self):
+ """Test that horizontal rule view renders without raising an error."""
+ view = getMultiAdapter(
+ (self.row, self.request),
+ name="cs_dynamicpages-horizontal-rule-view",
+ )
+ html = view()
+ self.assertIsInstance(html, str)
+
+ def test_horizontal_rule_view_renders_hr_element(self):
+ """Test that horizontal rule view renders an hr element."""
+ view = getMultiAdapter(
+ (self.row, self.request),
+ name="cs_dynamicpages-horizontal-rule-view",
+ )
+ html = view()
+ self.assertIn("
0)
+
+ def test_vocab_row_type_terms_start_with_prefix(self):
+ """Test that all row type terms start with cs_dynamicpages- prefix."""
+ vocab_name = "cs_dynamicpages.RowType"
+ factory = getUtility(IVocabularyFactory, vocab_name)
+ vocabulary = factory(self.portal)
+ for term in vocabulary:
+ self.assertTrue(
+ term.value.startswith("cs_dynamicpages-"),
+ f"Term {term.value} does not start with cs_dynamicpages-",
+ )
diff --git a/src/cs_dynamicpages/tests/test_vocab_row_vertical_spacing.py b/src/cs_dynamicpages/tests/test_vocab_row_vertical_spacing.py
new file mode 100644
index 0000000..31e5d8c
--- /dev/null
+++ b/src/cs_dynamicpages/tests/test_vocab_row_vertical_spacing.py
@@ -0,0 +1,92 @@
+from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
+from plone.app.testing import setRoles
+from plone.app.testing import TEST_USER_ID
+from zope.component import getUtility
+from zope.schema.interfaces import IVocabularyFactory
+from zope.schema.vocabulary import SimpleVocabulary
+
+import unittest
+
+
+class RowPaddingTopVocabularyIntegrationTest(unittest.TestCase):
+ """Integration tests for RowPaddingTop vocabulary."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_vocab_row_padding_top_registered(self):
+ """Test that the vocabulary is registered."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowPaddingTop")
+ self.assertIsNotNone(factory)
+
+ def test_vocab_row_padding_top_returns_vocabulary(self):
+ """Test that the factory returns a SimpleVocabulary."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowPaddingTop")
+ vocabulary = factory(self.portal)
+ self.assertIsInstance(vocabulary, SimpleVocabulary)
+
+
+class RowPaddingBottomVocabularyIntegrationTest(unittest.TestCase):
+ """Integration tests for RowPaddingBottom vocabulary."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_vocab_row_padding_bottom_registered(self):
+ """Test that the vocabulary is registered."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowPaddingBottom")
+ self.assertIsNotNone(factory)
+
+ def test_vocab_row_padding_bottom_returns_vocabulary(self):
+ """Test that the factory returns a SimpleVocabulary."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowPaddingBottom")
+ vocabulary = factory(self.portal)
+ self.assertIsInstance(vocabulary, SimpleVocabulary)
+
+
+class RowMarginTopVocabularyIntegrationTest(unittest.TestCase):
+ """Integration tests for RowMarginTop vocabulary."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_vocab_row_margin_top_registered(self):
+ """Test that the vocabulary is registered."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowMarginTop")
+ self.assertIsNotNone(factory)
+
+ def test_vocab_row_margin_top_returns_vocabulary(self):
+ """Test that the factory returns a SimpleVocabulary."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowMarginTop")
+ vocabulary = factory(self.portal)
+ self.assertIsInstance(vocabulary, SimpleVocabulary)
+
+
+class RowMarginBottomVocabularyIntegrationTest(unittest.TestCase):
+ """Integration tests for RowMarginBottom vocabulary."""
+
+ layer = CS_DYNAMICPAGES_INTEGRATION_TESTING
+
+ def setUp(self):
+ self.portal = self.layer["portal"]
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
+
+ def test_vocab_row_margin_bottom_registered(self):
+ """Test that the vocabulary is registered."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowMarginBottom")
+ self.assertIsNotNone(factory)
+
+ def test_vocab_row_margin_bottom_returns_vocabulary(self):
+ """Test that the factory returns a SimpleVocabulary."""
+ factory = getUtility(IVocabularyFactory, "cs_dynamicpages.RowMarginBottom")
+ vocabulary = factory(self.portal)
+ self.assertIsInstance(vocabulary, SimpleVocabulary)
diff --git a/src/cs_dynamicpages/tests/test_vocab_row_width.py b/src/cs_dynamicpages/tests/test_vocab_row_width.py
index 21ae37c..9af947a 100644
--- a/src/cs_dynamicpages/tests/test_vocab_row_width.py
+++ b/src/cs_dynamicpages/tests/test_vocab_row_width.py
@@ -1,4 +1,3 @@
-from cs_dynamicpages import _
from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
@@ -17,14 +16,34 @@ def setUp(self):
self.portal = self.layer["portal"]
setRoles(self.portal, TEST_USER_ID, ["Manager"])
- def test_vocab_row_width(self):
+ def test_vocab_row_width_registered(self):
+ """Test that RowWidth vocabulary is registered."""
vocab_name = "cs_dynamicpages.RowWidth"
factory = getUtility(IVocabularyFactory, vocab_name)
self.assertTrue(IVocabularyFactory.providedBy(factory))
+ def test_vocab_row_width_returns_tokenized_vocabulary(self):
+ """Test that vocabulary returns tokenized vocabulary."""
+ vocab_name = "cs_dynamicpages.RowWidth"
+ factory = getUtility(IVocabularyFactory, vocab_name)
vocabulary = factory(self.portal)
self.assertTrue(IVocabularyTokenized.providedBy(vocabulary))
- self.assertEqual(
- vocabulary.getTerm("sony-a7r-iii").title,
- _("Sony Aplha 7R III"),
- )
+
+ def test_vocab_row_width_has_terms(self):
+ """Test that vocabulary has terms from registry defaults."""
+ vocab_name = "cs_dynamicpages.RowWidth"
+ factory = getUtility(IVocabularyFactory, vocab_name)
+ vocabulary = factory(self.portal)
+ # Default registry has terms like "col-md-6 offset-md-3" (Narrow)
+ self.assertTrue(len(vocabulary) > 0)
+
+ def test_vocab_row_width_term_structure(self):
+ """Test that vocabulary terms have correct structure."""
+ vocab_name = "cs_dynamicpages.RowWidth"
+ factory = getUtility(IVocabularyFactory, vocab_name)
+ vocabulary = factory(self.portal)
+ if len(vocabulary) > 0:
+ term = next(iter(vocabulary))
+ self.assertIsNotNone(term.value)
+ self.assertIsNotNone(term.token)
+ self.assertIsNotNone(term.title)
diff --git a/src/cs_dynamicpages/views/configure.zcml b/src/cs_dynamicpages/views/configure.zcml
index f9c6501..82a7904 100644
--- a/src/cs_dynamicpages/views/configure.zcml
+++ b/src/cs_dynamicpages/views/configure.zcml
@@ -2,7 +2,6 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:plone="http://namespaces.plone.org/plone"
- xmlns:zcml="http://namespaces.zope.org/zcml"
i18n_domain="cs_dynamicpages"
>
@@ -79,18 +78,15 @@