robotframework-testdoc 0.2.3__py3-none-any.whl → 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robotframework-testdoc
3
- Version: 0.2.3
3
+ Version: 0.2.6
4
4
  Summary: A CLI Tool to generate a Test Documentation for your RobotFramework Test Scripts.
5
5
  Author-email: Marvin Klerx <marvinklerx20@gmail.com>
6
6
  License: Apache-2.0
@@ -1,28 +1,28 @@
1
- robotframework_testdoc-0.2.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1
+ robotframework_testdoc-0.2.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
2
2
  testdoc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  testdoc/__main__.py,sha256=09c4nsw4Vnp1LaK0CnlofJQFbKmeFexOXyTBDY9NrFk,67
4
- testdoc/cli.py,sha256=MqY7MAVoFwW-vv1tuJ7pZuzcfqa1aEAbyyQWaFJhWMc,5002
4
+ testdoc/cli.py,sha256=el9eqTr4MjxF91XancigF6U7nJ5ffSXw7Wnhna3OkW0,5027
5
5
  testdoc/default.toml,sha256=PK7O2gat8326ZYOXBC1mt6-5ceBhdbgs0BL1uo4XLjQ,87
6
- testdoc/testdoc.py,sha256=cVJguXoFhkCM2nkUlZGB8m-6rBhKwthFFtdtz0T2l4Q,772
6
+ testdoc/testdoc.py,sha256=WpghXZ_frebqJ0BkP_5EM34bVfyLTaCr1z-lYbitS08,762
7
7
  testdoc/helper/cliargs.py,sha256=nsgxXho8QAtcxF5B0OCriLuUEByGQwagDkvR7ISAGWA,2627
8
8
  testdoc/helper/datetimeconverter.py,sha256=1IuJ_rZlKKut3pallS9WSdlQ00YNQX2Nhf2oYWt7QDc,159
9
9
  testdoc/helper/logger.py,sha256=STPEEdMIGpK004xHDskj8zzW3knBWP05GllYajQMaSY,272
10
- testdoc/helper/pathconverter.py,sha256=jVFJrBz7-DqHcGYzcwcLhpYGUOmA2xZSoJdsaPtGQJY,1656
10
+ testdoc/helper/pathconverter.py,sha256=iY7VBofJxiQHevlLO_kek80mwmaB9gdnHWmUzHowCqk,2096
11
11
  testdoc/helper/toml_reader.py,sha256=JUpCdUQAwS-zImH0fU9leziM8Mc9CXAAHFUs6E0eQRA,323
12
12
  testdoc/html/images/robotframework.svg,sha256=w1yNL6XtuHOCCwzjGX3pZQG7ZcJghzllvc7cQ9MKKbQ,1426
13
+ testdoc/html/rendering/render.py,sha256=Q_zDQHPrr00tHlzEGdDuHV66jljbSFBIn3XuLgJKdPw,1957
13
14
  testdoc/html/templates/v1/jinja_template_01.html,sha256=H0CVKV3HljrdQeT_4hWJq3xNw6kc5vqiJtzCXoWBtdY,18389
14
15
  testdoc/html/templates/v1/jinja_template_02.html,sha256=0CFAqCHQ035hsHgxZsirHgsdZO5-jdUH9SUQmy3EHkg,5152
15
- testdoc/html/templates/v2/jinja_template_03.html,sha256=gpQj8Fso-7sdQmC6rL_ym29CVf5fQXL0l0FoT7unS7c,16136
16
+ testdoc/html/templates/v2/jinja_template_03.html,sha256=r0PJIgDp8oATZBp2hHD30djGZRMpSJA9GKuXa0HkCFc,15886
16
17
  testdoc/html/themes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
18
  testdoc/html/themes/theme_config.py,sha256=3AFUSoddhAZswcEsshbvqcnmbLOFaBYUFy-lrfNBV3Q,1495
18
19
  testdoc/html/themes/themes.py,sha256=6xlHW7O-XO9Z1B33_aRwzffkuWR65jM7CYXsZWUjdmY,1091
