behavex 4.2.0__tar.gz → 4.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.
Files changed (101) hide show
  1. {behavex-4.2.0/behavex.egg-info → behavex-4.2.2}/PKG-INFO +37 -7
  2. {behavex-4.2.0 → behavex-4.2.2}/README.md +36 -6
  3. behavex-4.2.2/behavex/__pycache__/__main__.cpython-39.pyc +0 -0
  4. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/arguments.cpython-39.pyc +0 -0
  5. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/conf_mgr.cpython-39.pyc +0 -0
  6. behavex-4.2.2/behavex/__pycache__/environment.cpython-39.pyc +0 -0
  7. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/runner.cpython-39.pyc +0 -0
  8. behavex-4.2.2/behavex/__pycache__/utils.cpython-39.pyc +0 -0
  9. {behavex-4.2.0 → behavex-4.2.2}/behavex/arguments.py +10 -0
  10. {behavex-4.2.0 → behavex-4.2.2}/behavex/environment.py +66 -26
  11. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__pycache__/formatter_manager.cpython-39.pyc +0 -0
  12. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__pycache__/jinja_mgr.cpython-39.pyc +0 -0
  13. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__pycache__/output_strings.cpython-39.pyc +0 -0
  14. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__pycache__/report_html.cpython-39.pyc +0 -0
  15. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__pycache__/report_json.cpython-39.pyc +0 -0
  16. behavex-4.2.2/behavex/outputs/__pycache__/report_utils.cpython-39.pyc +0 -0
  17. behavex-4.2.2/behavex/outputs/__pycache__/report_xml.cpython-39.pyc +0 -0
  18. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/formatter_manager.py +13 -2
  19. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/formatters/__pycache__/allure_behavex_formatter.cpython-39.pyc +0 -0
  20. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/formatters/allure_behavex_formatter.py +36 -31
  21. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/output_strings.py +16 -0
  22. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/report_json.py +7 -0
  23. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/report_utils.py +85 -19
  24. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/report_xml.py +43 -4
  25. {behavex-4.2.0 → behavex-4.2.2}/behavex/runner.py +45 -4
  26. {behavex-4.2.0 → behavex-4.2.2}/behavex/utils.py +7 -0
  27. {behavex-4.2.0 → behavex-4.2.2/behavex.egg-info}/PKG-INFO +37 -7
  28. {behavex-4.2.0 → behavex-4.2.2}/behavex.egg-info/SOURCES.txt +1 -0
  29. {behavex-4.2.0 → behavex-4.2.2}/pyproject.toml +1 -1
  30. {behavex-4.2.0 → behavex-4.2.2}/setup.py +1 -1
  31. {behavex-4.2.0 → behavex-4.2.2}/tests/features/secondary_features/steps/allure_formatter_secondary_steps.py +1 -1
  32. {behavex-4.2.0 → behavex-4.2.2}/tests/features/steps/allure_formatter_steps.py +40 -5
  33. behavex-4.2.0/behavex/__pycache__/environment.cpython-39.pyc +0 -0
  34. behavex-4.2.0/behavex/__pycache__/utils.cpython-39.pyc +0 -0
  35. behavex-4.2.0/behavex/outputs/__pycache__/report_utils.cpython-39.pyc +0 -0
  36. behavex-4.2.0/behavex/outputs/__pycache__/report_xml.cpython-39.pyc +0 -0
  37. {behavex-4.2.0 → behavex-4.2.2}/LICENSE +0 -0
  38. {behavex-4.2.0 → behavex-4.2.2}/MANIFEST.in +0 -0
  39. {behavex-4.2.0 → behavex-4.2.2}/behavex/.DS_Store +0 -0
  40. {behavex-4.2.0 → behavex-4.2.2}/behavex/__init__.py +0 -0
  41. {behavex-4.2.0 → behavex-4.2.2}/behavex/__main__.py +0 -0
  42. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/__init__.cpython-39.pyc +0 -0
  43. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/execution_singleton.cpython-39.pyc +0 -0
  44. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/global_vars.cpython-39.pyc +0 -0
  45. {behavex-4.2.0 → behavex-4.2.2}/behavex/__pycache__/progress_bar.cpython-39.pyc +0 -0
  46. {behavex-4.2.0 → behavex-4.2.2}/behavex/conf_behavex.cfg +0 -0
  47. {behavex-4.2.0 → behavex-4.2.2}/behavex/conf_logging.cfg +0 -0
  48. {behavex-4.2.0 → behavex-4.2.2}/behavex/conf_mgr.py +0 -0
  49. {behavex-4.2.0 → behavex-4.2.2}/behavex/execution_singleton.py +0 -0
  50. {behavex-4.2.0 → behavex-4.2.2}/behavex/global_vars.py +0 -0
  51. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__init__.py +0 -0
  52. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/__pycache__/__init__.cpython-39.pyc +0 -0
  53. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/behavex.css +0 -0
  54. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/bootstrap-theme.css +0 -0
  55. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/bootstrap-theme.min.css +0 -0
  56. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/bootstrap-theme.min.css.map +0 -0
  57. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/bootstrap.css +0 -0
  58. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/bootstrap.min.css +0 -0
  59. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/bootstrap.min.css.map +0 -0
  60. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/css/pace-theme-loading-bar.min.css +0 -0
  61. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  62. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.svg +0 -0
  63. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  64. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  65. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  66. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/bootstrap.js +0 -0
  67. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/bootstrap.min.js +0 -0
  68. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/chart.js +0 -0
  69. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/jquery-1.11.0.min.js +0 -0
  70. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/jquery-ui.min.js +0 -0
  71. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/jquery.tablesorter.min.js +0 -0
  72. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/jquery.tooltipster.min.js +0 -0
  73. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/bootstrap/js/pace.min.js +0 -0
  74. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/jinja/base.jinja2 +0 -0
  75. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/jinja/main.jinja2 +0 -0
  76. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/jinja/manifest.jinja2 +0 -0
  77. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/jinja/xml.jinja2 +0 -0
  78. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/jinja/xml_json.jinja2 +0 -0
  79. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/jinja_mgr.py +0 -0
  80. {behavex-4.2.0 → behavex-4.2.2}/behavex/outputs/report_html.py +0 -0
  81. {behavex-4.2.0 → behavex-4.2.2}/behavex/progress_bar.py +0 -0
  82. {behavex-4.2.0 → behavex-4.2.2}/behavex/steps/__init__.py +0 -0
  83. {behavex-4.2.0 → behavex-4.2.2}/behavex.egg-info/dependency_links.txt +0 -0
  84. {behavex-4.2.0 → behavex-4.2.2}/behavex.egg-info/entry_points.txt +0 -0
  85. {behavex-4.2.0 → behavex-4.2.2}/behavex.egg-info/requires.txt +0 -0
  86. {behavex-4.2.0 → behavex-4.2.2}/behavex.egg-info/top_level.txt +0 -0
  87. {behavex-4.2.0 → behavex-4.2.2}/docs/conf.py +0 -0
  88. {behavex-4.2.0 → behavex-4.2.2}/setup.cfg +0 -0
  89. {behavex-4.2.0 → behavex-4.2.2}/tests/features/crashing_features/environment.py +0 -0
  90. {behavex-4.2.0 → behavex-4.2.2}/tests/features/crashing_features/steps/__init__.py +0 -0
  91. {behavex-4.2.0 → behavex-4.2.2}/tests/features/crashing_features/steps/crashing_steps.py +0 -0
  92. {behavex-4.2.0 → behavex-4.2.2}/tests/features/environment.py +0 -0
  93. {behavex-4.2.0 → behavex-4.2.2}/tests/features/secondary_features/__init__.py +0 -0
  94. {behavex-4.2.0 → behavex-4.2.2}/tests/features/secondary_features/environment.py +0 -0
  95. {behavex-4.2.0 → behavex-4.2.2}/tests/features/secondary_features/steps/__init__.py +0 -0
  96. {behavex-4.2.0 → behavex-4.2.2}/tests/features/secondary_features/steps/generic_secondary_steps.py +0 -0
  97. {behavex-4.2.0 → behavex-4.2.2}/tests/features/steps/__init__.py +0 -0
  98. {behavex-4.2.0 → behavex-4.2.2}/tests/features/steps/allure_formatter_unit_steps.py +0 -0
  99. {behavex-4.2.0 → behavex-4.2.2}/tests/features/steps/execution_steps.py +0 -0
  100. {behavex-4.2.0 → behavex-4.2.2}/tests/features/steps/install_steps.py +0 -0
  101. {behavex-4.2.0 → behavex-4.2.2}/venv/bin/activate_this.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: behavex
