orionis 0.291.0__py3-none-any.whl → 0.293.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.
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.291.0"
8
+ VERSION = "0.293.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -25,6 +25,8 @@ from orionis.test.exceptions.test_failure_exception import OrionisTestFailureExc
25
25
  from orionis.test.exceptions.test_value_error import OrionisTestValueError
26
26
  from orionis.test.logs.history import TestHistory
27
27
  from orionis.test.suites.contracts.test_unit import IUnitTest
28
+ from orionis.test.view.render import TestingResultRender
29
+ from rich.text import Text
28
30
 
29
31
  class UnitTest(IUnitTest):
30
32
  """
@@ -757,6 +759,15 @@ class UnitTest(IUnitTest):
757
759
  if self.persistent:
758
760
  self._persistTestResults(report)
759
761
 
762
+ # Handle Web Report Rendering
763
+ path = self._webReport(report)
764
+
765
+ # Elegant invitation to view the results, with underlined path
766
+ invite_text = Text("Test results saved. ", style="green")
767
+ invite_text.append("View report: ", style="bold green")
768
+ invite_text.append(str(path), style="underline blue")
769
+ self.rich_console.print(invite_text)
770
+
760
771
  # Return the summary
761
772
  return {
762
773
  "total_tests": result.testsRun,
@@ -769,6 +780,45 @@ class UnitTest(IUnitTest):
769
780
  "test_details": test_details
770
781
  }
771
782
 
783
+ def _webReport(self, summary: Dict[str, Any]) -> None:
784
+ """
785
+ Generates a web report for the test results summary.
786
+
787
+ Parameters
788
+ ----------
789
+ summary : dict
790
+ The summary of test results to generate a web report for.
791
+
792
+ Returns
793
+ -------
794
+ str
795
+ The path to the generated web report.
796
+
797
+ Notes
798
+ -----
799
+ - Determines the storage path based on the current working directory and base_path.
800
+ - Uses TestingResultRender to generate the report.
801
+ - If persistence is enabled and the driver is 'sqlite', the report is marked as persistent.
802
+ - Returns the path to the generated report for further use.
803
+ """
804
+ # Determine the absolute path for storing results
805
+ project = os.path.basename(os.getcwd())
806
+ storage_path = os.path.abspath(os.path.join(os.getcwd(), self.base_path))
807
+
808
+ # Only use storage_path if project is recognized
809
+ if project not in ['framework', 'orionis']:
810
+ storage_path = None
811
+
812
+ # Create the TestingResultRender instance with the storage path and summary
813
+ render = TestingResultRender(
814
+ storage_path=storage_path,
815
+ result=summary,
816
+ persist=self.persistent and self.persistent_driver == 'sqlite'
817
+ )
818
+
819
+ # Render the report and return the path
820
+ return render.render()
821
+
772
822
  def _persistTestResults(self, summary: Dict[str, Any]) -> None:
773
823
  """
774
824
  Persist the test results summary using the configured persistent driver.