19
- testdoc/html_rendering/render.py,sha256=Ij6Jtut7oUCM-z_FIz04A0showacIAIqbkArBg7I2u8,1969
20
- testdoc/parser/testcaseparser.py,sha256=CXZRS1mvHx1O6G2FgqF8T_dWp1RcSJbeN7Le0ZInows,6405
21
- testdoc/parser/testsuiteparser.py,sha256=K7nHa6Kf64kixlGrlsv7leIDl30ct01TE0jxgnXJxl8,2788
22
- testdoc/parser/modifier/sourceprefixmodifier.py,sha256=Vy_keEKztF7UrjtWjmkU7usGR7E-xLvxJOWocPRu6KI,3950
20
+ testdoc/parser/testcaseparser.py,sha256=tfaBfcz2t8Iiu558oDNCGQjQuWphbJTJlsInCTTTgFU,6835
21
+ testdoc/parser/testsuiteparser.py,sha256=7-t_Kdw6527x_bQ_IWJgrvUnGC2umK9mjjJToWVWC_k,6020
22
+ testdoc/parser/modifier/sourceprefixmodifier.py,sha256=uJTuF3KxtDt4m4nqJ9yF6NzdsjXUZGhRYsP9y9hqQ5k,3964
23
23
  testdoc/parser/modifier/suitefilemodifier.py,sha256=OuDuleQj4dRjUcu0AROEPZ-2vR3lWJfWmQVuoWLkXuY,4865
24
- robotframework_testdoc-0.2.3.dist-info/METADATA,sha256=2dnkYPT0Z_y3dWX9oYHYqKp62QhdcfBSjgBfJcQ1upw,6947
25
- robotframework_testdoc-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- robotframework_testdoc-0.2.3.dist-info/entry_points.txt,sha256=BUHy23mdlGCqYOWpsvRhSb1c0tPMzIwyTwr-sHI6xUs,45
27
- robotframework_testdoc-0.2.3.dist-info/top_level.txt,sha256=p1axpYooAmdwwXQOzFsSXF3u_-88QFKCDxPf67siv3Y,8
28
- robotframework_testdoc-0.2.3.dist-info/RECORD,,
24
+ robotframework_testdoc-0.2.6.dist-info/METADATA,sha256=HJPzhuHpUoO2EWXPhaxBnU1aKlyz_xXOffhZXcgj14A,6947
25
+ robotframework_testdoc-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ robotframework_testdoc-0.2.6.dist-info/entry_points.txt,sha256=BUHy23mdlGCqYOWpsvRhSb1c0tPMzIwyTwr-sHI6xUs,45
27
+ robotframework_testdoc-0.2.6.dist-info/top_level.txt,sha256=p1axpYooAmdwwXQOzFsSXF3u_-88QFKCDxPf67siv3Y,8
28
+ robotframework_testdoc-0.2.6.dist-info/RECORD,,
testdoc/cli.py CHANGED
@@ -25,7 +25,7 @@ CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
25
25
  @click.option("-c", "--configfile", required=False, help="Optional .toml configuration file (includes all cmd-args)")
26
26
  @click.option("-v", "--verbose", is_flag=True, required=False, help="More precise debugging into shell")
27
27
  @click.version_option(package_name='robotframework-testdoc')
28
- @click.argument("PATH")
28
+ @click.argument("PATH", nargs=-1, required=True)
29
29
  @click.argument("OUTPUT")
