orionis 0.404.0__py3-none-any.whl → 0.406.0__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.
Files changed (165) hide show
  1. orionis/console/base/command.py +57 -50
  2. orionis/console/base/contracts/command.py +68 -0
  3. orionis/console/dynamic/contracts/progress_bar.py +3 -3
  4. orionis/console/dynamic/progress_bar.py +8 -8
  5. orionis/console/output/console.py +8 -2
  6. orionis/console/output/contracts/console.py +1 -1
  7. orionis/container/container.py +2 -2
  8. orionis/container/context/scope.py +4 -1
  9. orionis/container/contracts/service_provider.py +2 -2
  10. orionis/container/entities/binding.py +31 -44
  11. orionis/container/enums/lifetimes.py +22 -1
  12. orionis/container/facades/facade.py +1 -2
  13. orionis/container/providers/service_provider.py +2 -2
  14. orionis/foundation/application.py +542 -248
  15. orionis/foundation/config/app/entities/app.py +107 -90
  16. orionis/foundation/config/auth/entities/auth.py +4 -33
  17. orionis/foundation/config/cache/entities/cache.py +18 -41
  18. orionis/foundation/config/cache/entities/file.py +8 -35
  19. orionis/foundation/config/cache/entities/stores.py +17 -38
  20. orionis/foundation/config/cors/entities/cors.py +41 -54
  21. orionis/foundation/config/database/entities/connections.py +40 -56
  22. orionis/foundation/config/database/entities/database.py +11 -38
  23. orionis/foundation/config/database/entities/mysql.py +48 -76
  24. orionis/foundation/config/database/entities/oracle.py +30 -57
  25. orionis/foundation/config/database/entities/pgsql.py +45 -61
  26. orionis/foundation/config/database/entities/sqlite.py +26 -53
  27. orionis/foundation/config/filesystems/entitites/aws.py +28 -49
  28. orionis/foundation/config/filesystems/entitites/disks.py +27 -47
  29. orionis/foundation/config/filesystems/entitites/filesystems.py +15 -37
  30. orionis/foundation/config/filesystems/entitites/local.py +9 -35
  31. orionis/foundation/config/filesystems/entitites/public.py +14 -41
  32. orionis/foundation/config/logging/entities/channels.py +56 -86
  33. orionis/foundation/config/logging/entities/chunked.py +18 -10
  34. orionis/foundation/config/logging/entities/daily.py +17 -9
  35. orionis/foundation/config/logging/entities/hourly.py +15 -7
  36. orionis/foundation/config/logging/entities/logging.py +12 -18
  37. orionis/foundation/config/logging/entities/monthly.py +16 -8
  38. orionis/foundation/config/logging/entities/stack.py +15 -7
  39. orionis/foundation/config/logging/entities/weekly.py +15 -7
  40. orionis/foundation/config/logging/validators/path.py +6 -0
  41. orionis/foundation/config/mail/entities/file.py +9 -36
  42. orionis/foundation/config/mail/entities/mail.py +22 -40
  43. orionis/foundation/config/mail/entities/mailers.py +29 -44
  44. orionis/foundation/config/mail/entities/smtp.py +47 -48
  45. orionis/foundation/config/queue/entities/brokers.py +19 -41
  46. orionis/foundation/config/queue/entities/database.py +24 -46
  47. orionis/foundation/config/queue/entities/queue.py +28 -40
  48. orionis/foundation/config/roots/paths.py +272 -468
  49. orionis/foundation/config/session/entities/session.py +23 -53
  50. orionis/foundation/config/startup.py +165 -135
  51. orionis/foundation/config/testing/entities/testing.py +137 -122
  52. orionis/foundation/config/testing/enums/__init__.py +6 -2
  53. orionis/foundation/config/testing/enums/drivers.py +16 -0
  54. orionis/foundation/config/testing/enums/verbosity.py +18 -0
  55. orionis/foundation/contracts/application.py +152 -362
  56. orionis/foundation/providers/console_provider.py +24 -2
  57. orionis/foundation/providers/dumper_provider.py +24 -2
  58. orionis/foundation/providers/logger_provider.py +24 -2
  59. orionis/foundation/providers/path_resolver_provider.py +25 -2
  60. orionis/foundation/providers/progress_bar_provider.py +24 -2
  61. orionis/foundation/providers/testing_provider.py +39 -0
  62. orionis/foundation/providers/workers_provider.py +24 -2
  63. orionis/metadata/framework.py +1 -1
  64. orionis/services/environment/helpers/functions.py +1 -2
  65. orionis/services/environment/key/__init__.py +0 -0
  66. orionis/services/environment/key/key_generator.py +37 -0
  67. orionis/services/log/handlers/filename.py +64 -0
  68. orionis/services/log/handlers/size_rotating.py +9 -40
  69. orionis/services/log/handlers/timed_rotating.py +9 -41
  70. orionis/services/log/log_service.py +9 -52
  71. orionis/support/entities/__init__.py +0 -0
  72. orionis/support/entities/base.py +104 -0
  73. orionis/support/facades/testing.py +15 -0
  74. orionis/support/facades/workers.py +1 -1
  75. orionis/test/cases/asynchronous.py +0 -11
  76. orionis/test/cases/synchronous.py +0 -9
  77. orionis/test/contracts/dumper.py +11 -4
  78. orionis/test/contracts/kernel.py +5 -110
  79. orionis/test/contracts/logs.py +27 -65
  80. orionis/test/contracts/printer.py +16 -128
  81. orionis/test/contracts/test_result.py +100 -0
  82. orionis/test/contracts/unit_test.py +87 -150
  83. orionis/test/core/unit_test.py +608 -554
  84. orionis/test/entities/result.py +22 -2
  85. orionis/test/enums/__init__.py +0 -2
  86. orionis/test/enums/status.py +14 -9
  87. orionis/test/exceptions/config.py +9 -1
  88. orionis/test/exceptions/failure.py +34 -11
  89. orionis/test/exceptions/persistence.py +10 -2
  90. orionis/test/exceptions/runtime.py +9 -1
  91. orionis/test/exceptions/value.py +13 -1
  92. orionis/test/kernel.py +87 -289
  93. orionis/test/output/dumper.py +82 -18
  94. orionis/test/output/printer.py +399 -156
  95. orionis/test/records/logs.py +203 -82
  96. orionis/test/validators/__init__.py +33 -0
  97. orionis/test/validators/base_path.py +45 -0
  98. orionis/test/validators/execution_mode.py +45 -0
  99. orionis/test/validators/fail_fast.py +37 -0
  100. orionis/test/validators/folder_path.py +34 -0
  101. orionis/test/validators/module_name.py +31 -0
  102. orionis/test/validators/name_pattern.py +40 -0
  103. orionis/test/validators/pattern.py +36 -0
  104. orionis/test/validators/persistent.py +42 -0
  105. orionis/test/validators/persistent_driver.py +43 -0
  106. orionis/test/validators/print_result.py +37 -0
  107. orionis/test/validators/tags.py +37 -0
  108. orionis/test/validators/throw_exception.py +39 -0
  109. orionis/test/validators/verbosity.py +37 -0
  110. orionis/test/validators/web_report.py +35 -0
  111. orionis/test/validators/workers.py +31 -0
  112. orionis/test/view/render.py +48 -54
  113. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/METADATA +1 -1
  114. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/RECORD +160 -108
  115. tests/container/__init__.py +0 -0
  116. tests/container/context/__init__.py +0 -0
  117. tests/container/context/test_manager.py +27 -0
  118. tests/container/context/test_scope.py +23 -0
  119. tests/container/entities/__init__.py +0 -0
  120. tests/container/entities/test_binding.py +133 -0
  121. tests/container/enums/__init__.py +0 -0
  122. tests/container/enums/test_lifetimes.py +63 -0
  123. tests/container/facades/__init__.py +0 -0
  124. tests/container/facades/test_facade.py +61 -0
  125. tests/container/mocks/__init__.py +0 -0
  126. tests/container/mocks/mock_complex_classes.py +482 -0
  127. tests/container/mocks/mock_simple_classes.py +32 -0
  128. tests/container/providers/__init__.py +0 -0
  129. tests/container/providers/test_providers.py +48 -0
  130. tests/container/resolver/__init__.py +0 -0
  131. tests/container/resolver/test_resolver.py +55 -0
  132. tests/container/test_container.py +254 -0
  133. tests/container/test_singleton.py +98 -0
  134. tests/container/test_thread_safety.py +217 -0
  135. tests/container/validators/__init__.py +0 -0
  136. tests/container/validators/test_implements.py +140 -0
  137. tests/container/validators/test_is_abstract_class.py +99 -0
  138. tests/container/validators/test_is_callable.py +73 -0
  139. tests/container/validators/test_is_concrete_class.py +97 -0
  140. tests/container/validators/test_is_instance.py +105 -0
  141. tests/container/validators/test_is_not_subclass.py +117 -0
  142. tests/container/validators/test_is_subclass.py +115 -0
  143. tests/container/validators/test_is_valid_alias.py +113 -0
  144. tests/container/validators/test_lifetime.py +75 -0
  145. tests/foundation/config/logging/test_foundation_config_logging_chunked.py +12 -34
  146. tests/foundation/config/logging/test_foundation_config_logging_daily.py +11 -11
  147. tests/foundation/config/logging/test_foundation_config_logging_hourly.py +7 -8
  148. tests/foundation/config/logging/test_foundation_config_logging_monthly.py +7 -10
  149. tests/foundation/config/logging/test_foundation_config_logging_stack.py +6 -11
  150. tests/foundation/config/logging/test_foundation_config_logging_weekly.py +6 -5
  151. tests/foundation/config/testing/test_foundation_config_testing.py +1 -1
  152. tests/metadata/test_metadata_framework.py +18 -18
  153. tests/testing/test_testing_result.py +117 -117
  154. tests/testing/test_testing_unit.py +209 -209
  155. orionis/foundation/config/base.py +0 -112
  156. orionis/test/arguments/parser.py +0 -187
  157. orionis/test/contracts/parser.py +0 -43
  158. orionis/test/entities/arguments.py +0 -38
  159. orionis/test/enums/execution_mode.py +0 -16
  160. /orionis/{test/arguments → console/base/contracts}/__init__.py +0 -0
  161. /orionis/foundation/config/testing/enums/{test_mode.py → mode.py} +0 -0
  162. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/WHEEL +0 -0
  163. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/licenses/LICENCE +0 -0
  164. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/top_level.txt +0 -0
  165. {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/zip-safe +0 -0
@@ -9,211 +9,299 @@ from rich.table import Table
9
9
  from rich.text import Text
10
10
  from orionis.services.introspection.instances.reflection import ReflectionInstance
11
11
  from orionis.test.contracts.printer import ITestPrinter
12
+ from orionis.test.entities.result import TestResult
12
13
  from orionis.test.enums import TestStatus
13
14
 
14
15
  class TestPrinter(ITestPrinter):
15
16
 
16
17
  def __init__(
17
- self
18
+ self,
19
+ print_result: bool = True,
20
+ title: str = "🧪 Orionis Framework - Component Test Suite",
21
+ width: int = 75
18
22
  ) -> None:
19
23
  """
20
- Initialize the test output printer.
24
+ Initialize a TestPrinter instance for formatted test output.
21
25
 
22
- This initializes a Rich Console for output rendering, setting up panel
23
- parameters and debug keywords for test result display.
26
+ This constructor sets up the Rich Console for rendering output, configures
27
+ the panel title and width for display, and defines keywords used to detect
28
+ debugging calls in test code.
24
29
 
25
30
  Parameters
26
31
  ----------
27
- None
32
+ print_result : bool, optional
33
+ Whether to print test results to the console (default is True).
34
+ title : str, optional
35
+ The title to display in the output panel (default is "🧪 Orionis Framework - Component Test Suite").
36
+ width : int, optional
37
+ The width of the output panel as a percentage of the console width (default is 75).
28
38
 
29
39
  Returns
30
40
  -------
31
41
  None
42
+ This method does not return a value. It initializes instance attributes for output formatting.
32
43
 
33
44
  Notes
34
45
  -----
35
- Sets up the following attributes:
36
- - __rich_console: Rich Console instance for formatted terminal output
37
- - __panel_title: Title string for the output panel
38
- - __panel_width: Width of the output panel (75% of console width)
39
- - __debbug_keywords: List of keywords for identifying debug calls
46
+ - Initializes the following attributes:
47
+ - __rich_console: Rich Console instance for formatted terminal output.
48
+ - __panel_title: Title string for the output panel.
49
+ - __panel_width: Width of the output panel, calculated as a percentage of the console width.
50
+ - __debbug_keywords: List of keywords for identifying debug calls in test code.
51
+ - __print_result: Flag indicating whether to print results.
40
52
  """
53
+ # Create a Rich Console instance for output rendering
41
54
  self.__rich_console = Console()
42
- self.__panel_title: str = "🧪 Orionis Framework - Component Test Suite"
43
- self.__panel_width: int = int(self.__rich_console.width * 0.75)
55
+
56
+ # Set the panel title for display
57
+ self.__panel_title: str = title
58
+
59
+ # Calculate the panel width as a percentage of the console width
60
+ self.__panel_width: int = int(self.__rich_console.width * (width / 100))
61
+
62
+ # Define keywords to detect debugging or dump calls in test code
44
63
  self.__debbug_keywords: list = ['self.dd', 'self.dump']
45
64
 
65
+ # Store the flag indicating whether to print results
66
+ self.__print_result: bool = print_result
67
+
46
68
  def print(
47
69
  self,
48
70
  value: Any
49
71
  ) -> None:
50
72
  """
51
- Prints a value to the console using the rich console.
73
+ Print a value to the console using the Rich library's console.
74
+
75
+ This method provides a unified way to output various types of values to the console,
76
+ leveraging Rich's formatting capabilities. It handles strings, objects, and lists,
77
+ ensuring each is displayed appropriately.
78
+
52
79
  Parameters
53
80
  ----------
54
81
  value : Any
55
- The value to be printed. It can be a string, object, or any other type.
82
+ The value to be printed. Can be a string, object, or list.
83
+
84
+ Returns
85
+ -------
86
+ None
87
+ This method does not return any value. It outputs the provided value(s) to the console.
88
+
56
89
  Notes
57
90
  -----
58
91
  - If the value is a string, it is printed directly.
59
- - If the value is an object, its string representation is printed.
60
- - If the value is a list, each item is printed on a new line.
92
+ - If the value is a list, each item in the list is printed on a new line.
93
+ - For any other object, its string representation is printed.
61
94
  """
95
+
96
+ # If not printing results, return early
97
+ if self.__print_result is False:
98
+ return
99
+
100
+ # If the value is a string, print it directly
62
101
  if isinstance(value, str):
63
102
  self.__rich_console.print(value)
64
- elif isinstance(value, object):
65
- self.__rich_console.print(str(value))
103
+
104
+ # If the value is a list, print each item on a new line
66
105
  elif isinstance(value, list):
67
106
  for item in value:
68
107
  self.__rich_console.print(item)
108
+
109
+ # For any other object, print its string representation
69
110
  else:
70
111
  self.__rich_console.print(str(value))
71
112
 
72
113
  def startMessage(
73
114
  self,
74
115
  *,
75
- print_result: bool,
76
116
  length_tests: int,
77
117
  execution_mode: str,
78
118
  max_workers: int
79
119
  ):
80
120
  """
81
- Displays a formatted start message for the test execution session.
121
+ Display a formatted start message for the test execution session.
122
+
123
+ This method prints a styled panel to the console at the beginning of a test run,
124
+ providing key information about the test session such as the total number of tests,
125
+ the execution mode (parallel or sequential), and the start time. The output is
126
+ rendered using the Rich library for enhanced readability.
82
127
 
83
128
  Parameters
84
129
  ----------
85
- print_result : bool
86
- Whether to print the start message.
87
130
  length_tests : int
88
- The total number of tests to be executed.
131
+ The total number of tests to be executed in the session.
89
132
  execution_mode : str
90
- The mode of execution, either "parallel" or "sequential".
133
+ The mode of execution for the tests. Accepts "parallel" or "sequential".
91
134
  max_workers : int
92
- The number of worker threads/processes for parallel execution.
135
+ The number of worker threads or processes to use if running in parallel mode.
93
136
 
94
- Side Effects
95
- ------------
96
- Prints a styled panel with test session information to the console if `print_result` is True.
137
+ Returns
138
+ -------
139
+ None
140
+ This method does not return any value. It only prints formatted output to the console.
141
+
142
+ Notes
143
+ -----
144
+ - The message is only printed if the `print_result` flag is set to True.
145
+ - The panel includes the total number of tests, execution mode, and the current timestamp.
97
146
  """
98
- if print_result:
99
- mode_text = f"[stat]Parallel with {max_workers} workers[/stat]" if execution_mode == "parallel" else "Sequential"
100
- textlines = [
101
- f"[bold]Total Tests:[/bold] [dim]{length_tests}[/dim]",
102
- f"[bold]Mode:[/bold] [dim]{mode_text}[/dim]",
103
- f"[bold]Started at:[/bold] [dim]{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}[/dim]"
104
- ]
105
-
106
- self.__rich_console.line(1)
107
- self.__rich_console.print(
108
- Panel(
109
- str('\n').join(textlines),
110
- border_style="blue",
111
- title=self.__panel_title,
112
- title_align="center",
113
- width=self.__panel_width,
114
- padding=(0, 1)
115
- )
147
+
148
+ # If not printing results, return early
149
+ if self.__print_result is False:
150
+ return
151
+
152
+ # Determine the execution mode text for display
153
+ mode_text = f"[stat]Parallel with {max_workers} workers[/stat]" if execution_mode == "parallel" else "Sequential"
154
+
155
+ # Prepare the lines of information to display in the panel
156
+ textlines = [
157
+ f"[bold]Total Tests:[/bold] [dim]{length_tests}[/dim]",
158
+ f"[bold]Mode:[/bold] [dim]{mode_text}[/dim]",
159
+ f"[bold]Started at:[/bold] [dim]{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}[/dim]"
160
+ ]
161
+
162
+ # Add a blank line before the panel
163
+ self.__rich_console.line(1)
164
+
165
+ # Print the panel with the formatted text lines
166
+ self.__rich_console.print(
167
+ Panel(
168
+ str('\n').join(textlines),
169
+ border_style="blue",
170
+ title=self.__panel_title,
171
+ title_align="center",
172
+ width=self.__panel_width,
173
+ padding=(0, 1)
116
174
  )
117
- self.__rich_console.line(1)
175
+ )
176
+
177
+ # Add a blank line after the panel
178
+ self.__rich_console.line(1)
118
179
 
119
180
  def finishMessage(
120
181
  self,
121
182
  *,
122
- print_result: bool,
123
183
  summary: Dict[str, Any]
124
184
  ) -> None:
125
185
  """
126
- Display a summary message for the test suite execution.
186
+ Display a final summary message for the test suite execution in a styled panel.
187
+
188
+ This method prints a completion message at the end of the test run, summarizing the overall
189
+ status and total execution time. The message includes a status icon (✅ for success, ❌ for failure)
190
+ based on whether any tests failed or errored. The output is formatted using the Rich library
191
+ for enhanced readability.
127
192
 
128
193
  Parameters
129
194
  ----------
130
195
  summary : dict
131
- Dictionary containing the test suite summary, including keys such as
132
- 'failed', 'errors', and 'total_time'.
196
+ Dictionary containing the test suite summary. Must include the following keys:
197
+ - 'failed': int, number of failed tests
198
+ - 'errors': int, number of errored tests
199
+ - 'total_time': float, total duration of the test suite execution in seconds
200
+
201
+ Returns
202
+ -------
203
+ None
204
+ This method does not return any value. It outputs a formatted completion message to the console.
133
205
 
134
206
  Notes
135
207
  -----
136
- - If `self.print_result` is False, the method returns without displaying anything.
137
- - Shows a status icon (✅ for success, for failure) based on the presence of
138
- failures or errors in the test suite.
139
- - Formats and prints the message within a styled panel using the `rich` library.
208
+ - If `self.__print_result` is False, the method returns without displaying anything.
209
+ - The status icon reflects the presence of failures or errors in the test suite.
210
+ - The message is displayed within a styled Rich panel for clarity.
140
211
  """
141
- if print_result:
142
- status_icon = "✅" if (summary['failed'] + summary['errors']) == 0 else "❌"
143
- msg = f"Test suite completed in {summary['total_time']:.2f} seconds"
144
- self.__rich_console.print(
145
- Panel(
146
- msg,
147
- border_style="blue",
148
- title=f"{status_icon} Test Suite Finished",
149
- title_align='left',
150
- width=self.__panel_width,
151
- padding=(0, 1)
152
- )
212
+
213
+ # If not printing results, return early
214
+ if self.__print_result is False:
215
+ return
216
+
217
+ # Determine status icon based on failures and errors
218
+ status_icon = "" if (summary['failed'] + summary['errors']) == 0 else "❌"
219
+
220
+ # Prepare the completion message with total execution time
221
+ msg = f"Test suite completed in {summary['total_time']:.2f} seconds"
222
+
223
+ # Print the message inside a styled Rich panel
224
+ self.__rich_console.print(
225
+ Panel(
226
+ msg,
227
+ border_style="blue",
228
+ title=f"{status_icon} Test Suite Finished",
229
+ title_align='left',
230
+ width=self.__panel_width,
231
+ padding=(0, 1)
153
232
  )
154
- self.__rich_console.line(1)
233
+ )
234
+
235
+ # Add a blank line after the panel for spacing
236
+ self.__rich_console.line(1)
155
237
 
156
238
  def executePanel(
157
239
  self,
158
240
  *,
159
- print_result: bool,
160
241
  flatten_test_suite: list,
161
242
  callable: callable
162
243
  ):
163
244
  """
164
- Executes a test suite panel with optional live console output.
245
+ Execute a test suite panel with optional live console output and debugging detection.
246
+
247
+ This method manages the display of a running message panel using the Rich library,
248
+ adapting its behavior based on whether debugging or dump calls are detected in the test suite
249
+ and whether result printing is enabled. If debugging or dump calls are present in the test code,
250
+ a static panel is shown before executing the test suite. Otherwise, a live panel is displayed
251
+ during execution for a more dynamic user experience.
165
252
 
166
253
  Parameters
167
254
  ----------
168
- print_result : bool
169
- If True, displays a running message panel while executing the test suite.
170
255
  flatten_test_suite : list
171
- The flattened list of test cases or test suite items to be executed.
256
+ The flattened list of test case instances or test suite items to be executed.
172
257
  callable : callable
173
258
  The function or method to execute the test suite.
174
259
 
175
260
  Returns
176
261
  -------
177
262
  Any
178
- The result returned by the provided callable after execution.
263
+ Returns the result produced by the provided callable after execution, which typically
264
+ contains the outcome of the test suite run (such as a summary or result object).
179
265
 
180
266
  Notes
181
267
  -----
182
- This method manages the display of a running message panel using the Rich library,
183
- depending on whether debugging is enabled in the test suite and whether results should be printed.
184
- If debugging or dump calls are detected in the test code, a live console is used to display
185
- real-time updates. Otherwise, a static panel is shown before executing the test suite.
268
+ - If debugging or dump calls are detected in the test code, a static panel is displayed.
269
+ - If no debugging or dump calls are found and result printing is enabled, a live panel is shown.
270
+ - If result printing is disabled, the test suite is executed without any panel display.
186
271
  """
187
272
 
188
- # Determines if the live console should be used based on the presence of debug or dump calls in the test code.
273
+ # Determine if the test suite contains active debugging or dump calls
189
274
  use_debugger = self.__withDebugger(
190
275
  flatten_test_suite=flatten_test_suite
191
276
  )
192
277
 
193
- # Prepare the running message based on whether live console is enabled
194
- if print_result:
195
- message = "[bold yellow]⏳ Running tests...[/bold yellow]\n"
196
- message += "[dim]This may take a few seconds. Please wait...[/dim]" if use_debugger else "[dim]Please wait, results will appear below...[/dim]"
278
+ # Only display output if printing results is enabled
279
+ if self.__print_result:
197
280
 
198
- # Panel for running message
281
+ # Prepare a minimal running message as a single line, using the configured panel width
199
282
  running_panel = Panel(
200
- message,
283
+ "[yellow]⏳ Running...[/yellow]",
201
284
  border_style="yellow",
202
- title="In Progress",
203
- title_align="left",
204
285
  width=self.__panel_width,
205
- padding=(1, 2)
286
+ padding=(0, 1)
206
287
  )
207
288
 
208
- # Elegant "running" message using Rich Panel
209
- if use_debugger:
289
+ # If no debugger/dump calls, use a live panel for dynamic updates
290
+ if not use_debugger:
291
+
292
+ # Execute the test suite and return its result
210
293
  with Live(running_panel, console=self.__rich_console, refresh_per_second=4, transient=True):
211
294
  return callable()
295
+
212
296
  else:
297
+
298
+ # If debugger/dump calls are present, print a static panel before running
213
299
  self.__rich_console.print(running_panel)
214
300
  return callable()
301
+
215
302
  else:
216
- # If not printing results, run the suite without live console
303
+
304
+ # If result printing is disabled, execute the test suite without any panel
217
305
  return callable()
218
306
 
219
307
  def linkWebReport(
@@ -221,16 +309,42 @@ class TestPrinter(ITestPrinter):
221
309
  path: str
222
310
  ):
223
311
  """
224
- Prints an elegant invitation to view the test results, with an underlined path.
312
+ Display a styled message inviting the user to view the test results report.
313
+
314
+ This method prints an elegant invitation to the console, indicating that the test results
315
+ have been saved and providing a clickable or visually distinct path to the report. The output
316
+ uses Rich's Text styling to highlight the message and underline the report path for emphasis.
225
317
 
226
318
  Parameters
227
319
  ----------
228
- path : str or Path
229
- The path to the test results report.
320
+ path : str
321
+ The file system path or URL to the test results report.
322
+
323
+ Returns
324
+ -------
325
+ None
326
+ This method does not return any value. It outputs a formatted message to the console.
327
+
328
+ Notes
329
+ -----
330
+ - The invitation message is styled with green text for success and an underlined blue path for visibility.
331
+ - Intended to be called after test execution to direct users to the generated report.
230
332
  """
333
+
334
+ # If not printing results, do not display the link
335
+ if self.__print_result is False:
336
+ return
337
+
338
+ # Create the base invitation text with a green style
231
339
  invite_text = Text("Test results saved. ", style="green")
340
+
341
+ # Append a bold green prompt to view the report
232
342
  invite_text.append("View report: ", style="bold green")
343
+
344
+ # Append the report path, styled as underlined blue for emphasis
233
345
  invite_text.append(str(path), style="underline blue")
346
+
347
+ # Print the composed invitation message to the console
234
348
  self.__rich_console.print(invite_text)
235
349
 
236
350
  def summaryTable(
@@ -238,37 +352,49 @@ class TestPrinter(ITestPrinter):
238
352
  summary: Dict[str, Any]
239
353
  ) -> None:
240
354
  """
241
- Prints a summary table of test results using the Rich library.
355
+ Display a summary table of test results using the Rich library.
356
+
357
+ This method prints a formatted table summarizing the results of a test suite execution.
358
+ The table includes the total number of tests, counts of passed, failed, errored, and skipped tests,
359
+ the total execution duration, and the overall success rate. The output is styled for readability
360
+ and is only displayed if result printing is enabled.
242
361
 
243
362
  Parameters
244
363
  ----------
245
364
  summary : dict
246
- Dictionary with the test summary data. Must contain the following keys:
247
- total_tests : int
248
- Total number of tests executed.
249
- passed : int
250
- Number of tests that passed.
251
- failed : int
252
- Number of tests that failed.
253
- errors : int
254
- Number of tests that had errors.
255
- skipped : int
256
- Number of tests that were skipped.
257
- total_time : float
258
- Total duration of the test execution in seconds.
259
- success_rate : float
260
- Percentage of tests that passed.
365
+ Dictionary containing the test summary data. Must include the following keys:
366
+ - total_tests (int): Total number of tests executed.
367
+ - passed (int): Number of tests that passed.
368
+ - failed (int): Number of tests that failed.
369
+ - errors (int): Number of tests that had errors.
370
+ - skipped (int): Number of tests that were skipped.
371
+ - total_time (float): Total duration of the test execution in seconds.
372
+ - success_rate (float): Percentage of tests that passed.
261
373
 
262
374
  Returns
263
375
  -------
264
376
  None
377
+ This method does not return any value. It outputs a formatted summary table to the console
378
+ if result printing is enabled.
379
+
380
+ Notes
381
+ -----
382
+ - The table is only displayed if the `print_result` flag is set to True.
383
+ - The table uses Rich's styling features for enhanced readability.
265
384
  """
385
+
386
+ # If result printing is disabled, do not display the summary table
387
+ if self.__print_result is False:
388
+ return
389
+
390
+ # Create a Rich Table with headers and styling
266
391
  table = Table(
267
392
  show_header=True,
268
393
  header_style="bold white",
269
394
  width=self.__panel_width,
270
395
  border_style="blue"
271
396
  )
397
+ # Add columns for each summary metric
272
398
  table.add_column("Total", justify="center")
273
399
  table.add_column("Passed", justify="center")
274
400
  table.add_column("Failed", justify="center")
@@ -276,6 +402,8 @@ class TestPrinter(ITestPrinter):
276
402
  table.add_column("Skipped", justify="center")
277
403
  table.add_column("Duration", justify="center")
278
404
  table.add_column("Success Rate", justify="center")
405
+
406
+ # Add a row with the summary values, formatting duration and success rate
279
407
  table.add_row(
280
408
  str(summary["total_tests"]),
281
409
  str(summary["passed"]),
@@ -285,44 +413,63 @@ class TestPrinter(ITestPrinter):
285
413
  f"{summary['total_time']:.2f}s",
286
414
  f"{summary['success_rate']:.2f}%"
287
415
  )
416
+
417
+ # Print the summary table to the console
288
418
  self.__rich_console.print(table)
419
+
420
+ # Add a blank line after the table for spacing
289
421
  self.__rich_console.line(1)
290
422
 
291
423
  def displayResults(
292
424
  self,
293
425
  *,
294
- print_result: bool,
295
426
  summary: Dict[str, Any]
296
427
  ) -> None:
297
428
  """
298
- Display the results of the test execution, including a summary table and detailed
299
- information about failed or errored tests grouped by their test classes.
429
+ Display a detailed summary of test execution results, including a summary table and
430
+ grouped panels for failed or errored tests.
431
+
432
+ This method prints a summary table of the test results and, if there are any failed or
433
+ errored tests, displays them grouped by their test class. For each failed or errored test,
434
+ a syntax-highlighted traceback panel is shown, along with metadata such as the test method
435
+ name and execution time. Different icons and border colors are used to distinguish between
436
+ failed and errored tests.
300
437
 
301
438
  Parameters
302
439
  ----------
303
440
  summary : dict
304
- Dictionary containing the summary of the test execution, including test details,
305
- statuses, and execution times.
441
+ Dictionary containing the overall summary and details of the test execution. It must
442
+ include keys such as 'test_details' (list of test result dicts), 'total_tests',
443
+ 'passed', 'failed', 'errors', 'skipped', 'total_time', and 'success_rate'.
444
+
445
+ Returns
446
+ -------
447
+ None
448
+ This method does not return any value. It outputs the formatted summary table and
449
+ detailed panels for failed or errored tests to the console.
306
450
 
307
451
  Notes
308
452
  -----
309
- - Prints a summary table of the test results.
310
- - Groups failed and errored tests by their test class and displays them in a structured
311
- format using panels.
312
- - For each failed or errored test, displays the traceback in a syntax-highlighted panel
313
- with additional metadata such as the test method name and execution time.
314
- - Uses different icons and border colors to distinguish between failed and errored tests.
315
- - Calls a finishing message method after displaying all results.
453
+ - The summary table provides an overview of the test results.
454
+ - Failed and errored tests are grouped and displayed by their test class.
455
+ - Each failed or errored test is shown in a panel with a syntax-highlighted traceback,
456
+ test method name, and execution time.
457
+ - Icons and border colors visually distinguish between failed (❌, yellow) and errored
458
+ (💥, red) tests.
459
+ - No output is produced if result printing is disabled.
316
460
  """
317
461
 
318
- # If not printing results, return early
319
- if not print_result:
462
+ # If result printing is disabled, do not display results
463
+ if not self.__print_result:
320
464
  return
321
465
 
322
- # Print summary table
466
+ # Print one blank line before the summary
467
+ self.__rich_console.line(1)
468
+
469
+ # Print the summary table of test results
323
470
  self.summaryTable(summary)
324
471
 
325
- # Group failures and errors by test class
472
+ # Group failed and errored tests by their test class
326
473
  failures_by_class = {}
327
474
  for test in summary["test_details"]:
328
475
  if test["status"] in (TestStatus.FAILED.name, TestStatus.ERRORED.name):
@@ -331,14 +478,22 @@ class TestPrinter(ITestPrinter):
331
478
  failures_by_class[class_name] = []
332
479
  failures_by_class[class_name].append(test)
333
480
 
334
- # Display grouped failures
481
+ # Display grouped failures and errors for each test class
335
482
  for class_name, tests in failures_by_class.items():
336
483
 
337
- class_panel = Panel.fit(f"[bold]{class_name}[/bold]", border_style="red", padding=(0, 2))
484
+ # Print a panel with the class name as the header
485
+ class_panel = Panel.fit(
486
+ f"[bold]{class_name}[/bold]",
487
+ border_style="red",
488
+ padding=(0, 2)
489
+ )
338
490
  self.__rich_console.print(class_panel)
339
491
 
340
492
  for test in tests:
493
+ # Sanitize the traceback to show only relevant parts
341
494
  traceback_str = self.__sanitizeTraceback(test['file_path'], test['traceback'])
495
+
496
+ # Create a syntax-highlighted panel for the traceback
342
497
  syntax = Syntax(
343
498
  traceback_str,
344
499
  lexer="python",
@@ -348,6 +503,7 @@ class TestPrinter(ITestPrinter):
348
503
  theme="monokai"
349
504
  )
350
505
 
506
+ # Choose icon and border color based on test status
351
507
  icon = "❌" if test["status"] == TestStatus.FAILED.name else "💥"
352
508
  border_color = "yellow" if test["status"] == TestStatus.FAILED.name else "red"
353
509
 
@@ -355,6 +511,7 @@ class TestPrinter(ITestPrinter):
355
511
  if not test['execution_time'] or test['execution_time'] == 0:
356
512
  test['execution_time'] = 0.001
357
513
 
514
+ # Print the panel with traceback and test metadata
358
515
  panel = Panel(
359
516
  syntax,
360
517
  title=f"{icon} {test['method']}",
@@ -368,47 +525,121 @@ class TestPrinter(ITestPrinter):
368
525
  self.__rich_console.print(panel)
369
526
  self.__rich_console.line(1)
370
527
 
528
+ def unittestResult(
529
+ self,
530
+ test_result: TestResult
531
+ ) -> None:
532
+ """
533
+ Display the result of a single unit test in a formatted manner using the Rich library.
534
+
535
+ This method prints the outcome of an individual unit test to the console, showing a status icon
536
+ (✅ for passed, ❌ for failed) along with the test name. If the test failed, the first line of the
537
+ error message is also displayed for quick reference. The output is styled for clarity and does not
538
+ use syntax highlighting.
539
+
540
+ Parameters
541
+ ----------
542
+ test_result : Any
543
+ An object representing the result of a unit test. It must have the following attributes:
544
+ - status: An enum or object with a 'name' attribute indicating the test status (e.g., "PASSED", "FAILED").
545
+ - name: The name of the test.
546
+ - error_message: The error message string (present if the test failed).
547
+
548
+ Returns
549
+ -------
550
+ None
551
+ This method does not return any value. It outputs the formatted test result to the console.
552
+
553
+ Notes
554
+ -----
555
+ - If the test passed, only the status and test name are displayed.
556
+ - If the test failed, the status, test name, and the first line of the error message are shown.
557
+ - Output is printed using the Rich console without syntax highlighting.
558
+ """
559
+
560
+ # If result printing is disabled, do not display results
561
+ if not self.__print_result:
562
+ return
563
+
564
+ # Determine the status icon and label based on the test result
565
+ if test_result.status.name == "PASSED":
566
+ status = "✅ PASSED"
567
+ elif test_result.status.name == "FAILED":
568
+ status = "❌ FAILED"
569
+ elif test_result.status.name == "SKIPPED":
570
+ status = "⏩ SKIPPED"
571
+ elif test_result.status.name == "ERRORED":
572
+ status = "💥 ERRORED"
573
+ else:
574
+ status = f"🔸 {test_result.status.name}"
575
+
576
+ msg = f"[{status}] {test_result.name}"
577
+
578
+ if test_result.status.name == "FAILED":
579
+ msg += f" | Error: {test_result.error_message.splitlines()[0].strip()}"
580
+
581
+ max_width = self.__rich_console.width - 2
582
+ display_msg = msg if len(msg) <= max_width else msg[:max_width - 3] + "..."
583
+ self.__rich_console.print(display_msg, highlight=False)
584
+
371
585
  def __withDebugger(
372
586
  self,
373
587
  flatten_test_suite: list
374
588
  ) -> bool:
375
589
  """
376
- Checks if any test case in the provided flattened test suite uses debugging or dumping methods.
377
- This method inspects the source code of each test case to determine if it contains
378
- calls to 'self.dd' or 'self.dump' that are not commented out. If such a call is found,
379
- the method returns True, indicating that a debugger or dump method is used.
590
+ Determine if any test case in the provided flattened test suite contains active debugging or dumping calls.
591
+
592
+ This method inspects the source code of each test case instance in the given list to check for the presence
593
+ of specific debugging or dumping method calls (such as 'self.dd' or 'self.dump'). Only lines that are not
594
+ commented out are considered. If any such call is found in the source code, the method immediately returns True,
595
+ indicating that a debugger or dump method is actively used in the test suite.
380
596
 
381
597
  Parameters
382
598
  ----------
383
599
  flatten_test_suite : list
384
- A list of test case instances to inspect.
600
+ A list of test case instances whose source code will be inspected for debugging or dumping calls.
385
601
 
386
602
  Returns
387
603
  -------
388
604
  bool
389
- True if any test case uses 'self.dd' or 'self.dump' outside of comments,
390
- False otherwise.
605
+ Returns True if any test case contains an active (non-commented) call to a debugging or dumping method
606
+ (e.g., 'self.dd' or 'self.dump'). Returns False if no such calls are found or if an exception occurs
607
+ during inspection.
391
608
 
392
609
  Notes
393
610
  -----
394
- Lines that contain the keywords but are commented out (i.e., start with '#') are ignored.
395
- If an exception occurs during the inspection process, the method conservatively returns False.
611
+ - Lines that are commented out (i.e., start with '#') are ignored during inspection.
612
+ - If an exception occurs while retrieving or processing the source code, the method returns False.
396
613
  """
397
614
 
398
615
  try:
616
+
617
+ # Iterate through each test case in the flattened test suite
399
618
  for test_case in flatten_test_suite:
619
+
620
+ # Retrieve the source code of the test case using reflection
400
621
  source = ReflectionInstance(test_case).getSourceCode()
622
+
623
+ # Check each line of the source code
401
624
  for line in source.splitlines():
625
+
626
+ # Strip leading and trailing whitespace from the line
402
627
  stripped = line.strip()
403
- # Ignore commented lines
628
+
629
+ # Skip lines that are commented out
404
630
  if stripped.startswith('#') or re.match(r'^\s*#', line):
405
631
  continue
406
- # Check for any debug keyword in the line
632
+
633
+ # If any debug keyword is present in the line, return True
407
634
  if any(keyword in line for keyword in self.__debbug_keywords):
408
- return False
409
- return True
635
+ return True
636
+
637
+ # No debug or dump calls found in any test case
638
+ return False
639
+
410
640
  except Exception:
411
- # If any error occurs, assume debugger is not used
641
+
642
+ # If any error occurs during inspection, return False
412
643
  return False
413
644
 
414
645
  def __sanitizeTraceback(
@@ -417,54 +648,66 @@ class TestPrinter(ITestPrinter):
417
648
  traceback_test: str
418
649
  ) -> str:
419
650
  """
420
- Sanitize a traceback string to extract and display the most relevant parts
421
- related to a specific test file.
651
+ Extract and return the most relevant portion of a traceback string that pertains to a specific test file.
652
+
653
+ This method processes a full Python traceback and attempts to isolate the lines that are directly related
654
+ to the provided test file. It does so by searching for the test file's name within the traceback and collecting
655
+ all subsequent lines that are relevant, such as those containing 'File' or non-empty lines. If the test file's
656
+ name cannot be determined or no relevant lines are found, the original traceback is returned. If the traceback
657
+ is empty, a default message is returned.
422
658
 
423
659
  Parameters
424
660
  ----------
425
661
  test_path : str
426
- The file path of the test file being analyzed.
662
+ The file path of the test file whose related traceback lines should be extracted.
427
663
  traceback_test : str
428
- The full traceback string to be sanitized.
664
+ The complete traceback string to be sanitized.
429
665
 
430
666
  Returns
431
667
  -------
432
668
  str
433
- A sanitized traceback string containing only the relevant parts related to the test file.
434
- If no relevant parts are found, the full traceback is returned.
435
- If the traceback is empty, a default message "No traceback available for this test." is returned.
669
+ Returns a string containing only the relevant traceback lines associated with the test file.
670
+ If no relevant lines are found or the file name cannot be determined, the full traceback is returned.
671
+ If the traceback is empty, returns "No traceback available for this test."
436
672
  """
437
673
 
438
- # Check if the traceback is empty
674
+ # Return a default message if the traceback is empty
439
675
  if not traceback_test:
440
676
  return "No traceback available for this test."
441
677
 
442
- # Try to extract the test file name
678
+ # Attempt to extract the test file's name (without extension) from the provided path
443
679
  file_match = re.search(r'([^/\\]+)\.py', test_path)
444
680
  file_name = file_match.group(1) if file_match else None
445
681
 
446
- # If we can't find the file name, return the full traceback
682
+ # If the file name cannot be determined, return the full traceback
447
683
  if not file_name:
448
684
  return traceback_test
449
685
 
450
- # Process traceback to show most relevant parts
686
+ # Split the traceback into individual lines for processing
451
687
  lines = traceback_test.splitlines()
452
688
  relevant_lines = []
689
+
690
+ # Determine if the test file is present in the traceback
691
+ # If not found, set found_test_file to True to include all lines
453
692
  found_test_file = False if file_name in traceback_test else True
454
693
 
455
- # Iterate through the traceback lines to find relevant parts
694
+ # Iterate through each line of the traceback
456
695
  for line in lines:
696
+
697
+ # Mark when the test file is first encountered in the traceback
457
698
  if file_name in line and not found_test_file:
458
699
  found_test_file = True
700
+
701
+ # Once the test file is found, collect relevant lines
459
702
  if found_test_file:
460
703
  if 'File' in line:
461
704
  relevant_lines.append(line.strip())
462
705
  elif line.strip() != '':
463
706
  relevant_lines.append(line)
464
707
 
465
- # If we didn't find the test file, return the full traceback
708
+ # If no relevant lines were found, return the full traceback
466
709
  if not relevant_lines:
467
710
  return traceback_test
468
711
 
469
- # Remove any lines that are not relevant to the test file
712
+ # Join and return only the relevant lines as a single string
470
713
  return str('\n').join(relevant_lines)