testhide-pytest-plugin 0.2.0__tar.gz → 0.2.2__tar.gz

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: testhide-pytest-plugin
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A pytest plugin for creating incremental XML test reports for Testhide system.
5
5
  Author-email: Mykola Kovhanko <thuesdays@gmail.com>
6
6
  License: MIT License
@@ -76,7 +76,10 @@ pytest --reruns 5 --report-xml=junittests.xml
76
76
  ```
77
77
 
78
78
  ## JIRA Integration
79
- To enable JIRA integration, simply provide the connection details as command-line options. Integration is enabled automatically when all three parameters are present.
79
+ The plugin can automatically enrich failure reports with information from JIRA, linking test failures to known bugs and their statuses. There are two ways to configure this integration.
80
+
81
+ ### Method 1: Command-Line Arguments
82
+ You can enable JIRA integration by providing the connection details as command-line options. The integration is activated automatically when all three parameters are present.
80
83
 
81
84
  * **--jira-url**: The URL of your JIRA instance.
82
85
  * **--jira-username**: The username for the connection.
@@ -88,6 +91,24 @@ pytest --report-xml=junittests.xml \
88
91
  --jira-password="your-api-token"
89
92
  ```
90
93
 
94
+ ### Method 2: Programmatic Configuration (for Frameworks)
95
+ If you are developing a test framework plugin and manage credentials in a central configuration object (e.g., a YAML file), you can programmatically set the JIRA options. This avoids exposing credentials in CI scripts.
96
+ Use the `pytest_cmdline_main` hook in your own plugin to set the configuration options before the `testhide-plugin` is configured.
97
+ ```python
98
+ import pytest
99
+
100
+ class MyFrameworkPlugin:
101
+ @pytest.hookimpl(tryfirst=True)
102
+ def pytest_cmdline_main(self, config):
103
+ # Assuming ConfigApp loads your central configuration
104
+ # from a file or environment variables.
105
+ from my_framework.config import ConfigApp
106
+
107
+ config.option.jira_url = ConfigApp.jira.url
108
+ config.option.jira_username = ConfigApp.jira.username
109
+ config.option.jira_password = ConfigApp.jira.password
110
+ ```
111
+
91
112
  ## Extending the Plugin (For Framework Developers)
92
113
  `testhide-pytest-plugin` provides custom hooks for integration with your own plugins, allowing you to inject project-specific metadata into the report.
93
114
 
@@ -37,7 +37,10 @@ pytest --reruns 5 --report-xml=junittests.xml
37
37
  ```
38
38
 
39
39
  ## JIRA Integration
40
- To enable JIRA integration, simply provide the connection details as command-line options. Integration is enabled automatically when all three parameters are present.
40
+ The plugin can automatically enrich failure reports with information from JIRA, linking test failures to known bugs and their statuses. There are two ways to configure this integration.
41
+
42
+ ### Method 1: Command-Line Arguments
43
+ You can enable JIRA integration by providing the connection details as command-line options. The integration is activated automatically when all three parameters are present.
41
44
 
42
45
  * **--jira-url**: The URL of your JIRA instance.
43
46
  * **--jira-username**: The username for the connection.
@@ -49,6 +52,24 @@ pytest --report-xml=junittests.xml \
49
52
  --jira-password="your-api-token"
50
53
  ```
51
54
 
55
+ ### Method 2: Programmatic Configuration (for Frameworks)
56
+ If you are developing a test framework plugin and manage credentials in a central configuration object (e.g., a YAML file), you can programmatically set the JIRA options. This avoids exposing credentials in CI scripts.
57
+ Use the `pytest_cmdline_main` hook in your own plugin to set the configuration options before the `testhide-plugin` is configured.
58
+ ```python
59
+ import pytest
60
+
61
+ class MyFrameworkPlugin:
62
+ @pytest.hookimpl(tryfirst=True)
63
+ def pytest_cmdline_main(self, config):
64
+ # Assuming ConfigApp loads your central configuration
65
+ # from a file or environment variables.
66
+ from my_framework.config import ConfigApp
67
+
68
+ config.option.jira_url = ConfigApp.jira.url
69
+ config.option.jira_username = ConfigApp.jira.username
70
+ config.option.jira_password = ConfigApp.jira.password
71
+ ```
72
+
52
73
  ## Extending the Plugin (For Framework Developers)
53
74
  `testhide-pytest-plugin` provides custom hooks for integration with your own plugins, allowing you to inject project-specific metadata into the report.
54
75
 
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "testhide-pytest-plugin"
9
- version = "0.2.0"
9
+ version = "0.2.2"
10
10
  authors = [
11
11
  { name="Mykola Kovhanko", email="thuesdays@gmail.com" },
12
12
  ]