3
- Version: 4.2.0
3
+ Version: 4.2.2
4
4
  Summary: Agile testing framework on top of Behave (BDD).
5
5
  Home-page: https://github.com/hrcorval/behavex
6
6
  Author: Hernan Rey
@@ -56,6 +56,14 @@ Dynamic: requires-python
56
56
 
57
57
  # BehaveX Documentation
58
58
 
59
+ ## 📢 Important News
60
+
61
+ **🎉 NEW: Allure Reports Integration!** *(Available since BehaveX 4.2.1)*
62
+
63
+ BehaveX now provides seamless integration with Allure, the popular test reporting framework! Generate beautiful, comprehensive test reports with detailed evidence.
64
+
65
+ [Learn more about Allure integration](#allure-reports-integration) 👈
66
+
59
67
  ## Table of Contents
60
68
  - [Introduction](#introduction)
61
69
  - [Features](#features)
@@ -66,8 +74,11 @@ Dynamic: requires-python
66
74
  - [Specific Arguments from BehaveX](#specific-arguments-from-behavex)
67
75
  - [Parallel Test Executions](#parallel-test-executions)
68
76
  - [Test Execution Reports](#test-execution-reports)
69
- - [Attaching Images and Evidence](#attaching-images-and-evidence)
70
- - [Test Logs and Metrics](#test-logs-and-metrics)
77
+ - [Attaching Images to the HTML Report](#attaching-images-to-the-html-report)
78
+ - [Attaching Additional Execution Evidence to the HTML Report](#attaching-additional-execution-evidence-to-the-html-report)
79
+ - [Test Logs per Scenario](#test-logs-per-scenario)
80
+ - [Metrics and Insights](#metrics-and-insights)
81
+ - [Dry Runs](#dry-runs)
71
82
  - [Muting Test Scenarios](#muting-test-scenarios)
72
83
  - [Handling Failing Scenarios](#handling-failing-scenarios)
73
84
  - [Displaying Progress Bar in Console](#displaying-progress-bar-in-console)
@@ -76,20 +87,20 @@ Dynamic: requires-python
76
87
 
77
88
  ## Introduction
78
89
 
79
- **BehaveX** is a BDD testing solution designed to enhance your Behave-based testing workflow by providing additional features and performance improvements. It's particularly beneficial in the following scenarios:
90
+ **BehaveX** is a BDD testing solution built on top of the Python Behave library, orchestrating parallel test sessions to enhance your testing workflow with additional features and performance improvements. It's particularly beneficial in the following scenarios:
80
91
 
81
92
  - **Accelerating test execution**: Significantly reduce test run times through parallel execution by feature or scenario.
82
93
  - **Enhancing test reporting**: Generate comprehensive and visually appealing HTML and JSON reports for in-depth analysis and integration with other tools.
83
94
  - **Improving test visibility**: Provide detailed evidence, such as screenshots and logs, essential for understanding test failures and successes.
84
95
  - **Optimizing test automation**: Utilize features like test retries, test muting, and performance metrics for efficient test maintenance and analysis.
85
- - **Managing complex test suites**: Handle large test suites with advanced features for organization, execution, and reporting.
96
+ - **Managing complex test suites**: Handle large test suites with advanced features for organization, execution, and comprehensive reporting through multiple formats and custom formatters.
86
97
 
87
98
  ## Features
88
99
 
89
100
  BehaveX provides the following features:
90
101
 
91
102
  - **Parallel Test Executions**: Execute tests using multiple processes, either by feature or by scenario.
92
- - **Enhanced Reporting**: Generate friendly HTML and JSON reports that can be exported and integrated with third-party tools.
103
+ - **Enhanced Reporting**: Generate comprehensive reports in multiple formats (HTML, JSON, JUnit) and utilize custom formatters like Allure for advanced reporting capabilities that can be exported and integrated with third-party tools.
93
104
  - **Evidence Collection**: Include images/screenshots and additional evidence in the HTML report.
94
105
  - **Test Logs**: Automatically compile logs generated during test execution into individual log reports for each scenario.
95
106
  - **Test Muting**: Add the `@MUTE` tag to test scenarios to execute them without including them in JUnit reports.
@@ -199,6 +210,9 @@ Execute BehaveX in the same way as Behave from the command line, using the `beha
199
210
  - **parallel-processes** (--parallel-processes): Specifies the number of parallel Behave processes.
200
211
  - **parallel-scheme** (--parallel-scheme): Performs parallel test execution by [scenario|feature].
201
212
  - **show-progress-bar** (--show-progress-bar): Displays a progress bar in the console during parallel test execution.
213
+ - **formatter** (--formatter): Specifies a custom formatter for test reports (e.g., Allure formatter).
214
+ - **formatter-outdir** (--formatter-outdir): Specifies the output directory for formatter results (default: output/allure-results for Allure).
215
+ - **no-formatter-attach-logs** (--no-formatter-attach-logs): Disables automatic attachment of scenario log files to formatter reports.
202
216
 
203
217
  ## Parallel Test Executions
204
218
 
@@ -354,6 +368,8 @@ If you are printing logs in the console, you can configure the progress bar to d
354
368
 
355
369
  BehaveX provides integration with Allure, a flexible, lightweight multi-language test reporting tool. The Allure formatter creates detailed and visually appealing reports that include comprehensive test information, evidence, and categorization of test results.
356
370
 
371
+ **Note**: Since BehaveX is designed to run tests in parallel, the Allure formatter processes the consolidated `report.json` file after all parallel test executions are completed. This ensures that all test results from different parallel processes are properly aggregated before generating the final Allure report.
372
+
357
373
  ### Prerequisites
358
374
 
359
375
  1. Install Allure on your system. Please refer to the [official Allure installation documentation](https://docs.qameta.io/allure/#_installing_a_commandline) for detailed instructions for your operating system.
@@ -372,6 +388,16 @@ By default, the Allure results will be generated in the `output/allure-results`
372
388
  behavex -t=@TAG --formatter=behavex.outputs.formatters.allure_behavex_formatter:AllureBehaveXFormatter --formatter-outdir=my-allure-results
373
389
  ```
374
390
 
391
+ ### Attaching Screenshots and Evidence to Allure Reports
392
+
393
+ When using Allure reports, you should continue to use the same methods for attaching screenshots and evidence as described in the sections above:
394
+
395
+ - **For screenshots**: Use the methods described in [Attaching Images to the HTML Report](#attaching-images-to-the-html-report) section. The `attach_image_file()` and `attach_image_binary()` methods from the `behavex_images` library will automatically work with Allure reports.
396
+
397
+ - **For additional evidence**: Use the approach described in [Attaching Additional Execution Evidence to the HTML Report](#attaching-additional-execution-evidence-to-the-html-report) section. Files stored in the `context.evidence_path` will be automatically included in the Allure reports.
398
+
399
+ The evidence and screenshots attached using these methods will be seamlessly integrated into your Allure reports, providing comprehensive test execution documentation.
400
+
375
401
  ### Viewing Allure Reports
376
402
 
377
403
  After running the tests, you can generate and view the Allure report using the following commands:
@@ -387,7 +413,11 @@ allure generate output/allure-results --output output/allure-report --clean --si
387
413
  allure generate output/allure-results --output output/allure-report --clean
388
414
  ```
389
415
 
390
- For more information about Allure reports, including features, customization options, and special tags you can use in your scenarios, please refer to the [official Allure documentation](https://docs.qameta.io/allure/).
416
+ ### Disabling Log Attachments
417
+ By default, `scenario.log` files are attached to each scenario in the Allure report. You can disable this by passing the `--no-formatter-attach-logs` argument:
418
+ ```bash
419
+ behavex --formatter behavex.outputs.formatters.allure_behavex_formatter:AllureBehaveXFormatter --no-formatter-attach-logs
420
+ ```
391
421
 
392
422
  ## Show Your Support
393
423
 
@@ -8,6 +8,14 @@
8
8
 
9
9
  # BehaveX Documentation
10
10
 
11
+ ## 📢 Important News
12
+
13
+ **🎉 NEW: Allure Reports Integration!** *(Available since BehaveX 4.2.1)*
14
+
15
+ BehaveX now provides seamless integration with Allure, the popular test reporting framework! Generate beautiful, comprehensive test reports with detailed evidence.
16
+
17
+ [Learn more about Allure integration](#allure-reports-integration) 👈
18
+
11
19
  ## Table of Contents
12
20
  - [Introduction](#introduction)
13
21
  - [Features](#features)
@@ -18,8 +26,11 @@
18
26
  - [Specific Arguments from BehaveX](#specific-arguments-from-behavex)
19
27
  - [Parallel Test Executions](#parallel-test-executions)
20
28
  - [Test Execution Reports](#test-execution-reports)
21
- - [Attaching Images and Evidence](#attaching-images-and-evidence)
22
- - [Test Logs and Metrics](#test-logs-and-metrics)
29
+ - [Attaching Images to the HTML Report](#attaching-images-to-the-html-report)
30
+ - [Attaching Additional Execution Evidence to the HTML Report](#attaching-additional-execution-evidence-to-the-html-report)
31
+ - [Test Logs per Scenario](#test-logs-per-scenario)
32
+ - [Metrics and Insights](#metrics-and-insights)
33
+ - [Dry Runs](#dry-runs)
23
34
  - [Muting Test Scenarios](#muting-test-scenarios)
24
35
  - [Handling Failing Scenarios](#handling-failing-scenarios)
25
36
  - [Displaying Progress Bar in Console](#displaying-progress-bar-in-console)
@@ -28,20 +39,20 @@
28
39
 
29
40
  ## Introduction
30
41
 
31
- **BehaveX** is a BDD testing solution designed to enhance your Behave-based testing workflow by providing additional features and performance improvements. It's particularly beneficial in the following scenarios:
42
+ **BehaveX** is a BDD testing solution built on top of the Python Behave library, orchestrating parallel test sessions to enhance your testing workflow with additional features and performance improvements. It's particularly beneficial in the following scenarios:
32
43
 
33
44
  - **Accelerating test execution**: Significantly reduce test run times through parallel execution by feature or scenario.
34
45
  - **Enhancing test reporting**: Generate comprehensive and visually appealing HTML and JSON reports for in-depth analysis and integration with other tools.
35
46
  - **Improving test visibility**: Provide detailed evidence, such as screenshots and logs, essential for understanding test failures and successes.
36
47
  - **Optimizing test automation**: Utilize features like test retries, test muting, and performance metrics for efficient test maintenance and analysis.
37
- - **Managing complex test suites**: Handle large test suites with advanced features for organization, execution, and reporting.
48
+ - **Managing complex test suites**: Handle large test suites with advanced features for organization, execution, and comprehensive reporting through multiple formats and custom formatters.
38
49
 
39
50
  ## Features
40
51
 
41
52
  BehaveX provides the following features:
42
53
 
43
54
  - **Parallel Test Executions**: Execute tests using multiple processes, either by feature or by scenario.
44
- - **Enhanced Reporting**: Generate friendly HTML and JSON reports that can be exported and integrated with third-party tools.
55
+ - **Enhanced Reporting**: Generate comprehensive reports in multiple formats (HTML, JSON, JUnit) and utilize custom formatters like Allure for advanced reporting capabilities that can be exported and integrated with third-party tools.
45
56
  - **Evidence Collection**: Include images/screenshots and additional evidence in the HTML report.
46
57
  - **Test Logs**: Automatically compile logs generated during test execution into individual log reports for each scenario.
47
58
  - **Test Muting**: Add the `@MUTE` tag to test scenarios to execute them without including them in JUnit reports.
@@ -151,6 +162,9 @@ Execute BehaveX in the same way as Behave from the command line, using the `beha
151
162
  - **parallel-processes** (--parallel-processes): Specifies the number of parallel Behave processes.
152
163
  - **parallel-scheme** (--parallel-scheme): Performs parallel test execution by [scenario|feature].
153
164
  - **show-progress-bar** (--show-progress-bar): Displays a progress bar in the console during parallel test execution.
165
+ - **formatter** (--formatter): Specifies a custom formatter for test reports (e.g., Allure formatter).
166
+ - **formatter-outdir** (--formatter-outdir): Specifies the output directory for formatter results (default: output/allure-results for Allure).
167
+ - **no-formatter-attach-logs** (--no-formatter-attach-logs): Disables automatic attachment of scenario log files to formatter reports.
154
168
 
155
169
  ## Parallel Test Executions
156
170
 
@@ -306,6 +320,8 @@ If you are printing logs in the console, you can configure the progress bar to d
306
320
 
307
321
  BehaveX provides integration with Allure, a flexible, lightweight multi-language test reporting tool. The Allure formatter creates detailed and visually appealing reports that include comprehensive test information, evidence, and categorization of test results.
308
322
 
323
+ **Note**: Since BehaveX is designed to run tests in parallel, the Allure formatter processes the consolidated `report.json` file after all parallel test executions are completed. This ensures that all test results from different parallel processes are properly aggregated before generating the final Allure report.
324
+
309
325
  ### Prerequisites
310
326
 
311
327
  1. Install Allure on your system. Please refer to the [official Allure installation documentation](https://docs.qameta.io/allure/#_installing_a_commandline) for detailed instructions for your operating system.
@@ -324,6 +340,16 @@ By default, the Allure results will be generated in the `output/allure-results`
324
340
  behavex -t=@TAG --formatter=behavex.outputs.formatters.allure_behavex_formatter:AllureBehaveXFormatter --formatter-outdir=my-allure-results
325
341
  ```
326
342
 
343
+ ### Attaching Screenshots and Evidence to Allure Reports
344
+
345
+ When using Allure reports, you should continue to use the same methods for attaching screenshots and evidence as described in the sections above:
346
+
347
+ - **For screenshots**: Use the methods described in [Attaching Images to the HTML Report](#attaching-images-to-the-html-report) section. The `attach_image_file()` and `attach_image_binary()` methods from the `behavex_images` library will automatically work with Allure reports.
348
+
349
+ - **For additional evidence**: Use the approach described in [Attaching Additional Execution Evidence to the HTML Report](#attaching-additional-execution-evidence-to-the-html-report) section. Files stored in the `context.evidence_path` will be automatically included in the Allure reports.
350
+
351
+ The evidence and screenshots attached using these methods will be seamlessly integrated into your Allure reports, providing comprehensive test execution documentation.
352
+
327
353
  ### Viewing Allure Reports
328
354
 
329
355
  After running the tests, you can generate and view the Allure report using the following commands:
@@ -339,7 +365,11 @@ allure generate output/allure-results --output output/allure-report --clean --si
339
365
  allure generate output/allure-results --output output/allure-report --clean
340
366
  ```
341
367
 
342
- For more information about Allure reports, including features, customization options, and special tags you can use in your scenarios, please refer to the [official Allure documentation](https://docs.qameta.io/allure/).
368
+ ### Disabling Log Attachments
369
+ By default, `scenario.log` files are attached to each scenario in the Allure report. You can disable this by passing the `--no-formatter-attach-logs` argument:
370
+ ```bash
371
+ behavex --formatter behavex.outputs.formatters.allure_behavex_formatter:AllureBehaveXFormatter --no-formatter-attach-logs
372
+ ```
343
373
 
344
374
  ## Show Your Support
345
375
 
@@ -71,6 +71,7 @@ BEHAVEX_ARGS = [
71
71
  'show_progress_bar',
72
72
  'formatter',
73
73
  'formatter_outdir',
74
+ 'formatter_attach_logs',
74
75
  ]
75
76
 
76
77
 
@@ -336,6 +337,15 @@ def parse_arguments(args):
336
337
  required=False,
337
338
  )
338
339
 
340
+ parser.add_argument(
341
+ '--no-formatter-attach-logs',
342
+ dest='formatter_attach_logs',
343
+ help="Do not attach scenario logs to the report formatter.",
344
+ action='store_false',
345
+ default=True,
346
+ required=False,
347
+ )
348
+
339
349
  # parser.add_argument('--logging-format',
340
350
  # help="Specify custom format to print statements. Uses "
341
351
  # " the same format as used by standard logging "
@@ -28,10 +28,12 @@ Context.__getattribute__ = create_custom_log_when_called
28
28
 
29
29
  hooks_already_set = False
30
30
 
31
+
31
32
  def _get_current_timestamp_ms():
32
33
  """Get current time as Unix epoch milliseconds."""
33
34
  return int(datetime.now().timestamp() * 1000)
34
35
 
36
+
35
37
  def extend_behave_hooks():
36
38
  """
37
39
  Extend Behave hooks with BehaveX hooks code.
@@ -106,7 +108,7 @@ def extend_behave_hooks():
106
108
  def before_all(context):
107
109
  """Setup up initial tests configuration."""
108
110
  try:
109
- # Initialyzing log handler
111
+ # Initialize critical state variables to ensure consistent state
110
112
  context.bhx_log_handler = None
111
113
  context.bhx_inside_scenario = False
112
114
  # Store framework settings to make them accessible from steps
@@ -120,40 +122,68 @@ def before_feature(context, feature):
120
122
  try:
121
123
  context.bhx_execution_attempts = {}
122
124
  scenarios_instances = get_scenarios_instances(feature.scenarios)
125
+
123
126
  for scenario in scenarios_instances:
124
127
  scenario_tags = get_scenario_tags(scenario)
125
- if get_param('dry_run'):
126
- if 'MANUAL' not in scenario_tags:
127
- scenario.tags.append(u'BHX_MANUAL_DRY_RUN')
128
- scenario.tags.append(u'MANUAL')
128
+
129
+ # Handle dry run scenario tagging
130
+ if get_param('dry_run') and 'MANUAL' not in scenario_tags:
131
+ scenario.tags.extend(['BHX_MANUAL_DRY_RUN', 'MANUAL'])
132
+
133
+ # Configure scenario auto-retry
129
134
  configured_attempts = get_autoretry_attempts(scenario_tags)
130
135
  if configured_attempts > 0:
131
136
  patch_scenario_with_autoretry(scenario, configured_attempts)
132
137
  except Exception as exception:
138
+ # Keep generic exception as fallback for truly unexpected errors
133
139
  _log_exception_and_continue('before_feature (behavex)', exception)
134
140
 
135
141
 
136
142
  def before_scenario(context, scenario):
137
143
  """Initialize logs for current scenario."""
144
+ # Initialize critical variables first to ensure they're always set
145
+ context.bhx_inside_scenario = True
146
+ # Initialize log handler to None to ensure it's always defined
147
+ context.bhx_log_handler = None
148
+
138
149
  try:
139
- context.bhx_inside_scenario = True
150
+ # Handle execution attempts tracking
140
151
  if scenario.name not in context.bhx_execution_attempts:
141
152
  context.bhx_execution_attempts[scenario.name] = 0
142
153
  execution_attempt = context.bhx_execution_attempts[scenario.name]
143
154
  retrying_execution = True if execution_attempt > 0 else False
144
-
145
155
  # Calculate and store the scenario identifier hash
146
156
  scenario_identifier = f"{str(context.feature.filename)}-{str(scenario.line)}"
157
+ # Calculate the scenario identifier hash
147
158
  scenario.identifier_hash = get_string_hash(scenario_identifier)
148
-
149
- context.log_path = create_log_path(scenario_identifier, retrying_execution)
159
+ # Create log path
160
+ context.log_path = create_log_path(scenario.identifier_hash, retrying_execution)
161
+ # Add log handler
150
162
  context.bhx_log_handler = _add_log_handler(context.log_path)
163
+ # Handle retry scenario cleanup
151
164
  if retrying_execution:
152
- logging.info('Retrying scenario execution...\n'.format())
153
- shutil.rmtree(context.evidence_path)
165
+ try:
166
+ logging.info('Retrying scenario execution...')
167
+ if hasattr(context, 'evidence_path') and context.evidence_path:
168
+ try:
169
+ shutil.rmtree(context.evidence_path)
170
+ except FileNotFoundError:
171
+ # Directory already removed by another process - this is fine
172
+ pass
173
+ except Exception as cleanup_ex:
174
+ logging.warning(f"Failed to remove evidence directory {context.evidence_path}: {cleanup_ex}")
175
+ except Exception as retry_ex:
176
+ logging.warning(f"Failed to clean up evidence path during retry: {retry_ex}")
177
+ scenario.process_id = os.getpid()
178
+ if "config" in context and "worker_id" in context.config.userdata:
179
+ scenario.worker_id = context.config.userdata['worker_id']
180
+ else:
181
+ scenario.worker_id = str(os.getpid())
154
182
  except Exception as exception:
183
+ # Log the exception but ensure execution continues
155
184
  _log_exception_and_continue('before_scenario (behavex)', exception)
156
- scenario.start = _get_current_timestamp_ms()
185
+ finally:
186
+ scenario.start = _get_current_timestamp_ms()
157
187
 
158
188
 
159
189
  def before_step(context, step):
@@ -191,9 +221,12 @@ def after_scenario(context, scenario):
191
221
  else:
192
222
  global_vars.retried_scenarios[feature_name].append(scenario.name)
193
223
  context.bhx_execution_attempts[scenario.name] += 1
194
- _close_log_handler(context.bhx_log_handler)
195
224
  except Exception as exception:
196
225
  _log_exception_and_continue('after_scenario (behavex)', exception)
226
+ finally:
227
+ # Always reset the scenario state flag, regardless of success or failure
228
+ _close_log_handler(context.bhx_log_handler)
229
+ context.bhx_inside_scenario = False
197
230
 
198
231
 
199
232
  # noinspection PyUnusedLocal
@@ -217,24 +250,31 @@ def after_all(context):
217
250
 
218
251
  def _add_log_handler(log_path):
219
252
  """Adding a new log handler to logger"""
220
- log_filename = os.path.join(log_path, 'scenario.log')
221
- file_handler = logging.FileHandler(
222
- log_filename, mode='+a', encoding=LOGGING_CFG['file_handler']['encoding']
223
- )
224
- log_level = get_logging_level()
225
- logging.getLogger().setLevel(log_level)
226
- file_handler.addFilter(lambda record: setattr(record, 'msg', strip_ansi_codes(str(record.msg))) or True)
227
- file_handler.setFormatter(_get_log_formatter())
228
- logging.getLogger().addHandler(file_handler)
253
+ file_handler = None
254
+ try:
255
+ log_filename = os.path.join(log_path, 'scenario.log')
256
+ file_handler = logging.FileHandler(
257
+ log_filename, mode='+a', encoding=LOGGING_CFG['file_handler']['encoding']
258
+ )
259
+ log_level = get_logging_level()
260
+ logging.getLogger().setLevel(log_level)
261
+ file_handler.addFilter(lambda record: setattr(record, 'msg', strip_ansi_codes(str(record.msg))) or True)
262
+ file_handler.setFormatter(_get_log_formatter())
263
+ logging.getLogger().addHandler(file_handler)
264
+ except Exception as exception:
265
+ _log_exception_and_continue('_add_log_handler', exception)
229
266
  return file_handler
230
267
 
231
268
 
232
269
  def _close_log_handler(handler):
233
- """Closing current log handlers and removing them from logger"""
270
+ """Closing current log handlers and removing them from logger."""
234
271
  if handler:
235
- if hasattr(handler, 'stream') and hasattr(handler.stream, 'close'):
236
- handler.stream.close()
237
- logging.getLogger().removeHandler(handler)
272
+ try:
273
+ if hasattr(handler, 'stream') and hasattr(handler.stream, 'close'):
274
+ handler.stream.close()
275
+ logging.getLogger().removeHandler(handler)
276
+ except Exception as exception:
277
+ _log_exception_and_continue('_close_log_handler', exception)
238
278
 
239
279
 
240
280
  def _get_log_formatter():
@@ -69,10 +69,16 @@ class FormatterManager:
69
69
  Get the output directory for the custom formatter.
70
70
 
71
71
  Returns:
72
- Absolute path to the formatter output directory
72
+ Absolute path to the formatter output directory, or None if no custom directory is specified
73
73
  """
74
74
  base_output = get_env('OUTPUT', 'output')
75
75
  formatter_outdir = get_param('formatter_outdir', '')
76
+
77
+ # Return None if no custom formatter output directory is specified
78
+ # This allows formatters to use their own default directories
79
+ if not formatter_outdir or formatter_outdir == 'report_artifacts':
80
+ return None
81
+
76
82
  return os.path.join(base_output, formatter_outdir)
77
83
 
78
84
  @staticmethod
@@ -92,7 +98,10 @@ class FormatterManager:
92
98
  return
93
99
 
94
100
  output_dir = FormatterManager.get_formatter_output_dir()
95
- os.makedirs(output_dir, exist_ok=True)
101
+
102
+ # Create the output directory only if a custom one is specified
103
+ if output_dir:
104
+ os.makedirs(output_dir, exist_ok=True)
96
105
 
97
106
  try:
98
107
  formatter = formatter_class()
@@ -104,3 +113,5 @@ class FormatterManager:
104
113
  formatter.parse_json_to_allure(json_output)
105
114
  except Exception as e:
106
115
  print(f"Error running formatter {formatter_spec}: {str(e)}")
116
+ import traceback
117
+ traceback.print_exc()
@@ -306,18 +306,10 @@ class AllureBehaveXFormatter:
306
306
  """
307
307
  # Get the formatter output directory from FormatterManager
308
308
  formatter_output_dir = FormatterManager.get_formatter_output_dir()
309
- base_output = get_env('OUTPUT', 'output')
310
309
 
311
- # Check if a custom formatter output directory was explicitly set via --formatter-outdir
312
- custom_outdir = get_param('formatter_outdir', '')
313
-
314
- # Check if the user explicitly set a non-default formatter output directory
315
- if custom_outdir and custom_outdir != 'report_artifacts':
316
- # Use the custom directory if explicitly specified (not the default)
317
- output_dir = formatter_output_dir
318
- else:
319
- # Default to allure-results subdirectory in the main output folder
320
- output_dir = os.path.join(base_output, 'allure-results')
310
+ # If a custom output directory is specified, use it.
311
+ # Otherwise, default to 'output/allure-results'.
312
+ output_dir = formatter_output_dir or os.path.join(get_env('OUTPUT', 'output'), 'allure-results')
321
313
 
322
314
  os.makedirs(output_dir, exist_ok=True)
323
315
 
@@ -365,6 +357,11 @@ class AllureBehaveXFormatter:
365
357
  if package_name:
366
358
  test_case.labels.append({"name": "package", "value": package_name})
367
359
 
360
+ # Process scenario outline parameters
361
+ if 'parameters' in scenario and scenario['parameters']:
362
+ for name, value in scenario['parameters'].items():
363
+ test_case.parameters.append(Parameter(name=name, value=value))
364
+
368
365
  # Process steps and look for failures
369
366
  test_error_msg = None # Store test's error message
370
367
 
@@ -425,8 +422,6 @@ class AllureBehaveXFormatter:
425
422
 
426
423
  # If test failed, add its error as a direct category
427
424
  if test_error_msg:
428
- category_name = self._sanitize_error_message(test_error_msg)
429
-
430
425
  # Explicitly mark this test case as a Product Defect
431
426
  test_case.labels.append({"name": "category", "value": "Product Defects"})
432
427
 
@@ -436,14 +431,9 @@ class AllureBehaveXFormatter:
436
431
  "flaky": False
437
432
  }
438
433
 
439
- # Add a parameter to help with subcategorization
440
- test_case.parameters.append(Parameter(
441
- name="Error Category",
442
- value=category_name
443
- ))
444
-
445
434
  # Process tags
446
435
  package_from_tag = None
436
+ test_thread_already_set = False
447
437
  for tag in scenario['tags']:
448
438
  if tag.startswith("epic="):
449
439
  test_case.labels.append({"name": "epic", "value": tag.split("=")[-1]})
@@ -476,25 +466,36 @@ class AllureBehaveXFormatter:
476
466
  test_case.labels = [label for label in test_case.labels if label.get("name") != "package"]
477
467
  # Add the package from tag
478
468
  test_case.labels.append({"name": "package", "value": package_from_tag})
469
+ elif tag.startswith("allure.label."):
470
+ label_key, label_value = tag.removeprefix("allure.label.").split(":")
471
+ if label_key == "thread":
472
+ test_thread_already_set = True
473
+ test_case.labels.append({
474
+ "name": label_key,
475
+ "value": label_value
476
+ })
479
477
  else:
480
478
  test_case.labels.append({"name": "tag", "value": tag})
481
-
479
+ # Add thread label if not already set and process_id is present
480
+ if "process_id" in scenario.keys() and not test_thread_already_set:
481
+ test_case.labels.append({"name": "thread", "value": scenario["process_id"]})
482
482
  # Add scenario timing
483
483
  test_case.start = scenario.get("start", now())
484
484
  test_case.stop = scenario.get("stop", now())
485
485
  test_case.status = scenario['status']
486
486
 
487
487
  # Process scenario logs as attachment
488
- scenario_hash = scenario.get('identifier_hash', get_string_hash(f"{str(feature['filename'])}-{str(scenario['line'])}"))
489
- log_path = os.path.join(get_env('logs', 'output/allure-results'), str(scenario_hash), 'scenario.log')
490
- if os.path.exists(log_path):
491
- # Use relative path for the source if possible to avoid duplication
492
- rel_path = os.path.relpath(log_path, output_dir) if output_dir in log_path else log_path
493
- test_case.attachments.append(
494
- Attachment(source=rel_path,
495
- name="scenario.log",
496
- type="text/plain")
497
- )
488
+ if get_param('formatter_attach_logs'):
489
+ scenario_hash = scenario.get('identifier_hash', get_string_hash(f"{str(feature['filename'])}-{str(scenario['line'])}"))
490
+ log_path = os.path.join(get_env('logs', 'output/allure-results'), str(scenario_hash), 'scenario.log')
491
+ if os.path.exists(log_path):
492
+ # Use relative path for the source if possible to avoid duplication
493
+ rel_path = os.path.relpath(log_path, output_dir) if output_dir in log_path else log_path
494
+ test_case.attachments.append(
495
+ Attachment(source=rel_path,
496
+ name="scenario.log",
497
+ type="text/plain")
498
+ )
498
499
 
499
500
  # Process evidence files
500
501
  test_case = self._attach_evidence_files(test_case, scenario_hash, output_dir)
@@ -569,7 +570,11 @@ class AllureBehaveXFormatter:
569
570
  # Add hierarchical labels for better organization in Allure report
570
571
  if feature_file_path:
571
572
  # Create hierarchical representation from feature file path
572
- hierarchical_package = feature_file_path.replace("/", ".").rstrip(".feature")
573
+ hierarchical_package = feature_file_path.replace("/", ".")
574
+ if hasattr(str, 'removesuffix'): # Python 3.9+
575
+ hierarchical_package = hierarchical_package.removesuffix('.feature')
576
+ elif hierarchical_package.endswith('.feature'):
577
+ hierarchical_package = hierarchical_package[:-len('.feature')]
573
578
 
574
579
  # Remove existing package and suite labels to avoid duplication
575
580
  test_case_dict["labels"] = [label for label in test_case_dict["labels"]
@@ -68,4 +68,20 @@ TEXTS = {
68
68
  },
69
69
  'folder': {'run_behave': u"Running folder: '{}' and feature '{}'."},
70
70
  'path': {'not_found': u'\nThe path "{}" was not found.\n'},
71
+ 'process': {
72
+ 'pool': {
73
+ 'broken': u'\nWarning: Process pool was terminated abruptly: {}',
74
+ 'broken.main': u'\nWarning: A process in the pool was terminated abruptly during main execution: {}'
75
+ }
76
+ },
77
+ 'execution': {
78
+ 'callback': {
79
+ 'error': u'\nError occurred during execution callback: {}'
80
+ }
81
+ },
82
+ 'parallel': {
83
+ 'process': {
84
+ 'error': u'\nError occurred during parallel process execution: {}'
85
+ }
86
+ }
71
87
  }
@@ -182,6 +182,11 @@ def _processing_scenarios(scenarios, scenario_list, id_feature):
182
182
  scenario_info['filename'] = text(scenario.filename)
183
183
  scenario_info['feature'] = scenario.feature.name
184
184
  scenario_info['id_feature'] = id_feature
185
+
186
+ # Extract parameters for scenario outlines
187
+ if hasattr(scenario, '_row') and scenario._row:
188
+ scenario_info['parameters'] = dict(zip(scenario._row.headings, scenario._row.cells))
189
+
185
190
  steps = []
186
191
  for step in scenario.steps:
187
192
  add_step_info(step, steps)
@@ -197,6 +202,8 @@ def _processing_scenarios(scenarios, scenario_list, id_feature):
197
202
  scenario_info['error_background'] = error_background
198
203
  scenario_info['id_hash'] = generate_uuid()
199
204
  scenario_info['identifier_hash'] = scenario.identifier_hash if hasattr(scenario, 'identifier_hash') else get_string_hash(f"{str(scenario.feature.filename)}-{str(scenario.line)}")
205
+ scenario_info['process_id'] = str(scenario.process_id) if hasattr(scenario, 'process_id') else str(os.getpid())
206
+ scenario_info['worker_id'] = str(scenario.worker_id) if hasattr(scenario, 'worker_id') else str(os.getpid())
200
207
  if scenario.feature.name in global_vars.retried_scenarios:
201
208
  if (
202
209
  scenario.name