Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,51 @@ For using this config file, just call the following command:
testdoc -c path/to/config.toml tests/ TestDocumentation.html
```

### pyproject.toml vs. custom toml file

Using the ``pyproject`` requires to define the ``testdoc`` sections with the prefix ``tool.``
Example section start: ``[tool.testdoc]``

Using your own custom toml-file, does not require you to use the prefix. Here, you can just use ``[testdoc]`` as section header.


### Example Configuration File
```toml
[tool.testdoc]
title = "New title of HTML document"
name = "New name of root suite element"
doc = "New doc text of root suite element"
sourceprefix = "gitlab::https://gitlab.com/myrepo/repo_path"
include = ["TagA", "TagB"]
exclude = ["TagC"]
hide_tags = true
hide_test_doc = true
hide_suite_doc = true
hide_source = true
hide_keywords = true
style = "blue"
verbose_mode = false

[tool.testdoc.metadata]
Author = "Your-Name"
Version = "1.0.0"
Source = "AnySourceAsMetaData"

[tool.testdoc.colors]
# Use predefined theme:
default = "blue"
# OR
# Use custom colors:
background = "#000028"
inner_color = "#000028"
button_active_color = "#193966"
button_hover_color = "#193966"
border_color = "#CCCCCC"
text_color = "#CCCCCC"
title_color = "#00ffb9"
robot_icon = "#00ffb9"
```

## HTML Template Selection

You can choose between multiple HTML template for the design of your test documentation.
Expand Down
2 changes: 1 addition & 1 deletion atest/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ def test_cli_cmd_verbose():
assert result.exit_code == 0
assert "Generated" in result.output
assert "output.html" in result.output
assert "test_cli.robot" in result.output
assert "Saving" in result.output
assert os.path.exists(output)
67 changes: 34 additions & 33 deletions src/testdoc/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import click
import os

from .testdoc import TestDoc
from .helper.cliargs import CommandLineArguments
Expand Down Expand Up @@ -47,8 +46,12 @@ def main(
path,
output,
):
"""
Welcome to robotframework-testdoc - the new test documentation generator for your Robot Framework tests!
"""Welcome to robotframework-testdoc - the new test documentation generator for your Robot Framework tests!

# Basic Usage:
$ testdoc tests/ TestDocumentation.html

See more in the README.md of the GitHub Project: https://github.com/MarvKler/robotframework-testdoc/blob/main/README.md
"""
color = "green"
entrypoint_msg = """
Expand All @@ -62,38 +65,36 @@ def main(
click.echo(click.style(entrypoint_msg, fg=color)
)

args_instance = CommandLineArguments()
if configfile:
if os.path.exists(configfile):
args_instance.load_from_config_file(configfile)
else:
click.echo(click.style(f"⚠️ Config File not found: {configfile}", fg="yellow"))
args_to_set = dict(
title=title,
name=name,
doc=doc,
metadata=dict(item.split("=", 1) for item in metadata) if metadata else None,
sourceprefix=sourceprefix,
include=list(include),
exclude=list(exclude),
hide_tags=hide_tags,
hide_test_doc=hide_test_doc,
hide_suite_doc=hide_suite_doc,
hide_source=hide_source,
hide_keywords=hide_keywords,
style=style,
html_template=html_template,
config_file=configfile,
verbose_mode=verbose,
suite_file=list(path),
output_file=output,
)

# Expose CLI args
args_to_set = {k: v for k, v in args_to_set.items() if v is not None}
CommandLineArguments().set_args(**args_to_set)

args = args_instance.data
cli_params = {
"title": title or None,
"name": name or None,
"doc": doc or None,
"metadata": dict(item.split("=", 1) for item in metadata) if metadata else None,
"sourceprefix": sourceprefix,
"include": list(include) if include else None,
"exclude": list(exclude) if exclude else None,
"hide_tags": hide_tags or None,
"hide_test_doc": hide_test_doc or None,
"hide_suite_doc": hide_suite_doc or None,
"hide_source": hide_source or None,
"hide_keywords": hide_keywords or None,
"verbose_mode": verbose or None,
"style": style or None,
"html_template": html_template or None,
"config_file": configfile or None,
}
args.suite_file = path
args.output_file = output
# Read & expose TOML args
if configfile:
from .helper.toml_reader import TOMLReader
TOMLReader().load_from_config_file(configfile)

for key, value in cli_params.items():
if value is not None:
setattr(args, key, value)
TestDoc().main()

if __name__ == "__main__":
Expand Down
165 changes: 91 additions & 74 deletions src/testdoc/helper/cliargs.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,100 @@
from dataclasses import dataclass, field
from typing import Any, List
from .toml_reader import TOMLReader
import os

@dataclass
class CommandLineArgumentsData:
title: str = "Robot Framework - Test Documentation"
name: str = None
doc: str = None
metadata: dict = None
sourceprefix: str = None
include: List[str] = field(default_factory=list)
exclude: List[str] = field(default_factory=list)
hide_tags: bool = False
hide_test_doc: bool = False
hide_suite_doc: bool = False
hide_source: bool = False
hide_keywords: bool = False
config_file: str = None
verbose_mode: bool = False
suite_file: str = None
style: str = None
html_template: str = "v2"
output_file: str = None
colors: dict = None

class CommandLineArguments:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)

cls.data = CommandLineArgumentsData()
cls._instance._args = {}
return cls._instance

###
### Load configuration file
###
def load_from_config_file(self, file_path: str):
config = TOMLReader()._read_toml(file_path)
_is_pyproject = self._is_pyproject_config(file_path)
if _is_pyproject:
self._handle_pyproject_config(config)
else:
self._handle_custom_config(config)

###
### Read pyproject.toml
###
def _handle_pyproject_config(self, config: dict[str, Any]):
testdoc_config = config.get("tool", {}).get("testdoc", {})

if "colors" in testdoc_config:
self.data.colors = testdoc_config["colors"]

if "metadata" in testdoc_config:
if hasattr(self.data, "metadata"):
setattr(self.data, "metadata", testdoc_config["metadata"])

for key, value in testdoc_config.items():
if key in ("colors", "metadata"):
continue
if hasattr(self.data, key):
setattr(self.data, key, value)

###
### Read custom.toml
###
def _handle_custom_config(self, config: dict[str, Any]):
if "colors" in config:
self.data.colors = config["colors"]

for key, value in config.items():
if hasattr(self.data, key):
setattr(self.data, key, value)

#####################################################################################
def update_args_if_not_set(self, **kwargs):
for key, value in kwargs.items():
if key not in self._args:
self._args[key] = value

def set_args(self, **kwargs):
self._args = kwargs

def _is_pyproject_config(self, file_path) -> bool:
return os.path.basename(file_path) == "pyproject.toml"
def __getattr__(self, name):
if name in self._args:
return self._args[name]
raise AttributeError(f"'CommandLineArguments' object has no attribute '{name}'")

@property
def title(self):
return self._args.get("title", "Robot Framework - Test Documentation")

@property
def name(self):
return self._args.get("name", None)

@property
def doc(self):
return self._args.get("doc", None)

@property
def metadata(self):
return self._args.get("metadata", None)

@property
def sourceprefix(self):
return self._args.get("sourceprefix", None)

@property
def include(self):
return self._args.get("include", [])

@property
def exclude(self):
return self._args.get("exclude", [])

@property
def hide_tags(self):
return self._args.get("hide_tags", False)

@property
def hide_test_doc(self):
return self._args.get("hide_test_doc", False)

@property
def hide_suite_doc(self):
return self._args.get("hide_suite_doc", False)

@property
def hide_source(self):
return self._args.get("hide_source", False)

@property
def hide_keywords(self):
return self._args.get("hide_keywords", False)

@property
def config_file(self):
return self._args.get("config_file", None)

@property
def verbose_mode(self):
return self._args.get("verbose_mode", False)

@property
def suite_file(self):
return self._args.get("suite_file", None)

@property
def style(self):
return self._args.get("style", None)

@property
def html_template(self):
return self._args.get("html_template", "v2")

@property
def output_file(self):
return self._args.get("output_file", None)

@property
def colors(self):
return self._args.get("colors", None)

#####################################################################################
@property
def all_as_dict(self):
return self._args
38 changes: 3 additions & 35 deletions src/testdoc/helper/pathconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,16 @@
class PathConverter():

def __init__(self):
self.args = CommandLineArguments().data
self.args = CommandLineArguments()

def path_convertion(self) -> str:

suite_path = self.args.suite_file
output_path = self.args.output_file
config_path = self.args.config_file

# Convert path to suite file / directory
if type(suite_path) is tuple:
suite_path = list(suite_path)
for idx, item in enumerate(suite_path):
_mod = PathConverter().conv_generic_path(item)
suite_path[idx] = _mod
else:
suite_path = PathConverter().conv_generic_path(path=suite_path)

# Convert path to output file
output_path = PathConverter().conv_generic_path(path=output_path)

# Convert path to config file
if self.args.config_file:
config_path = PathConverter().conv_generic_path(path=config_path)


# Print to console
if self.args.verbose_mode:
msg = ""
if type(suite_path) is not list:
suite_path = list(suite_path)

for item in suite_path:
if ".robot" in suite_path:
msg += f'Suite File: "{str(suite_path).split("/")[-1]}"\n'
else:
msg += f"Suite Directory: '{suite_path}'\n"

Logger().Log("=== TestDoc ===")
Logger().LogKeyValue("Generating Test Documentation for: ", msg)
Logger().LogKeyValue("Saving to output file: ", output_path)
Logger().LogKeyValue("Using config file: ", config_path) if config_path else None

return suite_path, output_path, config_path
return output_path

def conv_generic_path(self,
path: str
Expand Down
Loading