quantum-framework 0.9.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 (262) hide show
  1. quantum/__init__.py +1 -0
  2. quantum/cli/__init__.py +2 -0
  3. quantum/cli/commands/__init__.py +15 -0
  4. quantum/cli/commands/build.py +417 -0
  5. quantum/cli/commands/dev.py +185 -0
  6. quantum/cli/commands/docs.py +329 -0
  7. quantum/cli/commands/lint.py +523 -0
  8. quantum/cli/commands/migrate.py +527 -0
  9. quantum/cli/commands/new.py +622 -0
  10. quantum/cli/commands/serve.py +190 -0
  11. quantum/cli/commands/test.py +193 -0
  12. quantum/cli/deploy.py +810 -0
  13. quantum/cli/hot_reload.py +951 -0
  14. quantum/cli/jobs.py +356 -0
  15. quantum/cli/mq.py +582 -0
  16. quantum/cli/pkg.py +390 -0
  17. quantum/cli/runner.py +547 -0
  18. quantum/cli/server_process.py +159 -0
  19. quantum/cli/utils.py +334 -0
  20. quantum/compiler/__init__.py +30 -0
  21. quantum/compiler/base_generator.py +367 -0
  22. quantum/compiler/cli.py +295 -0
  23. quantum/compiler/expression_transformer.py +444 -0
  24. quantum/compiler/javascript/__init__.py +10 -0
  25. quantum/compiler/javascript/generator.py +659 -0
  26. quantum/compiler/optimizer.py +270 -0
  27. quantum/compiler/python/__init__.py +10 -0
  28. quantum/compiler/python/generator.py +883 -0
  29. quantum/compiler/python/runtime.py +863 -0
  30. quantum/compiler/transpiler.py +330 -0
  31. quantum/core/__init__.py +3 -0
  32. quantum/core/ast_nodes.py +2611 -0
  33. quantum/core/expression_diagnostics.py +87 -0
  34. quantum/core/expression_stdlib.py +148 -0
  35. quantum/core/expressions.py +591 -0
  36. quantum/core/features/agents/src/__init__.py +30 -0
  37. quantum/core/features/agents/src/ast_node.py +540 -0
  38. quantum/core/features/conditionals/src/__init__.py +8 -0
  39. quantum/core/features/conditionals/src/ast_node.py +68 -0
  40. quantum/core/features/data_fetching/src/__init__.py +21 -0
  41. quantum/core/features/data_fetching/src/ast_node.py +312 -0
  42. quantum/core/features/data_fetching/src/desktop_adapter.py +351 -0
  43. quantum/core/features/data_fetching/src/html_adapter.py +474 -0
  44. quantum/core/features/data_fetching/src/parser.py +225 -0
  45. quantum/core/features/data_import/src/__init__.py +0 -0
  46. quantum/core/features/data_import/src/ast_node.py +291 -0
  47. quantum/core/features/data_import/src/runtime.py +538 -0
  48. quantum/core/features/dump/src/__init__.py +12 -0
  49. quantum/core/features/dump/src/ast_node.py +106 -0
  50. quantum/core/features/dump/src/parser.py +61 -0
  51. quantum/core/features/dump/src/runtime.py +246 -0
  52. quantum/core/features/functions/src/__init__.py +8 -0
  53. quantum/core/features/functions/src/ast_node.py +149 -0
  54. quantum/core/features/game_engine_2d/src/__init__.py +19 -0
  55. quantum/core/features/game_engine_2d/src/ast_nodes.py +1719 -0
  56. quantum/core/features/game_engine_2d/src/parser.py +983 -0
  57. quantum/core/features/invocation/src/__init__.py +0 -0
  58. quantum/core/features/invocation/src/ast_node.py +146 -0
  59. quantum/core/features/invocation/src/runtime.py +327 -0
  60. quantum/core/features/knowledge_base/src/__init__.py +6 -0
  61. quantum/core/features/knowledge_base/src/ast_node.py +113 -0
  62. quantum/core/features/knowledge_base/src/parser.py +82 -0
  63. quantum/core/features/logging/src/__init__.py +12 -0
  64. quantum/core/features/logging/src/ast_node.py +111 -0
  65. quantum/core/features/logging/src/parser.py +50 -0
  66. quantum/core/features/logging/src/runtime.py +190 -0
  67. quantum/core/features/loops/src/__init__.py +8 -0
  68. quantum/core/features/loops/src/ast_node.py +60 -0
  69. quantum/core/features/query/src/__init__.py +0 -0
  70. quantum/core/features/query/src/database_service.py +322 -0
  71. quantum/core/features/query/src/query_validators.py +20 -0
  72. quantum/core/features/state_management/src/__init__.py +11 -0
  73. quantum/core/features/state_management/src/ast_node.py +228 -0
  74. quantum/core/features/terminal_engine/src/__init__.py +21 -0
  75. quantum/core/features/terminal_engine/src/ast_nodes.py +560 -0
  76. quantum/core/features/terminal_engine/src/parser.py +361 -0
  77. quantum/core/features/testing_engine/src/__init__.py +41 -0
  78. quantum/core/features/testing_engine/src/ast_nodes.py +1212 -0
  79. quantum/core/features/testing_engine/src/parser.py +604 -0
  80. quantum/core/features/theming/src/__init__.py +48 -0
  81. quantum/core/features/theming/src/ast_node.py +137 -0
  82. quantum/core/features/theming/src/presets.py +405 -0
  83. quantum/core/features/ui_engine/src/__init__.py +20 -0
  84. quantum/core/features/ui_engine/src/ast_nodes.py +1854 -0
  85. quantum/core/features/ui_engine/src/parser.py +1106 -0
  86. quantum/core/features/websocket/src/__init__.py +24 -0
  87. quantum/core/features/websocket/src/ast_node.py +247 -0
  88. quantum/core/html_compat.py +299 -0
  89. quantum/core/parser.py +1235 -0
  90. quantum/core/parser_registry.py +213 -0
  91. quantum/core/parsers/__init__.py +76 -0
  92. quantum/core/parsers/ai/__init__.py +12 -0
  93. quantum/core/parsers/ai/agent_parser.py +106 -0
  94. quantum/core/parsers/ai/knowledge_parser.py +86 -0
  95. quantum/core/parsers/ai/llm_parser.py +78 -0
  96. quantum/core/parsers/ai/team_parser.py +88 -0
  97. quantum/core/parsers/base.py +322 -0
  98. quantum/core/parsers/composition/__init__.py +10 -0
  99. quantum/core/parsers/composition/import_parser.py +50 -0
  100. quantum/core/parsers/composition/slot_parser.py +48 -0
  101. quantum/core/parsers/control_flow/__init__.py +11 -0
  102. quantum/core/parsers/control_flow/if_parser.py +68 -0
  103. quantum/core/parsers/control_flow/loop_parser.py +105 -0
  104. quantum/core/parsers/control_flow/set_parser.py +89 -0
  105. quantum/core/parsers/data/__init__.py +12 -0
  106. quantum/core/parsers/data/data_parser.py +217 -0
  107. quantum/core/parsers/data/invoke_parser.py +116 -0
  108. quantum/core/parsers/data/query_parser.py +188 -0
  109. quantum/core/parsers/data/transaction_parser.py +77 -0
  110. quantum/core/parsers/events/__init__.py +9 -0
  111. quantum/core/parsers/events/dispatch_event_parser.py +44 -0
  112. quantum/core/parsers/forms/__init__.py +11 -0
  113. quantum/core/parsers/forms/action_parser.py +54 -0
  114. quantum/core/parsers/forms/flash_parser.py +34 -0
  115. quantum/core/parsers/forms/redirect_parser.py +33 -0
  116. quantum/core/parsers/functions/__init__.py +11 -0
  117. quantum/core/parsers/functions/function_parser.py +137 -0
  118. quantum/core/parsers/functions/param_parser.py +66 -0
  119. quantum/core/parsers/functions/return_parser.py +31 -0
  120. quantum/core/parsers/html/__init__.py +10 -0
  121. quantum/core/parsers/html/component_call_parser.py +130 -0
  122. quantum/core/parsers/html/html_parser.py +115 -0
  123. quantum/core/parsers/jobs/__init__.py +11 -0
  124. quantum/core/parsers/jobs/job_parser.py +71 -0
  125. quantum/core/parsers/jobs/schedule_parser.py +62 -0
  126. quantum/core/parsers/jobs/thread_parser.py +57 -0
  127. quantum/core/parsers/messaging/__init__.py +17 -0
  128. quantum/core/parsers/messaging/message_ack_parser.py +30 -0
  129. quantum/core/parsers/messaging/message_nack_parser.py +30 -0
  130. quantum/core/parsers/messaging/message_parser.py +114 -0
  131. quantum/core/parsers/messaging/queue_parser.py +61 -0
  132. quantum/core/parsers/messaging/websocket_parser.py +121 -0
  133. quantum/core/parsers/persistence/__init__.py +9 -0
  134. quantum/core/parsers/persistence/persist_parser.py +64 -0
  135. quantum/core/parsers/routing/__init__.py +9 -0
  136. quantum/core/parsers/routing/route_parser.py +41 -0
  137. quantum/core/parsers/scripting/__init__.py +12 -0
  138. quantum/core/parsers/scripting/pyclass_parser.py +62 -0
  139. quantum/core/parsers/scripting/pydecorator_parser.py +68 -0
  140. quantum/core/parsers/scripting/pyimport_parser.py +52 -0
  141. quantum/core/parsers/scripting/python_parser.py +49 -0
  142. quantum/core/parsers/services/__init__.py +12 -0
  143. quantum/core/parsers/services/dump_parser.py +52 -0
  144. quantum/core/parsers/services/file_parser.py +48 -0
  145. quantum/core/parsers/services/log_parser.py +46 -0
  146. quantum/core/parsers/services/mail_parser.py +65 -0
  147. quantum/core/tiers.py +82 -0
  148. quantum/packages/__init__.py +28 -0
  149. quantum/packages/manager.py +413 -0
  150. quantum/packages/manifest.py +351 -0
  151. quantum/packages/registry.py +399 -0
  152. quantum/packages/resolver.py +336 -0
  153. quantum/plugins/__init__.py +33 -0
  154. quantum/plugins/hooks.py +329 -0
  155. quantum/plugins/loader.py +479 -0
  156. quantum/plugins/manifest.py +336 -0
  157. quantum/plugins/registry.py +371 -0
  158. quantum/runtime/__init__.py +28 -0
  159. quantum/runtime/action_handler.py +443 -0
  160. quantum/runtime/adapters/__init__.py +88 -0
  161. quantum/runtime/adapters/memory_adapter.py +690 -0
  162. quantum/runtime/adapters/rabbitmq_adapter.py +715 -0
  163. quantum/runtime/adapters/redis_adapter.py +582 -0
  164. quantum/runtime/adapters/sqlite_adapter.py +414 -0
  165. quantum/runtime/agent_service.py +1133 -0
  166. quantum/runtime/api_server.py +86 -0
  167. quantum/runtime/ast_cache.py +506 -0
  168. quantum/runtime/auth_service.py +267 -0
  169. quantum/runtime/component.py +990 -0
  170. quantum/runtime/component_composer.py +319 -0
  171. quantum/runtime/component_resolver.py +174 -0
  172. quantum/runtime/database_service.py +598 -0
  173. quantum/runtime/email_service.py +162 -0
  174. quantum/runtime/error_handler.py +295 -0
  175. quantum/runtime/execution_context.py +286 -0
  176. quantum/runtime/executor_registry.py +171 -0
  177. quantum/runtime/executors/__init__.py +71 -0
  178. quantum/runtime/executors/ai/__init__.py +12 -0
  179. quantum/runtime/executors/ai/agent_executor.py +217 -0
  180. quantum/runtime/executors/ai/knowledge_executor.py +114 -0
  181. quantum/runtime/executors/ai/llm_executor.py +153 -0
  182. quantum/runtime/executors/ai/team_executor.py +171 -0
  183. quantum/runtime/executors/base.py +262 -0
  184. quantum/runtime/executors/control_flow/__init__.py +11 -0
  185. quantum/runtime/executors/control_flow/if_executor.py +93 -0
  186. quantum/runtime/executors/control_flow/loop_executor.py +307 -0
  187. quantum/runtime/executors/control_flow/set_executor.py +412 -0
  188. quantum/runtime/executors/data/__init__.py +12 -0
  189. quantum/runtime/executors/data/data_executor.py +145 -0
  190. quantum/runtime/executors/data/invoke_executor.py +176 -0
  191. quantum/runtime/executors/data/query_executor.py +256 -0
  192. quantum/runtime/executors/data/transaction_executor.py +91 -0
  193. quantum/runtime/executors/jobs/__init__.py +11 -0
  194. quantum/runtime/executors/jobs/job_executor.py +190 -0
  195. quantum/runtime/executors/jobs/schedule_executor.py +132 -0
  196. quantum/runtime/executors/jobs/thread_executor.py +127 -0
  197. quantum/runtime/executors/messaging/__init__.py +17 -0
  198. quantum/runtime/executors/messaging/message_ack_executor.py +51 -0
  199. quantum/runtime/executors/messaging/message_executor.py +174 -0
  200. quantum/runtime/executors/messaging/queue_executor.py +103 -0
  201. quantum/runtime/executors/messaging/websocket_executor.py +197 -0
  202. quantum/runtime/executors/scripting/__init__.py +11 -0
  203. quantum/runtime/executors/scripting/pyclass_executor.py +90 -0
  204. quantum/runtime/executors/scripting/pyimport_executor.py +81 -0
  205. quantum/runtime/executors/scripting/python_executor.py +249 -0
  206. quantum/runtime/executors/services/__init__.py +12 -0
  207. quantum/runtime/executors/services/dump_executor.py +72 -0
  208. quantum/runtime/executors/services/file_executor.py +89 -0
  209. quantum/runtime/executors/services/log_executor.py +77 -0
  210. quantum/runtime/executors/services/mail_executor.py +81 -0
  211. quantum/runtime/expression_cache.py +498 -0
  212. quantum/runtime/file_upload_service.py +326 -0
  213. quantum/runtime/function_registry.py +118 -0
  214. quantum/runtime/game_builder.py +166 -0
  215. quantum/runtime/game_code_generator.py +2371 -0
  216. quantum/runtime/game_templates.py +2006 -0
  217. quantum/runtime/godot_code_generator.py +4681 -0
  218. quantum/runtime/godot_templates.py +1449 -0
  219. quantum/runtime/job_executor.py +1599 -0
  220. quantum/runtime/knowledge_service.py +500 -0
  221. quantum/runtime/llm_cache.py +100 -0
  222. quantum/runtime/llm_providers.py +704 -0
  223. quantum/runtime/llm_service.py +287 -0
  224. quantum/runtime/logging_setup.py +140 -0
  225. quantum/runtime/message_broker.py +364 -0
  226. quantum/runtime/message_queue_service.py +571 -0
  227. quantum/runtime/param_validation.py +184 -0
  228. quantum/runtime/pypy_compat.py +315 -0
  229. quantum/runtime/python_bridge.py +698 -0
  230. quantum/runtime/query_validators.py +304 -0
  231. quantum/runtime/renderer.py +733 -0
  232. quantum/runtime/service_container.py +444 -0
  233. quantum/runtime/terminal_builder.py +76 -0
  234. quantum/runtime/terminal_code_generator.py +607 -0
  235. quantum/runtime/terminal_templates.py +243 -0
  236. quantum/runtime/testing_builder.py +77 -0
  237. quantum/runtime/testing_code_generator.py +833 -0
  238. quantum/runtime/testing_templates.py +85 -0
  239. quantum/runtime/ui_builder.py +188 -0
  240. quantum/runtime/ui_desktop_adapter.py +1730 -0
  241. quantum/runtime/ui_desktop_templates.py +307 -0
  242. quantum/runtime/ui_html_adapter.py +2691 -0
  243. quantum/runtime/ui_html_templates.py +2297 -0
  244. quantum/runtime/ui_mobile_adapter.py +1832 -0
  245. quantum/runtime/ui_mobile_templates.py +1003 -0
  246. quantum/runtime/ui_textual_adapter.py +1866 -0
  247. quantum/runtime/ui_textual_templates.py +45 -0
  248. quantum/runtime/ui_tokens.py +465 -0
  249. quantum/runtime/ui_validator.py +365 -0
  250. quantum/runtime/validators.py +256 -0
  251. quantum/runtime/web_server.py +1766 -0
  252. quantum/runtime/websocket_adapter.py +501 -0
  253. quantum/runtime/websocket_service.py +585 -0
  254. quantum/runtime/websocket_transport.py +289 -0
  255. quantum/runtime/wsgi.py +101 -0
  256. quantum/utils/__init__.py +1 -0
  257. quantum_framework-0.9.0.dist-info/METADATA +244 -0
  258. quantum_framework-0.9.0.dist-info/RECORD +262 -0
  259. quantum_framework-0.9.0.dist-info/WHEEL +5 -0
  260. quantum_framework-0.9.0.dist-info/entry_points.txt +2 -0
  261. quantum_framework-0.9.0.dist-info/licenses/LICENSE +21 -0
  262. quantum_framework-0.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,604 @@