@@ -793,13 +843,13 @@ class UnitTest(IUnitTest):
793
843
  """
794
844
 
795
845
  try:
796
- if self.persistent_driver == 'sqlite':
846
+ # Determine the absolute path for storing results
847
+ project = os.getcwd().split(os.sep)[-1]
848
+ storage_path = None
849
+ if project in ['framework', 'orionis']:
850
+ storage_path = os.path.abspath(os.path.join(os.getcwd(), self.base_path))
797
851
 
798
- # Determine the absolute path for storing results
799
- project = os.getcwd().split(os.sep)[-1]
800
- storage_path = None
801
- if project in ['framework', 'orionis']:
802
- storage_path = os.path.abspath(os.path.join(os.getcwd(), self.base_path))
852
+ if self.persistent_driver == 'sqlite':
803
853
 
804
854
  # Initialize the TestHistory class for database operations
805
855
  history = TestHistory(
@@ -813,14 +863,14 @@ class UnitTest(IUnitTest):
813
863
 
814
864
  elif self.persistent_driver == 'json':
815
865
 
866
+ # Ensure the base path exists and write the summary to a JSON file
867
+ os.makedirs(storage_path, exist_ok=True)
868
+
816
869
  # Get the current timestamp for the log file name
817
870
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
818
871
 
819
- # Ensure the base path exists and write the summary to a JSON file
820
- os.makedirs(self.base_path, exist_ok=True)
821
-
822
872
  # Create the log file path with the timestamp
823
- log_path = os.path.join(self.base_path, f'test_{timestamp}.json')
873
+ log_path = os.path.abspath(os.path.join(storage_path, f'test_{timestamp}.json'))
824
874
 
825
875
  # Write the summary to the JSON file
826
876
  with open(log_path, 'w', encoding='utf-8') as log:
@@ -0,0 +1,128 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from orionis.services.environment.env import Env
5
+ from orionis.test.logs.history import TestHistory
6
+
7
+ class TestingResultRender:
8
+
9
+ def __init__(
10
+ self,
11
+ result,
12
+ storage_path:str = None,
13
+ persist=False
14
+ ):
15
+ """
16
+ Initialize the TestingResultRender object.
17
+
18
+ Parameters
19
+ ----------
20
+ result : Any
21
+ The test result data to be processed or stored.
22
+ storage_path : str, optional
23
+ Custom path to store the test report. If not provided, uses the environment variable
24
+ 'TEST_REPORT_PATH' or defaults to a local storage path.
25
+ persist : bool, optional
26
+ Whether to persist the report. Defaults to False.
27
+
28
+ Notes
29
+ -----
30
+ - Determines the file path for the test report based on the provided storage_path, environment variable,
31
+ or a default location.
32
+ - Ensures the parent directory for the report exists.
33
+ - Stores the resolved report path in the environment variable 'TEST_REPORT_PATH'.
34
+ """
35
+
36
+ # Initialize instance variables
37
+ self.__filename = 'test-results.html'
38
+ self.__result = result
39
+ self.__persist = persist
40
+
41
+ # Determine file path
42
+ db_path = None
43
+ if storage_path:
44
+ db_path = Path(storage_path).expanduser().resolve()
45
+ if db_path.is_dir():
46
+ db_path = db_path / self.__filename
47
+ else:
48
+ env_path = Env.get("TEST_REPORT_PATH", None)
49
+ if env_path:
50
+ db_path = Path(env_path).expanduser().resolve()
51
+ if db_path.is_dir():
52
+ db_path = db_path / self.__filename
53
+ else:
54
+ db_path = Path.cwd() / 'storage/framework/testing' / self.__filename
55
+
56
+ # Ensure parent directory exists
57
+ db_path.parent.mkdir(parents=True, exist_ok=True)
58
+
59
+ # Store path in environment
60
+ Env.set("TEST_REPORT_PATH", str(db_path), 'path')
61
+ self.__report_path = db_path
62
+
63
+ def render(self):
64
+ """
65
+ Otherwise, uses the current test result stored in memory. The method replaces placeholders in a
66
+ template file with the test results and the persistence mode, then writes the rendered content
67
+ to a report file.
68
+
69
+ Parameters
70
+ ----------
71
+ None
72
+
73
+ Returns
74
+ -------
75
+ str
76
+ The full path to the generated report file.
77
+
78
+ Notes
79
+ -----
80
+ - If persistence is enabled, the last 10 reports are fetched from the SQLite database.
81
+ - If persistence is not enabled, only the current test result in memory is used.
82
+ - The method reads a template file, replaces placeholders with the test results and persistence mode,
83
+ and writes the final content to the report file.
84
+ """
85
+
86
+ # Determine the source of test results based on persistence mode
87
+ if self.__persist:
88
+ # If persistence is enabled, fetch the last 10 reports from SQLite
89
+ logs = TestHistory()
90
+ reports = logs.get(last=10)
91
+ # Parse each report's JSON data into a list
92
+ results_list = [json.loads(report[1]) for report in reports]
93
+ else:
94
+ # If not persistent, use only the current in-memory result
95
+ results_list = [self.__result]
96
+
97
+ # Set placeholder values for the template
98
+ persistence_mode = 'SQLite' if self.__persist else 'Static'
99
+ test_results_json = json.dumps(results_list, ensure_ascii=False, indent=None)
100
+
101
+ # Locate the HTML template file
102
+ template_path = Path(__file__).parent / 'report.stub'
103
+
104
+ # Read the template content
105
+ with open(template_path, 'r', encoding='utf-8') as template_file:
106
+ template_content = template_file.read()
107
+
108
+ # Replace placeholders with actual values
109
+ rendered_content = template_content.replace(
110
+ '{{orionis-testing-result}}',
111
+ test_results_json
112
+ ).replace(
113
+ '{{orionis-testing-persistent}}',
114
+ persistence_mode
115
+ )
116
+
117
+ # Write the rendered HTML report to the specified path
118
+ with open(self.__report_path, 'w', encoding='utf-8') as report_file:
119
+ report_file.write(rendered_content)
120
+
121
+ # Open the generated report in the default web browser if running on Windows or macOS.
122
+ # This provides immediate feedback to the user after report generation.
123
+ if os.name == 'nt' or os.name == 'posix' and sys.platform == 'darwin':
124
+ import webbrowser
125
+ webbrowser.open(self.__report_path.as_uri())
126
+
127
+ # Return the absolute path to the generated report
128
+ return str(self.__report_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.291.0
3
+ Version: 0.293.0
4
4
  Summary: Orionis Framework – Elegant, Fast, and Powerful.
5
5
  Home-page: https://github.com/orionis-framework/framework
6
6
  Author: Raul Mauricio Uñate Castro
@@ -226,7 +226,7 @@ orionis/foundation/config/testing/entities/testing.py,sha256=m_i9jZlOXs_AzNKNNf0
226
226
  orionis/foundation/config/testing/enums/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
227
227
  orionis/foundation/config/testing/enums/test_mode.py,sha256=IbFpauu7J-iSAfmC8jDbmTEYl8eZr-AexL-lyOh8_74,337
228
228
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
229
- orionis/metadata/framework.py,sha256=gFGa1P4sgpUA5AUIlQeePV9NZLz4zPtOpg5N1Z_7yI8,4960
229
+ orionis/metadata/framework.py,sha256=QmC-g5i6UhTE0QWIF78bMyfW6Dm4d4521_PuYknYoGQ,4960
230
230
  orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
231
231
  orionis/patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
232
232
  orionis/patterns/singleton/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -345,13 +345,13 @@ orionis/test/output/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
345
345
  orionis/test/output/contracts/dumper.py,sha256=5OqGc4GEXCXX76sCX185giQMyKwwZvlOv3I7tTwV2fQ,1324
346
346
  orionis/test/suites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
347
347
  orionis/test/suites/test_suite.py,sha256=fdVmC56PJfWDuYeekY7oN-04AEHMqxwLI5mZNJAuOZI,5261
348
- orionis/test/suites/test_unit.py,sha256=ZkpRyUEHsxnbto3FBmvPfFHHRyOgv4NBIcS2LF1-Y_s,49374
348
+ orionis/test/suites/test_unit.py,sha256=JAla82Uc2OEvvBTE8oI8BT0hxeb03FJB1bdKU7G2xlA,51293
349
349
  orionis/test/suites/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
350
350
  orionis/test/suites/contracts/test_suite.py,sha256=eluzYwkNBbKjxYStj_tHN_Fm3YDPpGQdqMu5eiluh-E,1059
351
351
  orionis/test/suites/contracts/test_unit.py,sha256=l1LQllODyvcSByXMl1lGrUkoLsXbBHZZLWZI4A-mlQg,5881
352
352
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
353
- orionis/test/view/index.html,sha256=U4XYO4hA-mAJCK1gcVRgIysmISK3g3Vgi2ntLofFAhE,6592
354
- orionis-0.291.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
353
+ orionis/test/view/render.py,sha256=jXZkbITBknbUwm_mD8bcTiwLDvsFkrO9qrf0ZgPwqxc,4903
354
+ orionis-0.293.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
355
355
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
356
356
  tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
357
357
  tests/example/test_example.py,sha256=byd_lI6tVDgGPEIrr7PLZbBu0UoZOymmdmyA_4u-QUw,601
@@ -455,8 +455,8 @@ tests/support/inspection/fakes/fake_reflection_instance_with_abstract.py,sha256=
455
455
  tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
456
456
  tests/testing/test_testing_result.py,sha256=MrGK3ZimedL0b5Ydu69Dg8Iul017AzLTm7VPxpXlpfU,4315
457
457
  tests/testing/test_testing_unit.py,sha256=A6QkiOkP7GPC1Szh_GqsrV7GxjWjK8cIwFez6YfrzmM,7683
458
- orionis-0.291.0.dist-info/METADATA,sha256=dKe4TtuURAwVqliKd4odPuHJFE2TplRgxVek6i-jd9A,4772
459
- orionis-0.291.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
460
- orionis-0.291.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
461
- orionis-0.291.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
462
- orionis-0.291.0.dist-info/RECORD,,
458
+ orionis-0.293.0.dist-info/METADATA,sha256=VYamMjDShBXxI6Quu-380uo1bp772T2nDaTlWcmJdwI,4772
459
+ orionis-0.293.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
460
+ orionis-0.293.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
461
+ orionis-0.293.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
462
+ orionis-0.293.0.dist-info/RECORD,,
@@ -1,127 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Orionis Test Dashboard</title>
8
- <script src="https://cdn.tailwindcss.com"></script>
9
- <link rel="icon" href="https://orionis-framework.com/svg/logo.svg" />
10
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
11
- </head>
12
-
13
- <body class="bg-gray-100 text-gray-800 font-sans">
14
- <div class="max-w-7xl mx-auto px-4 sm:px-6 py-8">
15
-
16
- <!-- Header -->
17
- <header class="bg-gradient-to-r from-blue-900 to-cyan-400 text-white rounded-2xl shadow-xl p-6 mb-10">
18
- <div class="flex flex-col md:flex-row justify-between items-center gap-4">
19
- <div class="flex items-center gap-4">
20
- <img src="https://orionis-framework.com/svg/logo.svg" alt="Orionis Logo"
21
- class="h-10 brightness-0 invert" />
22
- <h1 class="text-2xl font-light tracking-wider">Orionis Testing Results Dashboard</h1>
23
- </div>
24
- <div id="timestamp" class="text-sm text-white/90"></div>
25
- </header>
26
-
27
- <!-- Execution Summary Card -->
28
- <div class="w-full mb-10">
29
- <div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-indigo-500 flex flex-col sm:flex-row items-center justify-between gap-4">
30
- <div>
31
- <div class="text-xs font-semibold text-gray-500 uppercase">Resumen de Ejecución</div>
32
- <div class="text-lg font-bold text-gray-800 mt-2" id="execution-summary-title">Ejecución Completa</div>
33
- <div class="text-sm text-gray-600 mt-1" id="execution-summary-desc">Todos los tests han sido ejecutados correctamente.</div>
34
- </div>
35
- <div class="flex items-center gap-4">
36
- <div class="flex items-center gap-1 text-gray-700">
37
- <i class="bi bi-clock-history text-xl text-indigo-500"></i>
38
- <span id="execution-time">Duración: 00:00:00</span>
39
- </div>
40
- </div>
41
- </div>
42
- </div>
43
-
44
- <!-- Summary Cards -->
45
- <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 mb-10">
46
-
47
- <div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-green-500">
48
- <div class="text-xs font-semibold text-gray-500 uppercase">Passed</div>
49
- <div class="text-4xl font-bold text-gray-800 mt-2" id="passed">0</div>
50
- <div class="mt-4 bg-gray-200 rounded-full h-2">
51
- <div class="bg-green-500 h-2 rounded-full" id="passed-progress" style="width: 0%"></div>
52
- </div>
53
- </div>
54
-
55
- <div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-red-500">
56
- <div class="text-xs font-semibold text-gray-500 uppercase">Failed</div>
57
- <div class="text-4xl font-bold text-gray-800 mt-2" id="failed">0</div>
58
- </div>
59
-
60
- <div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-yellow-500">
61
- <div class="text-xs font-semibold text-gray-500 uppercase">Errors</div>
62
- <div class="text-4xl font-bold text-gray-800 mt-2" id="errors">0</div>
63
- </div>
64
-
65
- <div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-blue-500">
66
- <div class="text-xs font-semibold text-gray-500 uppercase">Skipped</div>
67
- <div class="text-4xl font-bold text-gray-800 mt-2" id="skipped">0</div>
68
- </div>
69
-
70
- </div>
71
-
72
- <!-- Download Buttons & Select -->
73
- <div class="flex flex-wrap justify-between items-center mb-10 gap-4">
74
- <!-- Buttons to the left -->
75
- <div class="flex flex-wrap gap-4">
76
- <button id="download-json"
77
- class="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded shadow">
78
- <i class="bi bi-file-earmark-code-fill text-lg"></i>
79
- <span>Download JSON</span>
80
- </button>
81
- <button id="download-excel"
82
- class="flex items-center gap-2 bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded shadow">
83
- <i class="bi bi-file-earmark-excel-fill text-lg"></i>
84
- <span>Download Excel</span>
85
- </button>
86
- <button id="download-pdf"
87
- class="flex items-center gap-2 bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded shadow">
88
- <i class="bi bi-file-earmark-pdf-fill text-lg"></i>
89
- <span>Download PDF</span>
90
- </button>
91
- </div>
92
- <!-- Elegant Select to the right -->
93
- <div>
94
- <select
95
- class="appearance-none bg-white border border-gray-300 text-gray-700 py-2 px-4 pr-10 rounded-lg shadow focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 transition text-base font-medium">
96
- <option selected disabled>Selecciona una opción</option>
97
- <option>Opción 1</option>
98
- <option>Opción 2</option>
99
- <option>Opción 3</option>
100
- </select>
101
- </div>
102
- </div>
103
-
104
- <!-- JSON Viewer Panel -->
105
- <div class="bg-white rounded-2xl shadow-xl p-6">
106
- <h2 class="text-xl font-semibold mb-4">Test Report JSON</h2>
107
- <pre id="json-data"
108
- class="whitespace-pre-wrap text-sm text-gray-800 bg-gray-100 p-4 rounded-xl overflow-x-auto"></pre>
109
- </div>
110
-
111
- <!-- Footer -->
112
- <footer class="mt-12 text-center text-gray-500 text-sm py-6">
113
- Developed with the power of
114
- <a href="https://orionis-framework.com/" target="_blank" rel="noopener"
115
- class="font-semibold text-blue-700 hover:underline">
116
- Orionis Framework
117
- </a>
118
- <i class="bi bi-stars text-yellow-400 align-middle ml-1"></i>
119
- </footer>
120
-
121
- <script>
122
- // Placeholder JS logic
123
- document.getElementById("timestamp").textContent = new Date().toLocaleString();
124
- </script>
125
- </body>
126
-
127
- </html>