30
30
  def main(
31
31
  title,
@@ -15,11 +15,13 @@ class PathConverter():
15
15
  config_path = self.args.config_file
16
16
 
17
17
  # Convert path to suite file / directory
18
- suite_path = PathConverter().conv_generic_path(path=suite_path)
19
- if ".robot" in suite_path:
20
- msg = f'Suite File: "{str(suite_path).split("/")[-1]}"'
18
+ if type(suite_path) is tuple:
19
+ suite_path = list(suite_path)
20
+ for idx, item in enumerate(suite_path):
21
+ _mod = PathConverter().conv_generic_path(item)
22
+ suite_path[idx] = _mod
21
23
  else:
22
- msg = f"Suite Directory: '{suite_path}'"
24
+ suite_path = PathConverter().conv_generic_path(path=suite_path)
23
25
 
24
26
  # Convert path to output file
25
27
  output_path = PathConverter().conv_generic_path(path=output_path)
@@ -30,6 +32,16 @@ class PathConverter():
30
32
 
31
33
  # Print to console
32
34
  if self.args.verbose_mode:
35
+ msg = ""
36
+ if type(suite_path) is not list:
37
+ suite_path = list(suite_path)
38
+
39
+ for item in suite_path:
40
+ if ".robot" in suite_path:
41
+ msg += f'Suite File: "{str(suite_path).split("/")[-1]}"\n'
42
+ else:
43
+ msg += f"Suite Directory: '{suite_path}'\n"
44
+
33
45
  Logger().Log("=== TestDoc ===")
34
46
  Logger().LogKeyValue("Generating Test Documentation for: ", msg)
35
47
  Logger().LogKeyValue("Saving to output file: ", output_path)
@@ -1,10 +1,10 @@
1
1
  from jinja2 import Environment, FileSystemLoader
2
2
  import os
3
3
 
4
- from ..html.themes.theme_config import ThemeConfig
5
- from ..helper.cliargs import CommandLineArguments
6
- from ..helper.datetimeconverter import DateTimeConverter
7
- from ..helper.logger import Logger
4
+ from ...html.themes.theme_config import ThemeConfig
5
+ from ...helper.cliargs import CommandLineArguments
6
+ from ...helper.datetimeconverter import DateTimeConverter
7
+ from ...helper.logger import Logger
8
8
 
9
9
  class TestDocHtmlRendering():
10
10
 
@@ -17,11 +17,11 @@ class TestDocHtmlRendering():
17
17
  if self.args.html_template == "v1":
18
18
  self.HTML_TEMPLATE_VERSION = self.args.html_template
19
19
  self.HTML_TEMPLATE_NAME = "jinja_template_01.html"
20
- self.TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "html", "templates", self.HTML_TEMPLATE_VERSION)
20
+ self.TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "templates", self.HTML_TEMPLATE_VERSION)
21
21
  elif self.args.html_template == "v2":
22
22
  self.HTML_TEMPLATE_VERSION = self.args.html_template
23
23
  self.HTML_TEMPLATE_NAME = "jinja_template_03.html"
24
- self.TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "html", "templates", self.HTML_TEMPLATE_VERSION)
24
+ self.TEMPLATE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "templates", self.HTML_TEMPLATE_VERSION)
25
25
  else:
26
26
  raise ValueError(f"CLI Argument 'html_template' got value '{self.args.html_template}' - value not known!")
27
27
 
@@ -77,11 +77,7 @@
77
77
  <tr>
78
78
  <td style="width: 10%; font-weight: bold;">🏷 Tags:</td>
79
79
  <td style="text-align: left;">
80
- {% if test.tags and test.tags is string %}
81
- {{ test.tags }}
82
- {% else %}
83
- {{ test.tags | join(', ') }}
84
- {% endif %}
80
+ {{ test.tags | join(', ') }}
85
81
  </td>
86
82
  </tr>
87
83
  {% endif %}
@@ -85,7 +85,7 @@ class GitLabModifier():
85
85
  git_root = self._get_git_root(file_path)
86
86
  git_branch = self._get_git_branch(git_root)
87
87
  if not git_root:
88
- return "GitLink error"
88
+ return "Unable to fetch GitLab URL!"
89
89
  rel_path = os.path.relpath(file_path, git_root).replace(os.sep, "/")
90
90
  return prefix.rstrip("/") + "/-/blob/" + git_branch + "/" + rel_path
91
91
 
@@ -19,7 +19,7 @@ class TestCaseParser():
19
19
  "name": test.name,
20
20
  "doc": "<br>".join(line.replace("\\n","") for line in test.doc.splitlines()
21
21
  if line.strip()) if test.doc else "No Test Case Documentation Available",
22
- "tags": test.tags if test.tags else "No Tags Configured",
22
+ "tags": test.tags if test.tags else ["No Tags Configured"],
23
23
  "source": str(test.source),
24
24
  "keywords": self._keyword_parser(test.body)
25
25
  }
@@ -47,7 +47,7 @@ class TestCaseParser():
47
47
 
48
48
  # Fallback in case of no keywords