1
+ """
2
+ Testing Engine Parser - Parse qtest: namespace elements into Testing AST nodes.
3
+
4
+ This module provides parse functions for all testing-specific tags.
5
+ It is called from the main QuantumParser when a qtest: prefixed tag is encountered.
6
+ """
7
+
8
+ from xml.etree import ElementTree as ET
9
+ from typing import Optional, List
10
+
11
+ from .ast_nodes import (
12
+ # Core testing nodes
13
+ QTestSuiteNode, QTestCaseNode, ExpectNode, MockNode_Testing,
14
+ FixtureNode_Testing, SetupNode_Testing, TeardownNode_Testing,
15
+ BeforeEachNode, AfterEachNode, GenerateNode,
16
+ ScenarioNode, GivenNode, WhenNode, ThenNode,
17
+ # Browser testing nodes
18
+ BrowserConfigNode, NavigateNode, ClickNode, DblClickNode,
19
+ RightClickNode, HoverNode, FillNode, TypeNode_Testing,
20
+ SelectNode_Testing, CheckNode, UncheckNode, KeyboardNode,
21
+ DragNode, ScrollNode_Testing, UploadNode, DownloadNode,
22
+ WaitForNode, ScreenshotNode, InterceptNode, InterceptRespondNode,
23
+ FrameNode_Testing, PopupNode, AuthNode, SaveStateNode, PauseNode,
24
+ # Advanced testing nodes
25
+ SnapshotNode, FuzzNode, PropertyNode, FuzzFieldNode,
26
+ PerfNode, PerfMetricNode, A11yNode, ChaosNode, ChaosFailNode,
27
+ GeolocationNode,
28
+ )
29
+
30
+
31
+ class TestingParseError(Exception):
32
+ """Testing-specific parse error."""
33
+ pass
34
+
35
+
36
+ class TestingParser:
37
+ """Parser for qtest: namespace testing elements."""
38
+
39
+ def __init__(self, parent_parser):
40
+ """
41
+ Args:
42
+ parent_parser: The main QuantumParser instance, used to parse
43
+ q: namespace children (q:set, q:function, etc.)
44
+ """
45
+ self.parent = parent_parser
46
+
47
+ # ------------------------------------------------------------------
48
+ # Dispatch
49
+ # ------------------------------------------------------------------
50
+
51
+ TESTING_TAG_MAP = {
52
+ # Core testing
53
+ 'suite': '_parse_testing_suite',
54
+ 'case': '_parse_testing_case',
55
+ 'expect': '_parse_testing_expect',
56
+ 'mock': '_parse_testing_mock',
57
+ 'fixture': '_parse_testing_fixture',
58
+ 'setup': '_parse_testing_setup',
59
+ 'teardown': '_parse_testing_teardown',
60
+ 'before-each': '_parse_testing_before_each',
61
+ 'after-each': '_parse_testing_after_each',
62
+ 'generate': '_parse_testing_generate',
63
+ 'scenario': '_parse_testing_scenario',
64
+ 'given': '_parse_testing_given',
65
+ 'when': '_parse_testing_when',
66
+ 'then': '_parse_testing_then',
67
+ # Browser testing
68
+ 'browser-config': '_parse_testing_browser_config',
69
+ 'navigate': '_parse_testing_navigate',
70
+ 'click': '_parse_testing_click',
71
+ 'dblclick': '_parse_testing_dblclick',
72
+ 'right-click': '_parse_testing_right_click',
73
+ 'hover': '_parse_testing_hover',
74
+ 'fill': '_parse_testing_fill',
75
+ 'type': '_parse_testing_type',
76
+ 'select': '_parse_testing_select',
77
+ 'check': '_parse_testing_check',
78
+ 'uncheck': '_parse_testing_uncheck',
79
+ 'keyboard': '_parse_testing_keyboard',
80
+ 'drag': '_parse_testing_drag',
81
+ 'scroll': '_parse_testing_scroll',
82
+ 'upload': '_parse_testing_upload',
83
+ 'download': '_parse_testing_download',
84
+ 'wait-for': '_parse_testing_wait_for',
85
+ 'screenshot': '_parse_testing_screenshot',
86
+ 'intercept': '_parse_testing_intercept',
87
+ 'respond': '_parse_testing_respond',
88
+ 'frame': '_parse_testing_frame',
89
+ 'popup': '_parse_testing_popup',
90
+ 'auth': '_parse_testing_auth',
91
+ 'save-state': '_parse_testing_save_state',
92
+ 'pause': '_parse_testing_pause',
93
+ # Advanced testing
94
+ 'snapshot': '_parse_testing_snapshot',
95
+ 'fuzz': '_parse_testing_fuzz',
96
+ 'property': '_parse_testing_property',
97
+ 'field': '_parse_testing_field',
98
+ 'perf': '_parse_testing_perf',
99
+ 'metric': '_parse_testing_metric',
100
+ 'a11y': '_parse_testing_a11y',
101
+ 'chaos': '_parse_testing_chaos',
102
+ 'fail': '_parse_testing_fail',
103
+ 'geolocation': '_parse_testing_geolocation',
104
+ }
105
+
106
+ def parse_testing_element(self, local_name: str, element: ET.Element):
107
+ """Dispatch a qtest: element to the correct parse method."""
108
+ method_name = self.TESTING_TAG_MAP.get(local_name)
109
+ if method_name is None:
110
+ raise TestingParseError(f"Unknown testing tag: qtest:{local_name}")
111
+ method = getattr(self, method_name)
112
+ return method(element)
113
+
114
+ # ------------------------------------------------------------------
115
+ # Helpers
116
+ # ------------------------------------------------------------------
117
+
118
+ def _get_local_name(self, element: ET.Element) -> str:
119
+ tag = element.tag
120
+ if '}' in tag:
121
+ return tag.split('}')[-1]
122
+ if ':' in tag:
123
+ return tag.split(':')[-1]
124
+ return tag
125
+
126
+ def _get_namespace(self, element: ET.Element) -> Optional[str]:
127
+ tag = element.tag
128
+ if '{https://quantum.lang/testing}' in tag:
129
+ return 'testing'
130
+ if '{https://quantum.lang/ns}' in tag:
131
+ return 'quantum'
132
+ if tag.startswith('qtest:'):
133
+ return 'testing'
134
+ if tag.startswith('q:'):
135
+ return 'quantum'
136
+ return None
137
+
138
+ def _parse_float(self, value: Optional[str], default: float = 0) -> float:
139
+ if value is None:
140
+ return default
141
+ try:
142
+ return float(value)
143
+ except ValueError:
144
+ return default
145
+
146
+ def _parse_int(self, value: Optional[str], default: int = 0) -> int:
147
+ if value is None:
148
+ return default
149
+ try:
150
+ return int(value)
151
+ except ValueError:
152
+ return default
153
+
154
+ def _parse_bool(self, value: Optional[str], default: bool = False) -> bool:
155
+ if value is None:
156
+ return default
157
+ return value.lower() in ('true', '1', 'yes')
158
+
159
+ # ------------------------------------------------------------------
160
+ # Parse children - dispatches both qtest: and q: tags
161
+ # ------------------------------------------------------------------
162
+
163
+ def _parse_child(self, child: ET.Element):
164
+ """Parse a child element that can be either qtest: or q: namespace."""
165
+ ns = self._get_namespace(child)
166
+ local = self._get_local_name(child)
167
+
168
+ if ns == 'testing':
169
+ return self.parse_testing_element(local, child)
170
+ elif ns == 'quantum':
171
+ return self.parent._parse_statement(child)
172
+ return None
173
+
174
+ # ------------------------------------------------------------------
175
+ # Core testing nodes
176
+ # ------------------------------------------------------------------
177
+
178
+ def _parse_testing_suite(self, element: ET.Element) -> QTestSuiteNode:
179
+ name = element.get('name', 'default')
180
+ node = QTestSuiteNode(name)
181
+ node.browser = self._parse_bool(element.get('browser'))
182
+ node.parallel = self._parse_bool(element.get('parallel'))
183
+ node.workers = self._parse_int(element.get('workers'), 1)
184
+ node.browsers = element.get('browsers')
185
+ node.device = element.get('device')
186
+
187
+ for child in element:
188
+ parsed = self._parse_child(child)
189
+ if parsed:
190
+ node.add_child(parsed)
191
+ return node
192
+
193
+ def _parse_testing_case(self, element: ET.Element) -> QTestCaseNode:
194
+ name = element.get('name', '')
195
+ node = QTestCaseNode(name)
196
+ node.browser = self._parse_bool(element.get('browser'))
197
+ node.auth = element.get('auth')
198
+ node.device = element.get('device')
199
+ node.viewports = element.get('viewports')
200
+
201
+ for child in element:
202
+ parsed = self._parse_child(child)
203
+ if parsed:
204
+ node.add_child(parsed)
205
+ return node
206
+
207
+ def _parse_testing_expect(self, element: ET.Element) -> ExpectNode:
208
+ node = ExpectNode()
209
+ node.selector = element.get('selector')
210
+ node.visible = self._parse_bool(element.get('visible')) if element.get('visible') else None
211
+ node.hidden = self._parse_bool(element.get('hidden')) if element.get('hidden') else None
212
+ node.text = element.get('text')
213
+ node.text_contains = element.get('text-contains')
214
+ node.equals = element.get('equals')
215
+ node.count = self._parse_int(element.get('count')) if element.get('count') else None
216
+ node.url = element.get('url')
217
+ node.url_contains = element.get('url-contains')
218
+ node.var = element.get('var')
219
+ node.value = element.get('value')
220
+ node.enabled = self._parse_bool(element.get('enabled')) if element.get('enabled') else None
221
+ node.disabled = self._parse_bool(element.get('disabled')) if element.get('disabled') else None
222
+ node.checked = self._parse_bool(element.get('checked')) if element.get('checked') else None
223
+ node.has_class = element.get('has-class')
224
+ node.has_attribute = element.get('has-attribute')
225
+ node.not_ = self._parse_bool(element.get('not'))
226
+ return node
227
+
228
+ def _parse_testing_mock(self, element: ET.Element) -> MockNode_Testing:
229
+ node = MockNode_Testing()
230
+ node.query = element.get('query')
231
+ node.service = element.get('service')
232
+ node.auto_generate = self._parse_bool(element.get('auto-generate'))
233
+ node.when = element.get('when')
234
+ node.return_value = (element.text or '').strip() or None
235
+
236
+ for child in element:
237
+ parsed = self._parse_child(child)
238
+ if parsed:
239
+ node.add_child(parsed)
240
+ return node
241
+
242
+ def _parse_testing_fixture(self, element: ET.Element) -> FixtureNode_Testing:
243
+ name = element.get('name', '')
244
+ node = FixtureNode_Testing(name)
245
+ node.file = element.get('file')
246
+ node.scope = element.get('scope', 'function')
247
+ node.data = (element.text or '').strip() or None
248
+ return node
249
+
250
+ def _parse_testing_setup(self, element: ET.Element) -> SetupNode_Testing:
251
+ node = SetupNode_Testing()
252
+ for child in element:
253
+ parsed = self._parse_child(child)
254
+ if parsed:
255
+ node.add_child(parsed)
256
+ return node
257
+
258
+ def _parse_testing_teardown(self, element: ET.Element) -> TeardownNode_Testing:
259
+ node = TeardownNode_Testing()
260
+ for child in element:
261
+ parsed = self._parse_child(child)
262
+ if parsed:
263
+ node.add_child(parsed)
264
+ return node
265
+
266
+ def _parse_testing_before_each(self, element: ET.Element) -> BeforeEachNode:
267
+ node = BeforeEachNode()
268
+ for child in element:
269
+ parsed = self._parse_child(child)
270
+ if parsed:
271
+ node.add_child(parsed)
272
+ return node
273
+
274
+ def _parse_testing_after_each(self, element: ET.Element) -> AfterEachNode:
275
+ node = AfterEachNode()
276
+ for child in element:
277
+ parsed = self._parse_child(child)
278
+ if parsed:
279
+ node.add_child(parsed)
280
+ return node
281
+
282
+ def _parse_testing_generate(self, element: ET.Element) -> GenerateNode:
283
+ node = GenerateNode()
284
+ node.component = element.get('component')
285
+ node.coverage = element.get('coverage')
286
+ node.include = element.get('include')
287
+ return node
288
+
289
+ def _parse_testing_scenario(self, element: ET.Element) -> ScenarioNode:
290
+ name = element.get('name', '')
291
+ node = ScenarioNode(name)
292
+ for child in element:
293
+ parsed = self._parse_child(child)
294
+ if parsed:
295
+ node.add_child(parsed)
296
+ return node
297
+
298
+ def _parse_testing_given(self, element: ET.Element) -> GivenNode:
299
+ text = (element.text or '').strip()
300
+ node = GivenNode(text)
301
+ for child in element:
302
+ parsed = self._parse_child(child)
303
+ if parsed:
304
+ node.add_child(parsed)
305
+ return node
306
+
307
+ def _parse_testing_when(self, element: ET.Element) -> WhenNode:
308
+ text = (element.text or '').strip()
309
+ node = WhenNode(text)
310
+ for child in element:
311
+ parsed = self._parse_child(child)
312
+ if parsed:
313
+ node.add_child(parsed)
314
+ return node
315
+
316
+ def _parse_testing_then(self, element: ET.Element) -> ThenNode:
317
+ text = (element.text or '').strip()
318
+ node = ThenNode(text)
319
+ for child in element:
320
+ parsed = self._parse_child(child)
321
+ if parsed:
322
+ node.add_child(parsed)
323
+ return node
324
+
325
+ # ------------------------------------------------------------------
326
+ # Browser testing nodes
327
+ # ------------------------------------------------------------------
328
+
329
+ def _parse_testing_browser_config(self, element: ET.Element) -> BrowserConfigNode:
330
+ node = BrowserConfigNode()
331
+ node.engine = element.get('engine', 'chromium')
332
+ node.headless = self._parse_bool(element.get('headless'), True)
333
+ node.viewport_width = self._parse_int(element.get('viewport-width'), 1280)
334
+ node.viewport_height = self._parse_int(element.get('viewport-height'), 720)
335
+ node.locale = element.get('locale')
336
+ node.timezone = element.get('timezone')
337
+ node.base_url = element.get('base-url')
338
+ node.timeout = self._parse_int(element.get('timeout'), 30000)
339
+ node.video = self._parse_bool(element.get('video'))
340
+ node.trace = self._parse_bool(element.get('trace'))
341
+ node.screenshot = element.get('screenshot')
342
+ return node
343
+
344
+ def _parse_testing_navigate(self, element: ET.Element) -> NavigateNode:
345
+ node = NavigateNode()
346
+ node.to = element.get('to')
347
+ node.back = self._parse_bool(element.get('back'))
348
+ node.forward = self._parse_bool(element.get('forward'))
349
+ node.wait = element.get('wait')
350
+ return node
351
+
352
+ def _parse_testing_click(self, element: ET.Element) -> ClickNode:
353
+ node = ClickNode()
354
+ node.selector = element.get('selector')
355
+ node.text = element.get('text')
356
+ node.role = element.get('role')
357
+ node.test_id = element.get('test-id')
358
+ node.xpath = element.get('xpath')
359
+ return node
360
+
361
+ def _parse_testing_dblclick(self, element: ET.Element) -> DblClickNode:
362
+ node = DblClickNode()
363
+ node.selector = element.get('selector')
364
+ node.text = element.get('text')
365
+ node.role = element.get('role')
366
+ node.test_id = element.get('test-id')
367
+ return node
368
+
369
+ def _parse_testing_right_click(self, element: ET.Element) -> RightClickNode:
370
+ node = RightClickNode()
371
+ node.selector = element.get('selector')
372
+ node.text = element.get('text')
373
+ node.role = element.get('role')
374
+ node.test_id = element.get('test-id')
375
+ return node
376
+
377
+ def _parse_testing_hover(self, element: ET.Element) -> HoverNode:
378
+ node = HoverNode()
379
+ node.selector = element.get('selector')
380
+ node.text = element.get('text')
381
+ node.role = element.get('role')
382
+ node.test_id = element.get('test-id')
383
+ return node
384
+
385
+ def _parse_testing_fill(self, element: ET.Element) -> FillNode:
386
+ node = FillNode()
387
+ node.selector = element.get('selector')
388
+ node.value = element.get('value', '')
389
+ node.clear = self._parse_bool(element.get('clear'), True)
390
+ return node
391
+
392
+ def _parse_testing_type(self, element: ET.Element) -> TypeNode_Testing:
393
+ node = TypeNode_Testing()
394
+ node.selector = element.get('selector')
395
+ node.text = element.get('text', '')
396
+ node.delay = self._parse_int(element.get('delay'))
397
+ return node
398
+
399
+ def _parse_testing_select(self, element: ET.Element) -> SelectNode_Testing:
400
+ node = SelectNode_Testing()
401
+ node.selector = element.get('selector')
402
+ node.value = element.get('value')
403
+ node.values = element.get('values')
404
+ return node
405
+
406
+ def _parse_testing_check(self, element: ET.Element) -> CheckNode:
407
+ node = CheckNode()
408
+ node.selector = element.get('selector')
409
+ return node
410
+
411
+ def _parse_testing_uncheck(self, element: ET.Element) -> UncheckNode:
412
+ node = UncheckNode()
413
+ node.selector = element.get('selector')
414
+ return node
415
+
416
+ def _parse_testing_keyboard(self, element: ET.Element) -> KeyboardNode:
417
+ node = KeyboardNode()
418
+ node.press = element.get('press')
419
+ node.type_text = element.get('type')
420
+ node.delay = self._parse_int(element.get('delay'))
421
+ return node
422
+
423
+ def _parse_testing_drag(self, element: ET.Element) -> DragNode:
424
+ node = DragNode()
425
+ node.from_sel = element.get('from')
426
+ node.to_sel = element.get('to')
427
+ return node
428
+
429
+ def _parse_testing_scroll(self, element: ET.Element) -> ScrollNode_Testing:
430
+ node = ScrollNode_Testing()
431
+ node.selector = element.get('selector')
432
+ node.direction = element.get('direction', 'down')
433
+ node.amount = self._parse_int(element.get('amount')) if element.get('amount') else None
434
+ node.to_element = element.get('to-element')
435
+ node.to_top = self._parse_bool(element.get('to-top'))
436
+ return node
437
+
438
+ def _parse_testing_upload(self, element: ET.Element) -> UploadNode:
439
+ node = UploadNode()
440
+ node.selector = element.get('selector')
441
+ node.file = element.get('file')
442
+ node.files = element.get('files')
443
+ return node
444
+
445
+ def _parse_testing_download(self, element: ET.Element) -> DownloadNode:
446
+ node = DownloadNode()
447
+ node.trigger_click = element.get('trigger-click')
448
+ node.save_as = element.get('save-as')
449
+ return node
450
+
451
+ def _parse_testing_wait_for(self, element: ET.Element) -> WaitForNode:
452
+ node = WaitForNode()
453
+ node.selector = element.get('selector')
454
+ node.state = element.get('state')
455
+ node.url = element.get('url')
456
+ node.request = element.get('request')
457
+ node.response = element.get('response')
458
+ node.condition = element.get('condition')
459
+ node.timeout = self._parse_int(element.get('timeout')) if element.get('timeout') else None
460
+ node.has_text = element.get('has-text')
461
+ return node
462
+
463
+ def _parse_testing_screenshot(self, element: ET.Element) -> ScreenshotNode:
464
+ node = ScreenshotNode()
465
+ node.name = element.get('name')
466
+ node.selector = element.get('selector')
467
+ node.full_page = self._parse_bool(element.get('full-page'))
468
+ return node
469
+
470
+ def _parse_testing_intercept(self, element: ET.Element) -> InterceptNode:
471
+ node = InterceptNode()
472
+ node.method = element.get('method')
473
+ node.url = element.get('url')
474
+ node.delay = self._parse_int(element.get('delay')) if element.get('delay') else None
475
+ node.passthrough = self._parse_bool(element.get('passthrough'))
476
+ node.spy = self._parse_bool(element.get('spy'))
477
+
478
+ for child in element:
479
+ parsed = self._parse_child(child)
480
+ if parsed:
481
+ node.add_child(parsed)
482
+ return node
483
+
484
+ def _parse_testing_respond(self, element: ET.Element) -> InterceptRespondNode:
485
+ node = InterceptRespondNode()
486
+ node.status = self._parse_int(element.get('status'), 200)
487
+ node.content_type = element.get('content-type', 'application/json')
488
+ node.file = element.get('file')
489
+ node.body = (element.text or '').strip() or None
490
+ return node
491
+
492
+ def _parse_testing_frame(self, element: ET.Element) -> FrameNode_Testing:
493
+ node = FrameNode_Testing()
494
+ node.selector = element.get('selector')
495
+ for child in element:
496
+ parsed = self._parse_child(child)
497
+ if parsed:
498
+ node.add_child(parsed)
499
+ return node
500
+
501
+ def _parse_testing_popup(self, element: ET.Element) -> PopupNode:
502
+ node = PopupNode()
503
+ for child in element:
504
+ parsed = self._parse_child(child)
505
+ if parsed:
506
+ node.add_child(parsed)
507
+ return node
508
+
509
+ def _parse_testing_auth(self, element: ET.Element) -> AuthNode:
510
+ node = AuthNode()
511
+ node.name = element.get('name')
512
+ node.reuse = self._parse_bool(element.get('reuse'), True)
513
+ for child in element:
514
+ parsed = self._parse_child(child)
515
+ if parsed:
516
+ node.add_child(parsed)
517
+ return node
518
+
519
+ def _parse_testing_save_state(self, element: ET.Element) -> SaveStateNode:
520
+ node = SaveStateNode()
521
+ node.file = element.get('file')
522
+ return node
523
+
524
+ def _parse_testing_pause(self, element: ET.Element) -> PauseNode:
525
+ return PauseNode()
526
+
527
+ # ------------------------------------------------------------------
528
+ # Advanced testing nodes
529
+ # ------------------------------------------------------------------
530
+
531
+ def _parse_testing_snapshot(self, element: ET.Element) -> SnapshotNode:
532
+ node = SnapshotNode()
533
+ node.name = element.get('name')
534
+ node.threshold = self._parse_float(element.get('threshold'), 0.1)
535
+ node.ai_ignore = element.get('ai-ignore')
536
+ node.viewports = element.get('viewports')
537
+ node.mask_selectors = element.get('mask-selectors')
538
+ return node
539
+
540
+ def _parse_testing_fuzz(self, element: ET.Element) -> FuzzNode:
541
+ node = FuzzNode()
542
+ node.target = element.get('target')
543
+ node.iterations = self._parse_int(element.get('iterations'), 100)
544
+ node.generators = element.get('generators')
545
+ for child in element:
546
+ parsed = self._parse_child(child)
547
+ if parsed:
548
+ node.add_child(parsed)
549
+ return node
550
+
551
+ def _parse_testing_property(self, element: ET.Element) -> PropertyNode:
552
+ text = (element.text or '').strip()
553
+ return PropertyNode(text)
554
+
555
+ def _parse_testing_field(self, element: ET.Element) -> FuzzFieldNode:
556
+ node = FuzzFieldNode()
557
+ node.name = element.get('name')
558
+ node.generator = element.get('generator')
559
+ return node
560
+
561
+ def _parse_testing_perf(self, element: ET.Element) -> PerfNode:
562
+ node = PerfNode()
563
+ node.baseline = element.get('baseline')
564
+ node.regression_threshold = self._parse_float(element.get('regression-threshold'), 0.1)
565
+ for child in element:
566
+ parsed = self._parse_child(child)
567
+ if parsed:
568
+ node.add_child(parsed)
569
+ return node
570
+
571
+ def _parse_testing_metric(self, element: ET.Element) -> PerfMetricNode:
572
+ node = PerfMetricNode()
573
+ node.name = element.get('name')
574
+ node.max_value = self._parse_float(element.get('max-value')) if element.get('max-value') else None
575
+ return node
576
+
577
+ def _parse_testing_a11y(self, element: ET.Element) -> A11yNode:
578
+ node = A11yNode()
579
+ node.standard = element.get('standard', 'wcag2aa')
580
+ node.check = element.get('check')
581
+ node.ignore = element.get('ignore')
582
+ return node
583
+
584
+ def _parse_testing_chaos(self, element: ET.Element) -> ChaosNode:
585
+ node = ChaosNode()
586
+ for child in element:
587
+ parsed = self._parse_child(child)
588
+ if parsed:
589
+ node.add_child(parsed)
590
+ return node
591
+
592
+ def _parse_testing_fail(self, element: ET.Element) -> ChaosFailNode:
593
+ node = ChaosFailNode()
594
+ node.service = element.get('service')
595
+ node.probability = self._parse_float(element.get('probability'), 1.0)
596
+ node.delay = self._parse_int(element.get('delay')) if element.get('delay') else None
597
+ return node
598
+
599
+ def _parse_testing_geolocation(self, element: ET.Element) -> GeolocationNode:
600
+ node = GeolocationNode()
601
+ node.latitude = self._parse_float(element.get('latitude'))
602
+ node.longitude = self._parse_float(element.get('longitude'))
603
+ node.accuracy = self._parse_float(element.get('accuracy'), 100.0)
604
+ return node
@@ -0,0 +1,48 @@
1
+ """
2
+ Theming System for Quantum UI Engine
3
+
4
+ Provides dark/light mode support, custom color themes, and runtime theme switching.
5
+ Integrates with the design tokens system for consistent styling.
6
+
7
+ Usage:
8
+ # In .q files:
9
+ <q:application id="MyApp" type="ui" theme="dark">
10
+ <ui:window>...</ui:window>
11
+ </q:application>
12
+
13
+ # Or with ui:theme tag:
14
+ <q:application id="MyApp" type="ui">
15
+ <ui:theme preset="dark" />
16
+ <ui:window>...</ui:window>
17
+ </q:application>
18
+
19
+ # Custom theme:
20
+ <q:application id="MyApp" type="ui">
21
+ <ui:theme name="custom">
22
+ <ui:color name="primary" value="#ff6b6b" />
23
+ <ui:color name="background" value="#2d3436" />
24
+ </ui:theme>
25
+ <ui:window>...</ui:window>
26
+ </q:application>
27
+ """
28
+
29
+ from .ast_node import UIThemeNode, UIColorNode
30
+ from .presets import (
31
+ THEME_LIGHT,
32
+ THEME_DARK,
33
+ THEME_PRESETS,
34
+ get_theme_css,
35
+ get_theme_preset,
36
+ get_theme_switch_js,
37
+ )
38
+
39
+ __all__ = [
40
+ 'UIThemeNode',
41
+ 'UIColorNode',
42
+ 'THEME_LIGHT',
43
+ 'THEME_DARK',
44
+ 'THEME_PRESETS',
45
+ 'get_theme_css',
46
+ 'get_theme_preset',
47
+ 'get_theme_switch_js',
48
+ ]