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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "robotframework-testdoc"
version = "0.2.3"
version = "0.2.4"
description = "A CLI Tool to generate a Test Documentation for your RobotFramework Test Scripts."
readme = "README.md"
requires-python = ">=3.7"
Expand Down
2 changes: 1 addition & 1 deletion src/testdoc/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
@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.version_option(package_name='robotframework-testdoc')
@click.argument("PATH")
@click.argument("PATH", nargs=-1, required=True)
@click.argument("OUTPUT")
def main(
title,
Expand Down
20 changes: 16 additions & 4 deletions src/testdoc/helper/pathconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ def path_convertion(self) -> str:
config_path = self.args.config_file

# Convert path to suite file / directory
suite_path = PathConverter().conv_generic_path(path=suite_path)
if ".robot" in suite_path:
msg = f'Suite File: "{str(suite_path).split("/")[-1]}"'
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:
msg = f"Suite Directory: '{suite_path}'"
suite_path = PathConverter().conv_generic_path(path=suite_path)

# Convert path to output file
output_path = PathConverter().conv_generic_path(path=output_path)
Expand All @@ -30,6 +32,16 @@ def path_convertion(self) -> str:

# 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)
Expand Down
82 changes: 78 additions & 4 deletions src/testdoc/parser/testsuiteparser.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
# Portions of this file are derived from Robot Framework, licensed under the Apache License 2.0.
# Derived code: see class `RobotSuiteFiltering`.
import os
from pathlib import Path

from robot.api import SuiteVisitor, TestSuite
from .testcaseparser import TestCaseParser
from .modifier.suitefilemodifier import SuiteFileModifier
from ..helper.cliargs import CommandLineArguments
from..helper.pathconverter import PathConverter

from robot.conf import RobotSettings
from robot.running import TestSuiteBuilder
from robot.testdoc import USAGE
from robot.utils import (
abspath, Application, is_list_like
)

class RobotSuiteParser(SuiteVisitor):
def __init__(self):
self.suite_counter = 0
self.suites = []
self.tests = []
self.args = CommandLineArguments().data

def visit_suite(self, suite):

Expand All @@ -19,7 +31,7 @@ def visit_suite(self, suite):
# Test Suite Parser
suite_info = {
"id": str(suite.longname).lower().replace(".", "_").replace(" ", "_"),
"filename": str(Path(suite.source).name),
"filename": str(Path(suite.source).name) if suite.source else suite.name,
"name": suite.name,
"doc": "<br>".join(line.replace("\\n","") for line in suite.doc.splitlines() if line.strip()) if suite.doc else None,
"is_folder": self._is_directory(suite),
Expand All @@ -41,9 +53,14 @@ def visit_suite(self, suite):
suite_info["total_tests"] = total_tests
self.suites.append(suite_info)

def parse_suite(self, suite_path):
suite = TestSuite.from_file_system(suite_path)
suite = TestCaseParser().consider_tags(suite)
def parse_suite(self):
# Use official Robot Framework Application Package to parse cli arguments and modify suite object.
robot_options = self._convert_args()
_rfs = RobotSuiteFiltering()
_rfs.execute_cli(robot_options, False)
suite = _rfs._suite_object

# Custom suite object modification with new test doc library
suite = SuiteFileModifier()._modify_root_suite_details(suite)
suite.visit(self)
return self.suites
Expand Down Expand Up @@ -72,3 +89,60 @@ def _already_parsed(self, suite):
existing_suite = next((s for s in self.suites if s["name"] == suite.name), None)
if existing_suite:
return

def _convert_args(self):
""" Convert given cli args to match internal robotframework syntax """
_include = self.args.include
_exclude = self.args.exclude
_source = self.args.suite_file
# Type Conversions
if type(_include) is not list:
_include = list(_include)
if type(_exclude) is not list:
_exclude = list(_exclude)
if type(_source) is not list:
_source = list(_source)

# Format / Syntax Conversions
robot_options = []
for item in _include:
robot_options.append("-i")
robot_options.append(f"{item}")
for item in _exclude:
robot_options.append("-e")
robot_options.append(f"{item}")
for item in _source:
_os_indep_path = PathConverter().conv_generic_path(item)
robot_options.append(f"{_os_indep_path}")
robot_options.append(self.args.output_file)
return robot_options

class RobotSuiteFiltering(Application):
""" Use official RF Application package to build test suite object with given cli options & arguments """
OPTIONS = """
Options
=======
NOT SUPPORTED YET: -T --title title Set the title of the generated documentation.
Underscores in the title are converted to spaces.
The default title is the name of the top level suite.
NOT SUPPORTED YET: -N --name name Override the name of the top level suite.
NOT SUPPORTED YET: -D --doc document Override the documentation of the top level suite.
NOT SUPPORTED YET: -M --metadata name:value * Set/override metadata of the top level suite.
NOT SUPPORTED YET: -G --settag tag * Set given tag(s) to all test cases.
NOT SUPPORTED YET: -t --test name * Include tests by name.
NOT SUPPORTED YET: -s --suite name * Include suites by name.
-i --include tag * Include tests by tags.
-e --exclude tag * Exclude tests by tags.
"""
def __init__(self):
self._suite_object = None
Application.__init__(self, USAGE, arg_limits=(2,))

def main(self, datasources, title=None, **options):
abspath(datasources.pop())
settings = RobotSettings(options)
if not is_list_like(datasources):
datasources = [datasources]
suite = TestSuiteBuilder(process_curdir=False).build(*datasources)
suite.configure(**settings.suite_config)
self._suite_object = suite
2 changes: 1 addition & 1 deletion src/testdoc/testdoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def main(self):
suite_path, output_path, config_path = PathConverter().path_convertion()

# Parse suite object & return complete suite object with all information
suite_object = RobotSuiteParser().parse_suite(suite_path)
suite_object = RobotSuiteParser().parse_suite()

# Run SuiteFileModifier to modify the test suite object
suite_object = SuiteFileModifier().run(suite_object)
Expand Down