From 60872a0aec1c950a012cae18656b7a2908822758 Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Sun, 6 Jul 2025 11:02:57 +0200 Subject: [PATCH] Started unit test for Workflows.py and WorkflowDesigner.py --- src/assets/icons.py | 2 +- src/components/workflows/assets/Workflows.css | 11 ++ .../workflows/components/WorkflowDesigner.py | 26 +-- .../workflows/components/Workflows.py | 2 +- src/components/workflows/constants.py | 5 + tests/helpers.py | 13 +- tests/test_repositories.py | 2 +- tests/test_workflow_designer.py | 151 +++++++++++++++++ tests/test_workflows.py | 153 ++++++++++++++++++ 9 files changed, 348 insertions(+), 17 deletions(-) create mode 100644 tests/test_workflow_designer.py create mode 100644 tests/test_workflows.py diff --git a/src/assets/icons.py b/src/assets/icons.py index 782bb90..d008fc0 100644 --- a/src/assets/icons.py +++ b/src/assets/icons.py @@ -12,7 +12,7 @@ icon_dismiss_regular = NotStr( ) # Fluent Add16Regular -icon_add_regular = NotStr(""" +icon_add_regular = NotStr(""" diff --git a/src/components/workflows/assets/Workflows.css b/src/components/workflows/assets/Workflows.css index 42b6234..122f384 100644 --- a/src/components/workflows/assets/Workflows.css +++ b/src/components/workflows/assets/Workflows.css @@ -89,6 +89,17 @@ transition: none; } +.wkf-component-content { + padding: 0.75rem; /* p-3 in Tailwind */ + border-radius: 0.5rem; /* rounded-lg in Tailwind */ + border-width: 2px; /* border-2 in Tailwind */ + background-color: white; /* bg-white in Tailwind */ + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-lg in Tailwind */ + display: flex; /* flex in Tailwind */ + align-items: center; /* items-center in Tailwind */ +} + + .wkf-connection-line { position: absolute; pointer-events: none; diff --git a/src/components/workflows/components/WorkflowDesigner.py b/src/components/workflows/components/WorkflowDesigner.py index 22cb545..50b61a1 100644 --- a/src/components/workflows/components/WorkflowDesigner.py +++ b/src/components/workflows/components/WorkflowDesigner.py @@ -6,7 +6,7 @@ from fasthtml.xtend import Script from components.BaseComponent import BaseComponent from components.workflows.commands import WorkflowDesignerCommandManager -from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID +from components.workflows.constants import WORKFLOW_DESIGNER_INSTANCE_ID, ProcessorTypes from components.workflows.db_management import WorkflowsDesignerSettings, WorkflowComponent, \ Connection, WorkflowsDesignerDbManager from components_helpers import apply_boundaries, mk_tooltip, mk_dialog_buttons @@ -17,19 +17,19 @@ logger = logging.getLogger("WorkflowDesigner") # Component templates COMPONENT_TYPES = { - "producer": { + ProcessorTypes.Producer: { "title": "Data Producer", "description": "Generates or loads data", "icon": "📊", "color": "bg-green-100 border-green-300 text-neutral" }, - "filter": { + ProcessorTypes.Filter: { "title": "Data Filter", "description": "Filters and transforms data", "icon": "🔍", "color": "bg-blue-100 border-blue-300 text-neutral" }, - "presenter": { + ProcessorTypes.Presenter: { "title": "Data Presenter", "description": "Displays or exports data", "icon": "📋", @@ -38,9 +38,9 @@ COMPONENT_TYPES = { } PROCESSOR_TYPES = { - "producer": ["Repository", "Jira"], - "filter": ["Default"], - "presenter": ["Default"]} + ProcessorTypes.Producer: ["Repository", "Jira"], + ProcessorTypes.Filter: ["Default"], + ProcessorTypes.Presenter: ["Default"]} class WorkflowDesigner(BaseComponent): @@ -229,7 +229,7 @@ class WorkflowDesigner(BaseComponent): # Render components *[self._mk_workflow_component(comp) for comp in self._state.components.values()], - ), + ) def _mk_canvas(self, oob=False): return Div( @@ -264,9 +264,9 @@ class WorkflowDesigner(BaseComponent): return self._mk_jira_processor_details(component) elif processor_name == "Repository": return self._mk_repository_processor_details(component) - elif component.type == "filter" and processor_name == "Default": + elif component.type == ProcessorTypes.Filter and processor_name == "Default": return self._mk_filter_processor_details(component) - elif component.type == "presenter" and processor_name == "Default": + elif component.type == ProcessorTypes.Presenter and processor_name == "Default": return self._mk_presenter_processor_details(component) return Div('Not defined yet !') @@ -403,9 +403,9 @@ class WorkflowDesigner(BaseComponent): def _mk_presenter_processor_details(component): return Div( Fieldset( - Legend("Filter", cls="fieldset-legend"), + Legend("Presenter", cls="fieldset-legend"), Input(type="text", - name="filter", + name="presenter", value=component.properties.get("filter", ""), placeholder="Enter filter expression", cls="input w-full"), @@ -454,7 +454,7 @@ class WorkflowDesigner(BaseComponent): Div( Span(info["icon"], cls="text-xl mb-1"), H4(component.title, cls="font-semibold text-xs"), - cls=f"p-3 rounded-lg border-2 {info['color']} bg-white shadow-lg flex items-center" + cls=f"wkf-component-content {info['color']}" ), # Output connection point diff --git a/src/components/workflows/components/Workflows.py b/src/components/workflows/components/Workflows.py index 57831e9..95c4dc1 100644 --- a/src/components/workflows/components/Workflows.py +++ b/src/components/workflows/components/Workflows.py @@ -75,7 +75,7 @@ class Workflows(BaseComponentSingleton): self.tabs_manager.select_tab_by_key(tab_key) self.db.select_workflow(workflow_name) - return self.tabs_manager.refresh(), self.refresh() + return self.refresh(), self.tabs_manager.refresh() def refresh(self): return self._mk_workflows(True) diff --git a/src/components/workflows/constants.py b/src/components/workflows/constants.py index 4eafd49..4688621 100644 --- a/src/components/workflows/constants.py +++ b/src/components/workflows/constants.py @@ -5,6 +5,11 @@ WORKFLOW_DESIGNER_DB_ENTRY = "WorkflowDesigner" WORKFLOW_DESIGNER_DB_SETTINGS_ENTRY = "Settings" WORKFLOW_DESIGNER_DB_STATE_ENTRY = "State" +class ProcessorTypes: + Producer = "producer" + Filter = "filter" + Presenter = "presenter" + ROUTE_ROOT = "/workflows" diff --git a/tests/helpers.py b/tests/helpers.py index 71a8779..18c304e 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -420,7 +420,8 @@ def matches(actual, expected, path=""): assert matches(actual_child, expected_child) elif isinstance(expected, NotStr): - assert actual.s.lstrip('\n').startswith(expected.s), \ + to_compare = actual.s.lstrip('\n').lstrip() + assert to_compare.startswith(expected.s), \ f"{print_path(path)}NotStr are different: '{actual.s.lstrip('\n')}' != '{expected.s}'." elif hasattr(actual, "tag"): @@ -741,10 +742,20 @@ def _get_element_value(element): def icon(name: str): + """ + Test if an element is an icon + :param name: + :return: + """ return NotStr(f' + + + + + + + + + + """ + assert actual == expected + + +def test_i_can_render_elements_with_connections(designer, components): + designer._state.components = {c.id: c for c in components} + designer._state.connections = [Connection("conn_1", components[0].id, components[1].id), + Connection("conn_2", components[1].id, components[2].id)] + actual = designer._mk_elements() + expected = Div( + NotStr('