@@ -67,7 +67,7 @@ class TesthidePlugin:
67
67
  self.report_xml_path = config.option.report_xml
68
68
  self.temp_dir = os.path.join(str(config.rootdir), f".{os.path.basename(self.report_xml_path)}_temp")
69
69
  self.is_xdist_master = not hasattr(config, "workerinput")
70
- self.is_xdist_run = config.option.dist != "no"
70
+ self.is_xdist_run = getattr(config.option, 'dist', 'no') != 'no'
71
71
  self.rerun_counters = {}
72
72
  self.test_reports = {}
73
73
 
@@ -152,7 +152,7 @@ class TesthidePlugin:
152
152
  if summary not in "".join(final_trace_lines):
153
153
  final_trace_lines.append(summary)
154
154
 
155
- return "\n".join(final_trace_lines)
155
+ return "".join(final_trace_lines)
156
156
  else:
157
157
  return summary
158
158
 
@@ -167,6 +167,21 @@ class TesthidePlugin:
167
167
 
168
168
  if report.when in ('setup', 'call') and getattr(report, 'outcome', '') != 'rerun':
169
169
  item._final_report = report
170
+
171
+ if call.when == 'call' and call.excinfo:
172
+ fail_message = str(call.excinfo.value)
173
+ try:
174
+ fail_id = '%s.%s.%s.%s' % \
175
+ (item.module.__name__,
176
+ item.cls.__name__ if item.cls
177
+ else item.module.__name__,
178
+ re.sub(r'\[.+\]$', '', item.name),
179
+ '%s(%s)' % (call.excinfo.typename, fail_message))
180
+ except AttributeError:
181
+ return
182
+ fail_id = fail_id.encode('utf-8')
183
+ fail_id = md5(fail_id).hexdigest()
184
+ item.fail_id = fail_id
170
185
 
171
186
  @pytest.hookimpl(trylast=True)
172
187
  def pytest_runtest_teardown(self, item):
@@ -178,19 +193,17 @@ class TesthidePlugin:
178
193
  if not report:
179
194
  return
180
195
 
181
- filepath, line, name = report.location
196
+ filepath, line, _ = report.location
197
+ name = item.originalname if hasattr(item, 'originalname') else item.name
198
+ name = name.split('[')[0]
182
199
  classname_path = report.nodeid.split('::')
183
200
  if len(classname_path) > 2:
184
201
  classname = ".".join(classname_path[:-1]).replace('/', '.')
185
202
  else:
186
203
  classname = os.path.splitext(os.path.basename(filepath))[0]
187
204
 
188
- testcase_attrs = {
189
- 'classname': classname, 'name': name, 'file': str(filepath),
190
- 'line': str(line), 'time': f"{report.duration:.3f}"
191
- }
192
- testcase = ET.Element('testcase', **testcase_attrs)
193
-
205
+ fail_id = getattr(item, 'fail_id', None)
206
+ test_resolution = 'Product Defect'
194
207
  if report.failed:
195
208
  tag = 'error' if report.when == 'setup' else 'failure'
