diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml
index 3a96c2d..e058aa9 100644
--- a/.github/workflows/build-publish.yml
+++ b/.github/workflows/build-publish.yml
@@ -2,14 +2,19 @@ name: Build Python Package
on:
push:
+ branches:
+ - main
+ - '**'
tags-ignore:
- 'v*.*.*'
paths:
+ - '**.yml'
- '**.py'
- '**.html'
- pyproject.toml
pull_request:
paths:
+ - '**.yml'
- '**.py'
- '**.html'
- pyproject.toml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9de6644..f604cfb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -40,12 +40,6 @@ jobs:
- name: Build Package
run: python -m build
- - name: Publish to PyPI
- env:
- TWINE_USERNAME: __token__
- TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
- run: twine upload dist/*
-
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
@@ -53,3 +47,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Publish to PyPI
+ env:
+ TWINE_USERNAME: __token__
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
+ run: twine upload dist/*
+
diff --git a/README.md b/README.md
index 31b33fc..bb27499 100644
--- a/README.md
+++ b/README.md
@@ -92,8 +92,8 @@ You can select between several themes (color configurations) for your HTML docum
### Default Themes
-There are a few predefined default themes available that you can choose via the toml-configuration file.
-Therefore, please use the following syntax:
+There are a few predefined default themes available that you can choose via the toml-configuration file OR cli.
+Therefore, please use the following syntax: for the toml file
```toml
[colors]
# Use the default theme
@@ -110,6 +110,15 @@ default = "robot"
default = 3
```
+For setting it via CLI, please use the following:
+```shell
+# Applying dark theme
+testdoc ... -S dark PATH OUTPUT_FILE
+
+# Applying blue theme
+testdoc ... --style blue PATH OUTPUT_FILE
+```
+
> [!TIP]
> You can select the default theme using either a string value or an integer value.
@@ -131,3 +140,17 @@ robot_icon = "#00ffb9"
> [!TIP]
> Please make sure to configure all available color values from this example — missing values may cause layout or rendering issues in the generated HTML document!
+
+### Default Themes - Screenshot
+
+#### Dark
+
+
+
+#### Blue
+
+
+
+#### Robot / Default
+
+
\ No newline at end of file
diff --git a/atest/config/config_with_colors.toml b/atest/config/config_with_colors.toml
index 193392a..3e38030 100644
--- a/atest/config/config_with_colors.toml
+++ b/atest/config/config_with_colors.toml
@@ -4,7 +4,7 @@ verbose_mode = true
[colors]
# DEFAULT THEME:
-default = "robot"
+default = "dark"
# OR CUSTOM THEME:
# background = "#000028"
# inner_color = "#000028"
diff --git a/docs/style_blue.png b/docs/style_blue.png
new file mode 100644
index 0000000..d2570da
Binary files /dev/null and b/docs/style_blue.png differ
diff --git a/docs/style_dark.png b/docs/style_dark.png
new file mode 100644
index 0000000..c95fe04
Binary files /dev/null and b/docs/style_dark.png differ
diff --git a/docs/style_robot.png b/docs/style_robot.png
new file mode 100644
index 0000000..5bbae0e
Binary files /dev/null and b/docs/style_robot.png differ
diff --git a/pyproject.toml b/pyproject.toml
index 898d5e9..8d9480b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "robotframework-testdoc"
-version = "0.1.4"
+version = "0.1.5"
description = "A CLI Tool to generate a Test Documentation for your RobotFramework Test Scripts."
readme = "README.md"
requires-python = ">=3.7"
diff --git a/src/testdoc/__main__.py b/src/testdoc/__main__.py
index 95f3538..502a396 100644
--- a/src/testdoc/__main__.py
+++ b/src/testdoc/__main__.py
@@ -1,3 +1,4 @@
from testdoc.cli import main
+
if __name__ == "__main__":
main()
\ No newline at end of file
diff --git a/src/testdoc/cli.py b/src/testdoc/cli.py
index 1052117..5f63b9d 100644
--- a/src/testdoc/cli.py
+++ b/src/testdoc/cli.py
@@ -19,6 +19,7 @@
@click.option("--hide-suite-doc", is_flag=True, required=False, help="If given, suite documentation is hidden")
@click.option("--hide-source", is_flag=True, required=False, help="If given, test suite/ test case source is hidden")
@click.option("--hide-keywords", is_flag=True, required=False, help="If given, keyword calls in test cases are hidden")
+@click.option("-S", "--style", required=False, help="Choose a predefined default style theme - 'default', 'robot', 'dark' or 'blue' ")
@click.option("-c", "--configfile", required=False, help="Optional .toml configuration file (includes all cmd-args)")
@click.option("-v", "--verbose", is_flag=True, required=False, help="More precise debugging into shell")
@click.argument("PATH")
@@ -36,6 +37,7 @@ def main(
hide_suite_doc,
hide_source,
hide_keywords,
+ style,
configfile,
verbose,
path,
@@ -77,6 +79,7 @@ def main(
"hide_source": hide_source or None,
"hide_keywords": hide_keywords or None,
"verbose_mode": verbose or None,
+ "style": style or None,
"config_file": configfile or None,
}
args.suite_file = path
diff --git a/src/testdoc/default.toml b/src/testdoc/default.toml
new file mode 100644
index 0000000..812d283
--- /dev/null
+++ b/src/testdoc/default.toml
@@ -0,0 +1,3 @@
+# Find here some default settings like the used default theme
+[default]
+theme = "dark"
diff --git a/src/testdoc/helper/cliargs.py b/src/testdoc/helper/cliargs.py
index 764734b..f7cd379 100644
--- a/src/testdoc/helper/cliargs.py
+++ b/src/testdoc/helper/cliargs.py
@@ -19,6 +19,7 @@ class CommandLineArgumentsData:
config_file: str = None
verbose_mode: bool = False
suite_file: str = None
+ style: str = None
output_file: str = None
colors: dict = None
diff --git a/src/testdoc/html/themes/theme_config.py b/src/testdoc/html/themes/theme_config.py
index c94e64d..21d7698 100644
--- a/src/testdoc/html/themes/theme_config.py
+++ b/src/testdoc/html/themes/theme_config.py
@@ -1,18 +1,32 @@
from ...helper.cliargs import CommandLineArguments
from .themes import DEFAULT_THEME, ROBOT_THEME, DARK_THEME, BLUE_THEME
+import os
+import tomli
+
class ThemeConfig():
+ default_config = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "default.toml")
+
def __init__(self):
self.args = CommandLineArguments().data
+ with open(self.default_config, "rb") as file:
+ self.config = tomli.load(file)
+
+ #######################################################################################################
def theme(self):
- _theme = self.args.colors
- if _theme:
- if "default" in _theme:
- return self._get_predefined_theme(_theme.get("default"))
- return _theme
- return DARK_THEME
+ _cli_style = self.args.style
+ if _cli_style:
+ return self._get_predefined_theme(_cli_style)
+ _toml_theme = self.args.colors
+ if _toml_theme:
+ if "default" in _toml_theme:
+ return self._get_predefined_theme(_toml_theme.get("default"))
+ return _toml_theme
+ return self._get_predefined_theme(self.config["default"]["theme"])
+
+ #######################################################################################################
def _get_predefined_theme(self, theme: str):
theme = theme.strip()
diff --git a/src/testdoc/parser/modifier/sourceprefixmodifier.py b/src/testdoc/parser/modifier/sourceprefixmodifier.py
index 1ae33a4..7219d38 100644
--- a/src/testdoc/parser/modifier/sourceprefixmodifier.py
+++ b/src/testdoc/parser/modifier/sourceprefixmodifier.py
@@ -1,23 +1,45 @@
import os
+from abc import ABC, abstractmethod
from robot.api import TestSuite
from ...helper.cliargs import CommandLineArguments
from ...helper.logger import Logger
+available_implementations = "gitlab"
+
+########################################
+# Interface
+########################################
+class SourceModifier(ABC):
+ @abstractmethod
+ def apply(self, suite_dict, prefix):
+ pass
+
+########################################
+# Factory
+########################################
+class SourceModifierFactory:
+ @staticmethod
+ def get_modifier(prefix_type: str) -> SourceModifier:
+ if prefix_type.lower() == "gitlab":
+ return GitLabModifier()
+ # EXAMPLE Extension:
+ # elif prefix_type.lower() == "github":
+ # return GitHubModifier()
+ raise ValueError(
+ f"No source modifier found for type '{prefix_type}' - actually available implementation are:\n{available_implementations}"
+ )
+
+########################################
+# Prefix Modifier - Implementation
+########################################
class SourcePrefixModifier():
GITLAB_CONNECTOR = "-/blob/main/"
def __init__(self):
self.args = CommandLineArguments().data
-
- def _modify(self, suite: TestSuite, prefix: str):
- prefix_type, prefix = self._prefix_validation(prefix)
- if "gitlab" in prefix_type:
- SourcePrefixGitLab()._apply_gitlab_source_to_suite(suite, prefix)
- else:
- raise ValueError(f"No matching source-prefix modifier found for: {prefix_type} with prefix: {prefix}")
def _prefix_validation(self, prefix: str) -> list:
if "::" not in prefix:
@@ -27,11 +49,16 @@ def _prefix_validation(self, prefix: str) -> list:
def modify_source_prefix(self, suite_object: TestSuite) -> TestSuite:
Logger().LogKeyValue("Using Prefix for Source: ", self.args.sourceprefix, "yellow") if self.args.verbose_mode else None
+ prefix_type, prefix = self._prefix_validation(self.args.sourceprefix)
+ modifier = SourceModifierFactory.get_modifier(prefix_type)
for suite in suite_object:
- self._modify(suite, self.args.sourceprefix)
+ modifier.apply(suite, prefix)
return suite_object
-class SourcePrefixGitLab():
+########################################
+# Low-Level Implementation for GitLab
+########################################
+class GitLabModifier():
"""
Source Prefix Modifier for "GitLab" Projects.
Expected CMD Line Arg: "gitlab::prefix"
@@ -62,7 +89,7 @@ def _convert_to_gitlab_url(self, file_path, prefix):
rel_path = os.path.relpath(file_path, git_root).replace(os.sep, "/")
return prefix.rstrip("/") + "/-/blob/" + git_branch + "/" + rel_path
- def _apply_gitlab_source_to_suite(self, suite_dict, prefix):
+ def apply(self, suite_dict, prefix):
try:
suite_dict["source"] = self._convert_to_gitlab_url(suite_dict["source"], prefix)
except:
@@ -75,4 +102,9 @@ def _apply_gitlab_source_to_suite(self, suite_dict, prefix):
test["source"] = "GitLink error"
for sub_suite in suite_dict.get("sub_suites", []):
- self._apply_gitlab_source_to_suite(sub_suite, prefix)
+ self.apply(sub_suite, prefix)
+
+########################################
+# Low-Level Implementation for ...
+# [FUTURE EXTENSIONS LIKE GITHUB]
+########################################
\ No newline at end of file
diff --git a/src/testdoc/parser/modifier/suitefilemodifier.py b/src/testdoc/parser/modifier/suitefilemodifier.py
index 7039e8c..1c0048e 100644
--- a/src/testdoc/parser/modifier/suitefilemodifier.py
+++ b/src/testdoc/parser/modifier/suitefilemodifier.py
@@ -18,9 +18,6 @@ def run(self, suite_object: TestSuite = None):
self.suite = suite_object
# Modify generic params / hide some params
- self._modify_root_suite_name()
- self._modify_root_suite_doc()
- self._modify_root_suite_metadata()
self._modify_tags()
self._modify_test_doc()
self._modify_suite_doc()
@@ -29,29 +26,16 @@ def run(self, suite_object: TestSuite = None):
return self.suite
#############################################################################################################################
-
- def _modify_root_suite_name(self):
- if not self.args.name:
- return
- Logger().LogKeyValue("Modified Name of Root Suite: ", self.args.name, "yellow") if self.args.verbose_mode else None
- self.suite[0]["name"] = self.args.name
-
- #############################################################################################################################
-
- def _modify_root_suite_doc(self):
- if not self.args.doc:
- return
- Logger().LogKeyValue("Modified Doc of Root Suite: ", self.args.name, "yellow") if self.args.verbose_mode else None
- self.suite[0]["doc"] = self.args.doc
-
- #############################################################################################################################
-
- def _modify_root_suite_metadata(self):
- if not self.args.metadata:
- return
- Logger().LogKeyValue("Modified Metadata of Root Suite: ", self.args.metadata, "yellow") if self.args.verbose_mode else None
- formatted_metadata = "
".join([f"{k}: {v}" for k, v in self.args.metadata.items()])
- self.suite[0]["metadata"] = formatted_metadata
+
+ # Modify name, doc & metadata via officially provided robot api
+ def _modify_root_suite_details(self, suite: TestSuite):
+ if self.args.name:
+ suite.configure(name=self.args.name)
+ if self.args.doc:
+ suite.configure(doc=self.args.doc)
+ if self.args.metadata:
+ suite.configure(metadata=self.args.metadata)
+ return suite
#############################################################################################################################
diff --git a/src/testdoc/parser/testcaseparser.py b/src/testdoc/parser/testcaseparser.py
index 1662d7d..1ce0c05 100644
--- a/src/testdoc/parser/testcaseparser.py
+++ b/src/testdoc/parser/testcaseparser.py
@@ -23,6 +23,7 @@ def parse_test(self,
suite_info["tests"].append(test_info)
return suite_info
+ # Consider tags via officially provided robot api
def consider_tags(self, suite: TestSuite) -> TestSuite:
if len(self.args.include) > 0:
suite.configure(include_tags=self.args.include)
diff --git a/src/testdoc/parser/testsuiteparser.py b/src/testdoc/parser/testsuiteparser.py
index fe06638..606ca5a 100644
--- a/src/testdoc/parser/testsuiteparser.py
+++ b/src/testdoc/parser/testsuiteparser.py
@@ -2,6 +2,7 @@
from robot.api import SuiteVisitor, TestSuite
from .testcaseparser import TestCaseParser
+from .modifier.suitefilemodifier import SuiteFileModifier
class RobotSuiteParser(SuiteVisitor):
def __init__(self):
@@ -23,7 +24,7 @@ def visit_suite(self, suite):
"total_tests": 0,
"tests": [],
"sub_suites": [],
- "metadata": None
+ "metadata": "
".join([f"{k}: {v}" for k, v in suite.metadata.items()]) if suite.metadata else None
}
# Parse Test Cases
@@ -39,6 +40,7 @@ def visit_suite(self, suite):
def parse_suite(self, suite_path):
suite = TestSuite.from_file_system(suite_path)
suite = TestCaseParser().consider_tags(suite)
+ suite = SuiteFileModifier()._modify_root_suite_details(suite)
suite.visit(self)
return self.suites
@@ -65,4 +67,4 @@ def _is_directory(self, suite) -> bool:
def _already_parsed(self, suite):
existing_suite = next((s for s in self.suites if s["name"] == suite.name), None)
if existing_suite:
- return
\ No newline at end of file
+ return