hardpy 0.3.0__py3-none-any.whl → 0.5.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.
- hardpy/__init__.py +30 -2
- hardpy/hardpy_panel/api.py +15 -0
- hardpy/hardpy_panel/frontend/dist/asset-manifest.json +3 -3
- hardpy/hardpy_panel/frontend/dist/index.html +1 -1
- hardpy/hardpy_panel/frontend/dist/static/js/main.da686f40.js +3 -0
- hardpy/hardpy_panel/frontend/dist/static/js/main.da686f40.js.map +1 -0
- hardpy/hardpy_panel/runner.py +2 -0
- hardpy/pytest_hardpy/db/const.py +1 -0
- hardpy/pytest_hardpy/db/schema.py +23 -2
- hardpy/pytest_hardpy/plugin.py +50 -11
- hardpy/pytest_hardpy/pytest_call.py +84 -3
- hardpy/pytest_hardpy/pytest_wrapper.py +29 -0
- hardpy/pytest_hardpy/reporter/hook_reporter.py +12 -5
- hardpy/pytest_hardpy/utils/__init__.py +26 -1
- hardpy/pytest_hardpy/utils/config_data.py +5 -1
- hardpy/pytest_hardpy/utils/dialog_box.py +303 -0
- hardpy/pytest_hardpy/utils/exception.py +14 -0
- {hardpy-0.3.0.dist-info → hardpy-0.5.0.dist-info}/METADATA +8 -5
- {hardpy-0.3.0.dist-info → hardpy-0.5.0.dist-info}/RECORD +23 -22
- hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js +0 -3
- hardpy/hardpy_panel/frontend/dist/static/js/main.8ef63e9b.js.map +0 -1
- /hardpy/hardpy_panel/frontend/dist/static/js/{main.8ef63e9b.js.LICENSE.txt → main.da686f40.js.LICENSE.txt} +0 -0
- {hardpy-0.3.0.dist-info → hardpy-0.5.0.dist-info}/WHEEL +0 -0
- {hardpy-0.3.0.dist-info → hardpy-0.5.0.dist-info}/entry_points.txt +0 -0
- {hardpy-0.3.0.dist-info → hardpy-0.5.0.dist-info}/licenses/LICENSE +0 -0
hardpy/hardpy_panel/runner.py
CHANGED
|
@@ -21,6 +21,8 @@ def run():
|
|
|
21
21
|
parser.add_argument("-dbh", "--db_host", type=str, default=config.db_host, help="database hostname") # noqa: E501
|
|
22
22
|
parser.add_argument("-wh", "--web_host", type=str, default=config.web_host, help="web operator panel hostname") # noqa: E501
|
|
23
23
|
parser.add_argument("-wp", "--web_port", type=str, default=config.web_port, help="web operator panel port") # noqa: E501
|
|
24
|
+
parser.add_argument("-sp", "--sck_port", type=int, default=config.socket_port, help="internal socket port") # noqa: E501
|
|
25
|
+
parser.add_argument("-sa", "--sck_addr", type=int, default=config.socket_port, help="internal socket address") # noqa: E501
|
|
24
26
|
parser.add_argument("path", type=str, nargs='?', help="path to test directory")
|
|
25
27
|
# fmt: on
|
|
26
28
|
|
hardpy/pytest_hardpy/db/const.py
CHANGED
|
@@ -29,12 +29,23 @@ class CaseStateStore(IBaseResult):
|
|
|
29
29
|
"start_time": 1695817188,
|
|
30
30
|
"stop_time": 1695817189,
|
|
31
31
|
"assertion_msg": null,
|
|
32
|
-
"msg": null
|
|
32
|
+
"msg": null,
|
|
33
|
+
"dialog_box": {
|
|
34
|
+
"title_bar": "Example of text input",
|
|
35
|
+
"dialog_text": "Type some text and press the Confirm button",
|
|
36
|
+
"widget": {
|
|
37
|
+
"info": {
|
|
38
|
+
"text": "some text"
|
|
39
|
+
},
|
|
40
|
+
"type": "textinput"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
"""
|
|
35
45
|
|
|
36
46
|
assertion_msg: str | None = None
|
|
37
47
|
msg: dict | None = None
|
|
48
|
+
dialog_box: dict = {}
|
|
38
49
|
|
|
39
50
|
|
|
40
51
|
class CaseRunStore(IBaseResult):
|
|
@@ -175,7 +186,17 @@ class ResultStateStore(IBaseResult):
|
|
|
175
186
|
"start_time": 1695817263,
|
|
176
187
|
"stop_time": 1695817264,
|
|
177
188
|
"assertion_msg": null,
|
|
178
|
-
"msg": null
|
|
189
|
+
"msg": null,
|
|
190
|
+
"dialog_box": {
|
|
191
|
+
"title_bar": "Example of text input",
|
|
192
|
+
"dialog_text": "Type some text and press the Confirm button",
|
|
193
|
+
"widget": {
|
|
194
|
+
"info": {
|
|
195
|
+
"text": "some text"
|
|
196
|
+
},
|
|
197
|
+
"type": "textinput"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
179
200
|
},
|
|
180
201
|
"test_minute_parity": {
|
|
181
202
|
"status": "failed",
|
hardpy/pytest_hardpy/plugin.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing import Any, Callable
|
|
|
6
6
|
from logging import getLogger
|
|
7
7
|
from pathlib import Path, PurePath
|
|
8
8
|
from platform import system
|
|
9
|
+
from re import compile as re_compile
|
|
9
10
|
|
|
10
11
|
from natsort import natsorted
|
|
11
12
|
from pytest import (
|
|
@@ -19,6 +20,13 @@ from pytest import (
|
|
|
19
20
|
fixture,
|
|
20
21
|
ExitCode,
|
|
21
22
|
)
|
|
23
|
+
from _pytest._code.code import (
|
|
24
|
+
ExceptionRepr,
|
|
25
|
+
ReprFileLocation,
|
|
26
|
+
ExceptionInfo,
|
|
27
|
+
ReprExceptionInfo,
|
|
28
|
+
TerminalRepr,
|
|
29
|
+
)
|
|
22
30
|
|
|
23
31
|
from hardpy.pytest_hardpy.reporter import HookReporter
|
|
24
32
|
from hardpy.pytest_hardpy.utils import (
|
|
@@ -40,6 +48,8 @@ def pytest_addoption(parser: Parser):
|
|
|
40
48
|
parser.addoption("--hardpy-dbp", action="store", default=config_data.db_port, help="database port number") # noqa: E501
|
|
41
49
|
parser.addoption("--hardpy-dbh", action="store", default=config_data.db_host, help="database hostname") # noqa: E501
|
|
42
50
|
parser.addoption("--hardpy-pt", action="store_true", default=False, help="enable pytest-hardpy plugin") # noqa: E501
|
|
51
|
+
parser.addoption("--hardpy-sp", action="store", default=config_data.socket_port, help="internal socket port") # noqa: E501
|
|
52
|
+
parser.addoption("--hardpy-sa", action="store", default=config_data.socket_addr, help="internal socket address") # noqa: E501
|
|
43
53
|
# fmt: on
|
|
44
54
|
|
|
45
55
|
|
|
@@ -77,6 +87,8 @@ class HardpyPlugin(object):
|
|
|
77
87
|
config_data.db_host = config.getoption("--hardpy-dbh")
|
|
78
88
|
config_data.db_pswd = config.getoption("--hardpy-dbpw")
|
|
79
89
|
config_data.db_port = config.getoption("--hardpy-dbp")
|
|
90
|
+
config_data.socket_port = int(config.getoption("--hardpy-sp"))
|
|
91
|
+
config_data.socket_addr = config.getoption("--hardpy-sa")
|
|
80
92
|
|
|
81
93
|
config.addinivalue_line("markers", "case_name")
|
|
82
94
|
config.addinivalue_line("markers", "module_name")
|
|
@@ -183,8 +195,9 @@ class HardpyPlugin(object):
|
|
|
183
195
|
|
|
184
196
|
def pytest_runtest_logreport(self, report: TestReport):
|
|
185
197
|
"""Call after call of each test item."""
|
|
186
|
-
if report.when != "call":
|
|
198
|
+
if report.when != "call" and report.failed is False:
|
|
187
199
|
# ignore setup and teardown phase
|
|
200
|
+
# or continue processing setup and teardown failure (fixture exception handler)
|
|
188
201
|
return True
|
|
189
202
|
|
|
190
203
|
module_id = Path(report.fspath).stem
|
|
@@ -200,7 +213,7 @@ class HardpyPlugin(object):
|
|
|
200
213
|
case_id,
|
|
201
214
|
)
|
|
202
215
|
|
|
203
|
-
assertion_msg = self._decode_assertion_msg(report.
|
|
216
|
+
assertion_msg = self._decode_assertion_msg(report.longrepr)
|
|
204
217
|
self._reporter.set_assertion_msg(module_id, case_id, assertion_msg)
|
|
205
218
|
self._reporter.set_progress(self._progress.calculate(report.nodeid))
|
|
206
219
|
self._results[module_id][case_id] = report.outcome # noqa: WPS204
|
|
@@ -261,15 +274,41 @@ class HardpyPlugin(object):
|
|
|
261
274
|
case _:
|
|
262
275
|
return RunStatus.ERROR
|
|
263
276
|
|
|
264
|
-
def _decode_assertion_msg(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
277
|
+
def _decode_assertion_msg(
|
|
278
|
+
self,
|
|
279
|
+
error: ( # noqa: WPS320
|
|
280
|
+
ExceptionInfo[BaseException] # noqa: DAR101,DAR201
|
|
281
|
+
| tuple[str, int, str]
|
|
282
|
+
| str
|
|
283
|
+
| TerminalRepr
|
|
284
|
+
| None
|
|
285
|
+
),
|
|
286
|
+
) -> str | None:
|
|
287
|
+
"""Parse pytest assertion error message."""
|
|
288
|
+
if error is None:
|
|
289
|
+
return None
|
|
290
|
+
|
|
291
|
+
match error:
|
|
292
|
+
case str():
|
|
293
|
+
return error
|
|
294
|
+
case tuple() if len(error) == 3:
|
|
295
|
+
return error[2]
|
|
296
|
+
case ExceptionInfo():
|
|
297
|
+
error_repr = error.getrepr()
|
|
298
|
+
if isinstance(error_repr, ReprExceptionInfo) and error_repr.reprcrash:
|
|
299
|
+
return error_repr.reprcrash.message
|
|
300
|
+
case TerminalRepr():
|
|
301
|
+
if isinstance(error, ExceptionRepr) and isinstance( # noqa: WPS337
|
|
302
|
+
error.reprcrash, ReprFileLocation
|
|
303
|
+
):
|
|
304
|
+
# remove ansi codes
|
|
305
|
+
ansi_pattern = re_compile(
|
|
306
|
+
r"(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])" # noqa: E501
|
|
307
|
+
)
|
|
308
|
+
return ansi_pattern.sub("", error.reprcrash.message)
|
|
309
|
+
return str(error)
|
|
310
|
+
case _:
|
|
311
|
+
return None
|
|
273
312
|
|
|
274
313
|
def _handle_dependency(self, node_info: NodeInfo):
|
|
275
314
|
dependency = self._dependencies.get(
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
+
import socket
|
|
4
5
|
from os import environ
|
|
5
6
|
from dataclasses import dataclass
|
|
6
|
-
from typing import Optional
|
|
7
|
+
from typing import Optional, Any
|
|
7
8
|
from uuid import uuid4
|
|
8
9
|
|
|
9
10
|
from pycouchdb.exceptions import NotFound
|
|
@@ -14,7 +15,12 @@ from hardpy.pytest_hardpy.db import (
|
|
|
14
15
|
ResultRunStore,
|
|
15
16
|
RunStore,
|
|
16
17
|
)
|
|
17
|
-
from hardpy.pytest_hardpy.utils import
|
|
18
|
+
from hardpy.pytest_hardpy.utils import (
|
|
19
|
+
DuplicateSerialNumberError,
|
|
20
|
+
DuplicateDialogBoxError,
|
|
21
|
+
DialogBox,
|
|
22
|
+
ConfigData,
|
|
23
|
+
)
|
|
18
24
|
from hardpy.pytest_hardpy.reporter import RunnerReporter
|
|
19
25
|
|
|
20
26
|
|
|
@@ -184,7 +190,7 @@ def set_run_artifact(data: dict):
|
|
|
184
190
|
|
|
185
191
|
|
|
186
192
|
def set_driver_info(drivers: dict) -> None:
|
|
187
|
-
"""
|
|
193
|
+
"""Add or update drivers data.
|
|
188
194
|
|
|
189
195
|
Driver data is stored in both StateStore and RunStore databases.
|
|
190
196
|
|
|
@@ -203,6 +209,58 @@ def set_driver_info(drivers: dict) -> None:
|
|
|
203
209
|
reporter.update_db_by_doc()
|
|
204
210
|
|
|
205
211
|
|
|
212
|
+
def run_dialog_box(dialog_box_data: DialogBox) -> Any:
|
|
213
|
+
"""Display a dialog box.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
dialog_box_data (DialogBox): Data for creating the dialog box.
|
|
217
|
+
|
|
218
|
+
DialogBox attributes:
|
|
219
|
+
|
|
220
|
+
- dialog_text (str): The text of the dialog box.
|
|
221
|
+
- title_bar (str | None): The title bar of the dialog box.
|
|
222
|
+
If the title_bar field is missing, it is the case name.
|
|
223
|
+
- widget (DialogBoxWidget | None): Widget information.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Any: An object containing the user's response.
|
|
227
|
+
|
|
228
|
+
The type of the return value depends on the widget type:
|
|
229
|
+
|
|
230
|
+
- BASE: bool.
|
|
231
|
+
- TEXT_INPUT: str.
|
|
232
|
+
- NUMERIC_INPUT: float.
|
|
233
|
+
- RADIOBUTTON: str.
|
|
234
|
+
- CHECKBOX: list[str].
|
|
235
|
+
- IMAGE: bool.
|
|
236
|
+
- MULTISTEP: bool.
|
|
237
|
+
|
|
238
|
+
Raises:
|
|
239
|
+
ValueError: If the 'message' argument is empty.
|
|
240
|
+
DuplicateDialogBoxError: If the dialog box is already caused.
|
|
241
|
+
"""
|
|
242
|
+
if not dialog_box_data.dialog_text:
|
|
243
|
+
raise ValueError("The 'dialog_text' argument cannot be empty.")
|
|
244
|
+
|
|
245
|
+
current_test = _get_current_test()
|
|
246
|
+
reporter = RunnerReporter()
|
|
247
|
+
key = reporter.generate_key(
|
|
248
|
+
DF.MODULES,
|
|
249
|
+
current_test.module_id,
|
|
250
|
+
DF.CASES,
|
|
251
|
+
current_test.case_id,
|
|
252
|
+
DF.DIALOG_BOX,
|
|
253
|
+
)
|
|
254
|
+
if reporter.get_field(key):
|
|
255
|
+
raise DuplicateDialogBoxError
|
|
256
|
+
|
|
257
|
+
reporter.set_doc_value(key, dialog_box_data.to_dict(), statestore_only=True)
|
|
258
|
+
reporter.update_db_by_doc()
|
|
259
|
+
|
|
260
|
+
input_dbx_data = _get_socket_raw_data()
|
|
261
|
+
return dialog_box_data.widget.convert_data(input_dbx_data)
|
|
262
|
+
|
|
263
|
+
|
|
206
264
|
def _get_current_test() -> CurrentTestInfo:
|
|
207
265
|
current_node = environ.get("PYTEST_CURRENT_TEST")
|
|
208
266
|
|
|
@@ -224,3 +282,26 @@ def _get_current_test() -> CurrentTestInfo:
|
|
|
224
282
|
case_id = case_with_stage[:case_id_end_index]
|
|
225
283
|
|
|
226
284
|
return CurrentTestInfo(module_id=module_id, case_id=case_id)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _get_socket_raw_data() -> str:
|
|
288
|
+
# create socket connection
|
|
289
|
+
server = socket.socket()
|
|
290
|
+
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
291
|
+
config_data = ConfigData()
|
|
292
|
+
try:
|
|
293
|
+
server.bind((config_data.socket_addr, config_data.socket_port))
|
|
294
|
+
except socket.error as exc:
|
|
295
|
+
raise RuntimeError(f"Error creating socket: {exc}")
|
|
296
|
+
server.listen(1)
|
|
297
|
+
client, _ = server.accept()
|
|
298
|
+
|
|
299
|
+
# receive data
|
|
300
|
+
max_input_data_len = 1024
|
|
301
|
+
socket_data = client.recv(max_input_data_len).decode("utf-8")
|
|
302
|
+
|
|
303
|
+
# close connection
|
|
304
|
+
client.close()
|
|
305
|
+
server.close()
|
|
306
|
+
|
|
307
|
+
return socket_data
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import sys
|
|
5
5
|
import signal
|
|
6
6
|
import subprocess
|
|
7
|
+
from socket import socket
|
|
7
8
|
from platform import system
|
|
8
9
|
|
|
9
10
|
from hardpy.pytest_hardpy.utils.config_data import ConfigData
|
|
@@ -46,6 +47,10 @@ class PyTestWrapper(object):
|
|
|
46
47
|
self.config.db_user,
|
|
47
48
|
"--hardpy-dbpw",
|
|
48
49
|
self.config.db_pswd,
|
|
50
|
+
"--hardpy-sp",
|
|
51
|
+
str(self.config.socket_port),
|
|
52
|
+
"--hardpy-sa",
|
|
53
|
+
self.config.socket_addr,
|
|
49
54
|
"--hardpy-pt",
|
|
50
55
|
],
|
|
51
56
|
cwd=self.config.tests_dir.absolute(),
|
|
@@ -64,6 +69,10 @@ class PyTestWrapper(object):
|
|
|
64
69
|
self.config.db_user,
|
|
65
70
|
"--hardpy-dbpw",
|
|
66
71
|
self.config.db_pswd,
|
|
72
|
+
"--hardpy-sp",
|
|
73
|
+
str(self.config.socket_port),
|
|
74
|
+
"--hardpy-sa",
|
|
75
|
+
self.config.socket_addr,
|
|
67
76
|
"--hardpy-pt",
|
|
68
77
|
],
|
|
69
78
|
cwd=self.config.tests_dir.absolute(),
|
|
@@ -115,6 +124,26 @@ class PyTestWrapper(object):
|
|
|
115
124
|
)
|
|
116
125
|
return True
|
|
117
126
|
|
|
127
|
+
def confirm_dialog_box(self, dialog_box_output: str):
|
|
128
|
+
"""Set dialog box data to pytest subprocess.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
dialog_box_output (str): dialog box output data
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
bool: True if dialog box was confirmed, else False
|
|
135
|
+
"""
|
|
136
|
+
config_data = ConfigData()
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
client = socket()
|
|
140
|
+
client.connect((config_data.socket_addr, config_data.socket_port))
|
|
141
|
+
client.sendall(dialog_box_output.encode("utf-8"))
|
|
142
|
+
client.close()
|
|
143
|
+
except Exception:
|
|
144
|
+
return False
|
|
145
|
+
return True
|
|
146
|
+
|
|
118
147
|
def is_running(self) -> bool | None:
|
|
119
148
|
"""Check if pytest is running."""
|
|
120
149
|
return self._proc and self._proc.poll() is None
|
|
@@ -90,8 +90,8 @@ class HookReporter(BaseReporter):
|
|
|
90
90
|
item_statestore = self._statestore.get_field(key)
|
|
91
91
|
item_runstore = self._runstore.get_field(key)
|
|
92
92
|
|
|
93
|
-
self._init_case(item_statestore, node_info)
|
|
94
|
-
self._init_case(item_runstore, node_info,
|
|
93
|
+
self._init_case(item_statestore, node_info, is_only_statestore=True)
|
|
94
|
+
self._init_case(item_runstore, node_info, is_only_runstore=True)
|
|
95
95
|
|
|
96
96
|
self.set_doc_value(key, item_statestore, statestore_only=True)
|
|
97
97
|
self.set_doc_value(key, item_runstore, runstore_only=True)
|
|
@@ -176,7 +176,11 @@ class HookReporter(BaseReporter):
|
|
|
176
176
|
self.set_doc_value(key, int(time()))
|
|
177
177
|
|
|
178
178
|
def _init_case(
|
|
179
|
-
self,
|
|
179
|
+
self,
|
|
180
|
+
item: dict,
|
|
181
|
+
node_info: NodeInfo,
|
|
182
|
+
is_only_runstore: bool = False,
|
|
183
|
+
is_only_statestore: bool = False,
|
|
180
184
|
):
|
|
181
185
|
module_default = { # noqa: WPS204
|
|
182
186
|
DF.STATUS: TestStatus.READY,
|
|
@@ -195,7 +199,7 @@ class HookReporter(BaseReporter):
|
|
|
195
199
|
}
|
|
196
200
|
|
|
197
201
|
if item.get(node_info.module_id) is None: # noqa: WPS204
|
|
198
|
-
if
|
|
202
|
+
if is_only_runstore:
|
|
199
203
|
module_default[DF.ARTIFACT] = {}
|
|
200
204
|
item[node_info.module_id] = module_default # noqa: WPS204
|
|
201
205
|
else:
|
|
@@ -205,8 +209,11 @@ class HookReporter(BaseReporter):
|
|
|
205
209
|
item[node_info.module_id][DF.STOP_TIME] = None
|
|
206
210
|
item[node_info.module_id][DF.NAME] = self._get_module_name(node_info)
|
|
207
211
|
|
|
208
|
-
if
|
|
212
|
+
if is_only_runstore:
|
|
209
213
|
case_default[DF.ARTIFACT] = {}
|
|
214
|
+
|
|
215
|
+
if is_only_statestore:
|
|
216
|
+
case_default[DF.DIALOG_BOX] = {}
|
|
210
217
|
item[node_info.module_id][DF.CASES][node_info.case_id] = case_default
|
|
211
218
|
|
|
212
219
|
def _remove_outdate_node(
|
|
@@ -6,7 +6,22 @@ from hardpy.pytest_hardpy.utils.progress_calculator import ProgressCalculator
|
|
|
6
6
|
from hardpy.pytest_hardpy.utils.const import TestStatus, RunStatus
|
|
7
7
|
from hardpy.pytest_hardpy.utils.singleton import Singleton
|
|
8
8
|
from hardpy.pytest_hardpy.utils.config_data import ConfigData
|
|
9
|
-
from hardpy.pytest_hardpy.utils.exception import
|
|
9
|
+
from hardpy.pytest_hardpy.utils.exception import (
|
|
10
|
+
DuplicateSerialNumberError,
|
|
11
|
+
DuplicateDialogBoxError,
|
|
12
|
+
WidgetInfoError,
|
|
13
|
+
)
|
|
14
|
+
from hardpy.pytest_hardpy.utils.dialog_box import (
|
|
15
|
+
DialogBox,
|
|
16
|
+
TextInputWidget,
|
|
17
|
+
NumericInputWidget,
|
|
18
|
+
CheckboxWidget,
|
|
19
|
+
RadiobuttonWidget,
|
|
20
|
+
ImageWidget,
|
|
21
|
+
MultistepWidget,
|
|
22
|
+
StepWidget,
|
|
23
|
+
)
|
|
24
|
+
|
|
10
25
|
|
|
11
26
|
__all__ = [
|
|
12
27
|
"NodeInfo",
|
|
@@ -16,4 +31,14 @@ __all__ = [
|
|
|
16
31
|
"Singleton",
|
|
17
32
|
"ConfigData",
|
|
18
33
|
"DuplicateSerialNumberError",
|
|
34
|
+
"DuplicateDialogBoxError",
|
|
35
|
+
"WidgetInfoError",
|
|
36
|
+
"DialogBox",
|
|
37
|
+
"TextInputWidget",
|
|
38
|
+
"NumericInputWidget",
|
|
39
|
+
"CheckboxWidget",
|
|
40
|
+
"RadiobuttonWidget",
|
|
41
|
+
"ImageWidget",
|
|
42
|
+
"MultistepWidget",
|
|
43
|
+
"StepWidget"
|
|
19
44
|
]
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Copyright (c) 2024 Everypin
|
|
2
2
|
# GNU General Public License v3.0 (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from socket import gethostname
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from hardpy.pytest_hardpy.utils.singleton import Singleton
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
class ConfigData(Singleton):
|
|
9
11
|
"""Web connection data storage."""
|
|
@@ -17,6 +19,8 @@ class ConfigData(Singleton):
|
|
|
17
19
|
self.web_host: str = "0.0.0.0"
|
|
18
20
|
self.web_port: int = 8000
|
|
19
21
|
self.tests_dir = Path.cwd()
|
|
22
|
+
self.socket_port: int = 6525
|
|
23
|
+
self.socket_addr: str = gethostname()
|
|
20
24
|
self._initialized = True
|
|
21
25
|
|
|
22
26
|
@property
|