Repository: SFDO-Tooling/CumulusCI
Pull request #3058 (2022-02-02 17:20:17)

Feature/robot libdoc relative paths
This converts absolute paths to relative paths in the generated csv files, whenever possible.

Fixes W-10480903

# Critical Changes

# Changes
* whenever possible, libdoc will generate relative pathnames when creating csv output

# Issues Closed



Patch:
diff --git a/cumulusci/tasks/robotframework/libdoc.py b/cumulusci/tasks/robotframework/libdoc.py
index 8e24cf2d3..23cedba50 100644
--- a/cumulusci/tasks/robotframework/libdoc.py
+++ b/cumulusci/tasks/robotframework/libdoc.py
@@ -3,6 +3,7 @@ import os
 import os.path
 import re
 import time
+from pathlib import Path
 
 import jinja2
 import robot.utils
@@ -207,8 +208,10 @@ class KeywordFile:
         self.keywords[page_object] = libdoc
 
     def to_tuples(self):
-        """Convert the dictionary of keyword data to tuple of tuples"""
+        """Convert the dictionary of keyword data to a set of tuples"""
         rows = []
+        cwd = Path.cwd()
+
         for po, libdoc in self.keywords.items():
             (po_type, po_object) = po if po else ("", "")
             for keyword in libdoc.keywords:
@@ -218,11 +221,19 @@ class KeywordFile:
                 if "cumulusci/robotframework/pageobjects" in keyword.source:
                     continue
 
+                path = keyword.source
+                if path.startswith("/"):
+                    try:
+                        path = str(Path(path).relative_to(cwd))
+                    except ValueError:
+                        # ok, fine. We'll use the path as-is.
+                        pass
+
                 # make sure that if you change the list of columns here
                 # that you modify the `get_header` property too!
                 row = (
                     keyword.name,
-                    keyword.source,
+                    path,
                     keyword.lineno,
                     po_type,
                     po_object,


Test patch:
diff --git a/cumulusci/tasks/robotframework/tests/test_robotframework.py b/cumulusci/tasks/robotframework/tests/test_robotframework.py
index dbfc0a01f..fdfce454b 100644
--- a/cumulusci/tasks/robotframework/tests/test_robotframework.py
+++ b/cumulusci/tasks/robotframework/tests/test_robotframework.py
@@ -12,6 +12,7 @@ from xml.etree import ElementTree as ET
 
 import pytest
 import responses
+from robot.libdocpkg.robotbuilder import LibraryDocBuilder
 
 from cumulusci.core.config import BaseProjectConfig, TaskConfig, UniversalConfig
 from cumulusci.core.exceptions import RobotTestFailure, TaskOptionsError
@@ -483,7 +484,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ["Name", "Source", "Line#", "po type", "po_object", "Documentation"],
             [
                 "Keyword One",
-                f"{self.datadir}/TestPageObjects.py",
+                "cumulusci/tasks/robotframework/tests/TestPageObjects.py",
                 "13",
                 "Listing",
                 "Something__c",
@@ -491,7 +492,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Keyword One",
-                f"{self.datadir}/TestPageObjects.py",
+                "cumulusci/tasks/robotframework/tests/TestPageObjects.py",
                 "24",
                 "Detail",
                 "Something__c",
@@ -499,7 +500,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Keyword Three",
-                f"{self.datadir}/TestPageObjects.py",
+                "cumulusci/tasks/robotframework/tests/TestPageObjects.py",
                 "30",
                 "Detail",
                 "Something__c",
@@ -507,7 +508,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Keyword Two",
-                f"{self.datadir}/TestPageObjects.py",
+                "cumulusci/tasks/robotframework/tests/TestPageObjects.py",
                 "16",
                 "Listing",
                 "Something__c",
@@ -515,7 +516,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Keyword Two",
-                f"{self.datadir}/TestPageObjects.py",
+                "cumulusci/tasks/robotframework/tests/TestPageObjects.py",
                 "27",
                 "Detail",
                 "Something__c",
@@ -523,7 +524,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Library Keyword One",
-                f"{self.datadir}/TestLibrary.py",
+                "cumulusci/tasks/robotframework/tests/TestLibrary.py",
                 "13",
                 "",
                 "",
@@ -531,7 +532,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Library Keyword Two",
-                f"{self.datadir}/TestLibrary.py",
+                "cumulusci/tasks/robotframework/tests/TestLibrary.py",
                 "17",
                 "",
                 "",
@@ -539,7 +540,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Resource keyword one",
-                f"{self.datadir}/TestResource.robot",
+                "cumulusci/tasks/robotframework/tests/TestResource.robot",
                 "2",
                 "",
                 "",
@@ -547,7 +548,7 @@ class TestRobotLibDoc(MockLoggerMixin, unittest.TestCase):
             ],
             [
                 "Resource keyword two",
-                f"{self.datadir}/TestResource.robot",
+                "cumulusci/tasks/robotframework/tests/TestResource.robot",
                 "6",
                 "",
                 "",
@@ -585,6 +586,34 @@ class TestRobotLibDocKeywordFile(unittest.TestCase):
         assert len(kwfile.keywords) == 1
         assert kwfile.keywords[("Detail", "Contact")] == "the documentation..."
 
+    def test_to_tuples(self):
+        """Test that to_tuples returns relative paths when possible
+
+        The code attempts to convert absolute paths to relative paths,
+        but if it can't then the path remains unchainged. This test generates
+        results with one file that is relative to cwd and one that is not.
+        """
+
+        here = os.path.dirname(__file__)
+        path = Path(here) / "TestLibrary.py"
+        libdoc = LibraryDocBuilder().build(str(path))
+
+        # we'll set the first to a non-relative directory and leave
+        # the other one relative to here (assuming that `here` is
+        # relative to cwd)
+        libdoc.keywords[0].source = "/bogus/whatever.py"
+
+        # The returned result is a set, so the order is indeterminate. That's
+        # why the following line sorts it.
+        kwfile = KeywordFile("Whatever")
+        kwfile.add_keywords(libdoc)
+        rows = sorted(kwfile.to_tuples())
+
+        # verify the absolute path remains absolute
+        assert rows[0][1] == "/bogus/whatever.py"
+        # verify that the path to a file under cwd is relative
+        assert rows[1][1] == str(path.relative_to(os.getcwd()))
+
 
 class TestRobotLibDocOutput(unittest.TestCase):
     """Tests for the generated robot keyword documentation"""