49
49
  if len(_keyword_object) == 0:
50
- return "No Keyword Calls in Test"
50
+ return ["No Keyword Calls in Test"]
51
51
  return _keyword_object
52
52
 
53
53
  def _handle_keyword_types(self, kw: Keyword, indent: int = 0):
@@ -105,6 +105,19 @@ class TestCaseParser():
105
105
  result.extend(self._handle_keyword_types(subkw, indent=indent+1))
106
106
  result.append(f"{_indent}END")
107
107
 
108
+ # GROUP loop
109
+ elif kw_type == "GROUP":
110
+ header = f"{_indent}GROUP"
111
+ if not kw.name == "":
112
+ header += f"{_sd}{kw.name}"
113
+ if hasattr(kw, 'condition') and kw.condition:
114
+ header += f" {kw.condition}"
115
+ result.append(header)
116
+ if hasattr(kw, 'body'):
117
+ for subkw in kw.body:
118
+ result.extend(self._handle_keyword_types(subkw, indent=indent+1))
119
+ result.append(f"{_indent}END")
120
+
108
121
  # WHILE loop
109
122
  elif kw_type == "WHILE":
110
123
  header = f"{_indent}WHILE"
@@ -134,13 +147,9 @@ class TestCaseParser():
134
147
  elif kw_type in ("BREAK", "CONTINUE", "RETURN", "ERROR"):
135
148
  entry = f"{_indent}{kw_type}"
136
149
  if hasattr(kw, 'values') and kw.values:
137
- entry += f" {' '.join(kw.values)}"
150
+ entry += f" {_sd.join(kw.values)}"
138
151
  result.append(entry)
139
152
 
140
- # Other types
141
- elif kw_type in ("COMMENT", "EMPTY"):
142
- pass
143
-
144
153
  # Unknown types
145
154
  elif hasattr(kw, 'body'):
146
155
  for subkw in kw.body:
@@ -1,15 +1,27 @@
1
+ # Portions of this file are derived from Robot Framework, licensed under the Apache License 2.0.
2
+ # Derived code: see class `RobotSuiteFiltering`.
1
3
  import os
2
4
  from pathlib import Path
3
5
 
4
6
  from robot.api import SuiteVisitor, TestSuite
5
7
  from .testcaseparser import TestCaseParser
6
8
  from .modifier.suitefilemodifier import SuiteFileModifier
9
+ from ..helper.cliargs import CommandLineArguments
10
+ from..helper.pathconverter import PathConverter
11
+
12
+ from robot.conf import RobotSettings
13
+ from robot.running import TestSuiteBuilder
14
+ from robot.testdoc import USAGE
15
+ from robot.utils import (
16
+ abspath, Application, is_list_like
17
+ )
7
18
 
8
19
  class RobotSuiteParser(SuiteVisitor):
9
20
  def __init__(self):
10
21
  self.suite_counter = 0
11
22
  self.suites = []
12
23
  self.tests = []
24
+ self.args = CommandLineArguments().data
13
25
 
14
26
  def visit_suite(self, suite):
15
27
 
@@ -19,7 +31,7 @@ class RobotSuiteParser(SuiteVisitor):
19
31
  # Test Suite Parser
