diff --git a/python/cpmf_uips_xaml/__version__.py b/python/cpmf_uips_xaml/__version__.py index 4a05ed2..231f0d6 100644 --- a/python/cpmf_uips_xaml/__version__.py +++ b/python/cpmf_uips_xaml/__version__.py @@ -1,5 +1,5 @@ """Version information for cpmf_uips_xaml package.""" -__version__ = "0.1.0" +__version__ = "0.1.2" __author__ = "Christian Prior-Mamulyan" __description__ = "Standalone XAML workflow parser for automation projects" diff --git a/python/cpmf_uips_xaml/stages/parsing/extractors.py b/python/cpmf_uips_xaml/stages/parsing/extractors.py index 7f2abdd..d0265ab 100644 --- a/python/cpmf_uips_xaml/stages/parsing/extractors.py +++ b/python/cpmf_uips_xaml/stages/parsing/extractors.py @@ -20,6 +20,15 @@ if TYPE_CHECKING: from .parser import XamlDialect +# Tags that carry VB.NET / C# expressions as an ExpressionText attribute or element text, +# used by the "child-element expression syntax" (Syntax B) introduced in newer UiPath Studio. +_EXPR_ELEMENT_TAGS = frozenset({ + "VisualBasicValue", + "VisualBasicReference", + "CSharpValue", + "CSharpReference", +}) + class ArgumentExtractor: """Extracts workflow arguments from x:Members section.""" @@ -669,6 +678,25 @@ def _extract_activity_arguments(self, element: ET.Element) -> dict[str, Any]: clean_name = attr_name.split("}")[-1] if "}" in attr_name else attr_name arguments[clean_name] = attr_value + # Child-element argument syntax: + # + for child in element: + child_tag = get_local_tag(child) + if "." in child_tag: + for param_child in child: + param_tag = get_local_tag(param_child) + if param_tag in ("InArgument", "OutArgument", "InOutArgument"): + for expr_elem in param_child: + expr_tag = get_local_tag(expr_elem) + if expr_tag in _EXPR_ELEMENT_TAGS: + expr_text = expr_elem.get("ExpressionText") + if expr_text: + existing = arguments.get(child_tag) + if existing is None: + arguments[child_tag] = [expr_text] + elif isinstance(existing, list): + existing.append(expr_text) + return arguments def _extract_visible_properties(self, element: ET.Element) -> dict[str, Any]: @@ -759,6 +787,18 @@ def _extract_business_logic_expressions(self, element: ET.Element) -> list[str]: extracted_expressions = self.activity_utils.extract_expressions_from_text(element.text) expressions.extend(extracted_expressions) + # Child-element expression syntax: + # ExpressionText contains the raw VB.NET/C# expression (no brackets), so add directly. + for descendant in element.iter(): + tag = descendant.tag.split("}")[-1] if "}" in descendant.tag else descendant.tag + if tag in _EXPR_ELEMENT_TAGS: + expr_text = descendant.get("ExpressionText") + if expr_text: + expressions.append(expr_text) + # Older serializations may embed the expression as element text + if descendant.text and descendant.text.strip(): + expressions.append(descendant.text.strip()) + return list(set(expressions)) # Remove duplicates def _extract_variable_references( diff --git a/python/tests/unit/test_extractors.py b/python/tests/unit/test_extractors.py index 5dce620..35df030 100644 --- a/python/tests/unit/test_extractors.py +++ b/python/tests/unit/test_extractors.py @@ -1127,6 +1127,98 @@ def test_extract_business_logic_expressions(self, xaml_with_expressions): if len(expressions) > 0: assert any("[" in expr or "(" in expr for expr in expressions) + def test_extract_expressions_from_visual_basic_value_element(self): + """VisualBasicValue ExpressionText is extracted as an expression.""" + xaml = """ + + + + + + +""" + root = parse_xaml_string(xaml) + extractor = create_activity_extractor() + + expressions = extractor._extract_business_logic_expressions(root) + + assert "lookupData" in expressions + + def test_extract_expressions_from_visual_basic_reference_element(self): + """VisualBasicReference ExpressionText contributes to expressions.""" + xaml = """ + + + + + + +""" + root = parse_xaml_string(xaml) + extractor = create_activity_extractor() + + expressions = extractor._extract_business_logic_expressions(root) + + assert "myVar" in expressions + + def test_mixed_bracket_and_element_syntax_same_activity(self): + """Activities using both [expr] attributes and VisualBasicValue children are both extracted.""" + xaml = """ + + + + + + +""" + root = parse_xaml_string(xaml) + extractor = create_activity_extractor() + + expressions = extractor._extract_business_logic_expressions(root) + + assert "lookupData" in expressions + # Bracket syntax strips delimiters: [servicePoint] → servicePoint + assert "servicePoint" in expressions + + def test_extract_arguments_from_child_element_syntax(self): + """_extract_activity_arguments captures ExpressionText from InArgument children.""" + xaml = """ + + + + + + + + + +""" + root = parse_xaml_string(xaml) + extractor = create_activity_extractor() + + arguments = extractor._extract_activity_arguments(root) + + assert "InvokeMethod.Parameters" in arguments + params = arguments["InvokeMethod.Parameters"] + assert "in_LookupData" in params + assert "in_RulesString" in params + # ============================================================================ # Test ActivityExtractor - Activity Instance Extraction (ADR-009)