196
209
  failure_message = str(report.longrepr.reprcrash.message) if hasattr(report.longrepr, 'reprcrash') else str(
@@ -198,34 +211,55 @@ class TesthidePlugin:
198
211
 
199
212
  if self.jira_enabled:
200
213
  try:
201
- sub = re.sub(r'\[.+\]$', '', name)
202
- fail_id_str = f"{classname}.{sub}.{failure_message}"
203
- fail_id = md5(fail_id_str.encode('utf-8')).hexdigest()
204
-
205
- issue = self._get_issue_by_test_id(fail_id)
206
- if issue:
207
- issue_text = issue.fields.summary;
208
- issue_type = issue.fields.issuetype.name;
209
- issue_id = issue.permalink()
210
- status_name = issue.fields.status.name;
211
- test_resolution = 'Known issue'
212
- if status_name in ('Verified', 'Closed'):
213
- test_resolution = 'Need to reopen'
214
- elif status_name in ('Resolved', 'In Testing'):
215
- test_resolution = 'Resolved in branch'
216
- failure_message = f'{test_resolution} {issue_id} {issue_type} [{issue_text}]'
217
- else:
218
- failure_message += f"@@testid#{fail_id}"
214
+ if fail_id:
215
+ issue = self._get_issue_by_test_id(fail_id)
216
+ if issue:
217
+ issue_text = issue.fields.summary
218
+ issue_type = issue.fields.issuetype.name
219
+ issue_id = issue.permalink()
220
+ status_name = issue.fields.status.name
221
+ if status_name in ('Verified', 'Closed'):
222
+ if hasattr(issue.fields, 'customfield_10020') and issue.fields.customfield_10020:
223
+ status = issue.fields.customfield_10020.value
224
+ if status_name == 'Verified' and status == 'Verified at Branch':
225
+ test_resolution = 'Verified at Branch'
226
+ else:
227
+ test_resolution = 'Need to reopen'
228
+ else:
229
+ test_resolution = 'Need to reopen'
230
+ elif status_name in ['Resolved', 'In Testing']:
231
+ test_resolution = 'Resolved in branch'
232
+ else:
233
+ test_resolution = 'Known issue'
234
+ failure_message = f'{test_resolution} {issue_id} {issue_type} [{issue_text}]'
219
235
  except Exception as e:
220
236
  self.config.warn('JIRA_MARKER_ERROR', f"JIRA marker failed: {e}")
221
237
 
238
+ testcase_attrs = {
239
+ 'classname': classname, 'name': name, 'file': str(filepath),
240
+ 'line': str(line), 'time': f"{report.duration:.3f}", "fail_id": fail_id, "test_resolution": test_resolution,
241
+ }
242
+ testcase = ET.Element('testcase', **testcase_attrs)
222
243
  failure_element = ET.SubElement(testcase, tag, message=failure_message)
223
244
  failure_element.text = self._get_cleaned_traceback(report)
224
245
 
225
246
  elif report.skipped:
247
+ testcase_attrs = {
248
+ 'classname': classname, 'name': name, 'file': str(filepath),
249
+ 'line': str(line), 'time': f"{report.duration:.3f}", "fail_id": '',
250
+ "test_resolution": "Skipped",
251
+ }
252
+ testcase = ET.Element('testcase', **testcase_attrs)
226
253
  skipped_attrs = {'type': 'pytest.skip', 'message': report.longrepr[2]}
227
254
  ET.SubElement(testcase, 'skipped',
228
255
  **skipped_attrs).text = f"{report.longrepr[0]}:{report.longrepr[1]}: {report.longrepr[2]}"
256
+ else:
257
+ testcase_attrs = {
258
+ 'classname': classname, 'name': name, 'file': str(filepath),
259
+ 'line': str(line), 'time': f"{report.duration:.3f}", "fail_id": '',
260
+ "test_resolution": "Passed",
261
+ }
262
+ testcase = ET.Element('testcase', **testcase_attrs)
229
263
 
230
264
  all_properties = self.config.hook.pytest_testhide_get_test_case_properties(item=item, report=report)
231
265
  flat_properties = [prop for sublist in all_properties for prop in sublist]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testhide-pytest-plugin
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A pytest plugin for creating incremental XML test reports for Testhide system.
5
5
  Author-email: Mykola Kovhanko <thuesdays@gmail.com>
6
6
  License: MIT License
@@ -76,7 +76,10 @@ pytest --reruns 5 --report-xml=junittests.xml
76
76
  ```
77
77
 
78
78
  ## JIRA Integration
79
- To enable JIRA integration, simply provide the connection details as command-line options. Integration is enabled automatically when all three parameters are present.
79
+ The plugin can automatically enrich failure reports with information from JIRA, linking test failures to known bugs and their statuses. There are two ways to configure this integration.
80
+
81
+ ### Method 1: Command-Line Arguments
82
+ You can enable JIRA integration by providing the connection details as command-line options. The integration is activated automatically when all three parameters are present.
80
83
 
81
84
  * **--jira-url**: The URL of your JIRA instance.
82
85
  * **--jira-username**: The username for the connection.
@@ -88,6 +91,24 @@ pytest --report-xml=junittests.xml \
88
91
  --jira-password="your-api-token"
89
92
  ```
90
93
 
94
+ ### Method 2: Programmatic Configuration (for Frameworks)
95
+ If you are developing a test framework plugin and manage credentials in a central configuration object (e.g., a YAML file), you can programmatically set the JIRA options. This avoids exposing credentials in CI scripts.
96
+ Use the `pytest_cmdline_main` hook in your own plugin to set the configuration options before the `testhide-plugin` is configured.
97
+ ```python
98
+ import pytest
99
+
100
+ class MyFrameworkPlugin:
101
+ @pytest.hookimpl(tryfirst=True)
102
+ def pytest_cmdline_main(self, config):
103
+ # Assuming ConfigApp loads your central configuration
104
+ # from a file or environment variables.
105
+ from my_framework.config import ConfigApp
106
+
107
+ config.option.jira_url = ConfigApp.jira.url
108
+ config.option.jira_username = ConfigApp.jira.username
109
+ config.option.jira_password = ConfigApp.jira.password
110
+ ```
111
+
91
112
  ## Extending the Plugin (For Framework Developers)
92
113
  `testhide-pytest-plugin` provides custom hooks for integration with your own plugins, allowing you to inject project-specific metadata into the report.
93
114