20
32
  suite_info = {
21
33
  "id": str(suite.longname).lower().replace(".", "_").replace(" ", "_"),
22
- "filename": str(Path(suite.source).name),
34
+ "filename": str(Path(suite.source).name) if suite.source else suite.name,
23
35
  "name": suite.name,
24
36
  "doc": "<br>".join(line.replace("\\n","") for line in suite.doc.splitlines() if line.strip()) if suite.doc else None,
25
37
  "is_folder": self._is_directory(suite),
@@ -41,9 +53,14 @@ class RobotSuiteParser(SuiteVisitor):
41
53
  suite_info["total_tests"] = total_tests
42
54
  self.suites.append(suite_info)
43
55
 
44
- def parse_suite(self, suite_path):
45
- suite = TestSuite.from_file_system(suite_path)
46
- suite = TestCaseParser().consider_tags(suite)
56
+ def parse_suite(self):
57
+ # Use official Robot Framework Application Package to parse cli arguments and modify suite object.
58
+ robot_options = self._convert_args()
59
+ _rfs = RobotSuiteFiltering()
60
+ _rfs.execute_cli(robot_options, False)
61
+ suite = _rfs._suite_object
62
+
63
+ # Custom suite object modification with new test doc library
47
64
  suite = SuiteFileModifier()._modify_root_suite_details(suite)
48
65
  suite.visit(self)
49
66
  return self.suites
@@ -72,3 +89,60 @@ class RobotSuiteParser(SuiteVisitor):
72
89
  existing_suite = next((s for s in self.suites if s["name"] == suite.name), None)
73
90
  if existing_suite:
74
91
  return
92
+
93
+ def _convert_args(self):
94
+ """ Convert given cli args to match internal robotframework syntax """
95
+ _include = self.args.include
96
+ _exclude = self.args.exclude
97
+ _source = self.args.suite_file
98
+ # Type Conversions
99
+ if type(_include) is not list:
100
+ _include = list(_include)
101
+ if type(_exclude) is not list:
102
+ _exclude = list(_exclude)
103
+ if type(_source) is not list:
104
+ _source = list(_source)
105
+
106
+ # Format / Syntax Conversions
107
+ robot_options = []
108
+ for item in _include:
109
+ robot_options.append("-i")
110
+ robot_options.append(f"{item}")
111
+ for item in _exclude:
112
+ robot_options.append("-e")
113
+ robot_options.append(f"{item}")
114
+ for item in _source:
115
+ _os_indep_path = PathConverter().conv_generic_path(item)
116
+ robot_options.append(f"{_os_indep_path}")
117
+ robot_options.append(self.args.output_file)
118
+ return robot_options
119
+
120
+ class RobotSuiteFiltering(Application):
121
+ """ Use official RF Application package to build test suite object with given cli options & arguments """
122
+ OPTIONS = """
123
+ Options
124
+ =======
125
+ NOT SUPPORTED YET: -T --title title Set the title of the generated documentation.
126
+ Underscores in the title are converted to spaces.
127
+ The default title is the name of the top level suite.
128
+ NOT SUPPORTED YET: -N --name name Override the name of the top level suite.
129
+ NOT SUPPORTED YET: -D --doc document Override the documentation of the top level suite.
130
+ NOT SUPPORTED YET: -M --metadata name:value * Set/override metadata of the top level suite.
131
+ NOT SUPPORTED YET: -G --settag tag * Set given tag(s) to all test cases.
132
+ NOT SUPPORTED YET: -t --test name * Include tests by name.
133
+ NOT SUPPORTED YET: -s --suite name * Include suites by name.
134
+ -i --include tag * Include tests by tags.
135
+ -e --exclude tag * Exclude tests by tags.
136
+ """
137
+ def __init__(self):
138
+ self._suite_object = None
139
+ Application.__init__(self, USAGE, arg_limits=(2,))
140
+
141
+ def main(self, datasources, title=None, **options):
142
+ abspath(datasources.pop())
143
+ settings = RobotSettings(options)
144
+ if not is_list_like(datasources):
145
+ datasources = [datasources]
146
+ suite = TestSuiteBuilder(process_curdir=False).build(*datasources)
147
+ suite.configure(**settings.suite_config)
148
+ self._suite_object = suite
testdoc/testdoc.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .helper.pathconverter import PathConverter
2
2
  from .parser.testsuiteparser import RobotSuiteParser
3
- from .html_rendering.render import TestDocHtmlRendering
3
+ from .html.rendering.render import TestDocHtmlRendering
4
4
  from .parser.modifier.suitefilemodifier import SuiteFileModifier
5
5
 
6
6
  class TestDoc():
@@ -10,7 +10,7 @@ class TestDoc():
10
10
  suite_path, output_path, config_path = PathConverter().path_convertion()
11
11
 
12
12
  # Parse suite object & return complete suite object with all information
13
- suite_object = RobotSuiteParser().parse_suite(suite_path)
13
+ suite_object = RobotSuiteParser().parse_suite()
14
14
 
15
15
  # Run SuiteFileModifier to modify the test suite object
16
16
  suite_object = SuiteFileModifier().run(suite_object)