cucu 1.0.4__py3-none-any.whl → 1.0.7__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.
Potentially problematic release.
This version of cucu might be problematic. Click here for more details.
- cucu/cli/core.py +42 -11
- cucu/steps/file_input_steps.py +118 -23
- cucu/steps/webserver_steps.py +5 -1
- {cucu-1.0.4.dist-info → cucu-1.0.7.dist-info}/METADATA +4 -4
- {cucu-1.0.4.dist-info → cucu-1.0.7.dist-info}/RECORD +8 -8
- {cucu-1.0.4.dist-info → cucu-1.0.7.dist-info}/WHEEL +1 -1
- {cucu-1.0.4.dist-info → cucu-1.0.7.dist-info}/entry_points.txt +0 -0
- {cucu-1.0.4.dist-info → cucu-1.0.7.dist-info}/licenses/LICENSE +0 -0
cucu/cli/core.py
CHANGED
|
@@ -4,6 +4,7 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import shutil
|
|
6
6
|
import signal
|
|
7
|
+
import sys
|
|
7
8
|
import time
|
|
8
9
|
import xml.etree.ElementTree as ET
|
|
9
10
|
from importlib.metadata import version
|
|
@@ -189,6 +190,7 @@ def main():
|
|
|
189
190
|
"--workers",
|
|
190
191
|
default=None,
|
|
191
192
|
help="Specifies the number of workers to use to run tests in parallel",
|
|
193
|
+
type=int,
|
|
192
194
|
)
|
|
193
195
|
@click.option(
|
|
194
196
|
"--verbose/--no-verbose",
|
|
@@ -338,11 +340,20 @@ def run(
|
|
|
338
340
|
if os.path.isdir(filepath):
|
|
339
341
|
basepath = os.path.join(filepath, "**/*.feature")
|
|
340
342
|
feature_filepaths = list(glob.iglob(basepath, recursive=True))
|
|
341
|
-
|
|
342
343
|
else:
|
|
343
344
|
feature_filepaths = [filepath]
|
|
344
345
|
|
|
345
|
-
|
|
346
|
+
if sys.platform == "darwin":
|
|
347
|
+
logger.info(
|
|
348
|
+
"MAC OS detected, using 'forkserver' start method since 'fork' is unstable"
|
|
349
|
+
)
|
|
350
|
+
start_method = "forkserver"
|
|
351
|
+
else:
|
|
352
|
+
start_method = "fork"
|
|
353
|
+
|
|
354
|
+
with WorkerPool(
|
|
355
|
+
n_jobs=int(workers), start_method=start_method
|
|
356
|
+
) as pool:
|
|
346
357
|
# Each feature file is applied to the pool as an async task.
|
|
347
358
|
# It then polls the async result of each task. It the result
|
|
348
359
|
# is ready, it removes the result from the list of results that
|
|
@@ -364,6 +375,34 @@ def run(
|
|
|
364
375
|
timer = Timer(runtime_timeout, runtime_exit)
|
|
365
376
|
timer.start()
|
|
366
377
|
|
|
378
|
+
def kill_workers():
|
|
379
|
+
for worker in pool._workers:
|
|
380
|
+
try:
|
|
381
|
+
worker_proc = psutil.Process(worker.pid)
|
|
382
|
+
for child in worker_proc.children():
|
|
383
|
+
child.kill()
|
|
384
|
+
|
|
385
|
+
worker_proc.kill()
|
|
386
|
+
except psutil.NoSuchProcess:
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
def handle_kill_signal(signum, frame):
|
|
390
|
+
signal.signal(
|
|
391
|
+
signum, signal.SIG_IGN
|
|
392
|
+
) # ignore additional signals
|
|
393
|
+
logger.warn(
|
|
394
|
+
f"received signal {signum}, sending kill signal to workers"
|
|
395
|
+
)
|
|
396
|
+
kill_workers()
|
|
397
|
+
if timer:
|
|
398
|
+
timer.cancel()
|
|
399
|
+
|
|
400
|
+
os.kill(os.getpid(), signal.SIGINT)
|
|
401
|
+
|
|
402
|
+
# This is for local runs where you want to cancel the run with a ctrl+c or SIGTERM
|
|
403
|
+
signal.signal(signal.SIGINT, handle_kill_signal)
|
|
404
|
+
signal.signal(signal.SIGTERM, handle_kill_signal)
|
|
405
|
+
|
|
367
406
|
async_results = {}
|
|
368
407
|
for feature_filepath in feature_filepaths:
|
|
369
408
|
async_results[feature_filepath] = pool.apply_async(
|
|
@@ -430,15 +469,7 @@ def run(
|
|
|
430
469
|
|
|
431
470
|
if timeout_reached:
|
|
432
471
|
logger.warn("Timeout reached, send kill signal to workers")
|
|
433
|
-
|
|
434
|
-
try:
|
|
435
|
-
worker_proc = psutil.Process(worker.pid)
|
|
436
|
-
for child in worker_proc.children():
|
|
437
|
-
child.kill()
|
|
438
|
-
|
|
439
|
-
worker_proc.kill()
|
|
440
|
-
except psutil.NoSuchProcess:
|
|
441
|
-
pass
|
|
472
|
+
kill_workers()
|
|
442
473
|
|
|
443
474
|
task_failed.update(async_results)
|
|
444
475
|
|
cucu/steps/file_input_steps.py
CHANGED
|
@@ -41,30 +41,125 @@ def upload_file_to_input(ctx, filepath, name):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
JS_DROP_FILE = """
|
|
44
|
-
var
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
44
|
+
var args = arguments,
|
|
45
|
+
element = args[0],
|
|
46
|
+
offsetX = args[1],
|
|
47
|
+
offsetY = args[2],
|
|
48
|
+
doc = element.ownerDocument || document;
|
|
49
|
+
|
|
50
|
+
var box = element.getBoundingClientRect(),
|
|
51
|
+
clientX = box.left + (offsetX || box.width / 2),
|
|
52
|
+
clientY = box.top + (offsetY || box.height / 2),
|
|
53
|
+
target = doc.elementFromPoint(clientX, clientY);
|
|
54
|
+
if (!(target && element.contains(target))) {
|
|
55
|
+
element.scrollIntoView({
|
|
56
|
+
behavior: "instant",
|
|
57
|
+
block: "center",
|
|
58
|
+
inline: "center",
|
|
59
|
+
});
|
|
60
|
+
var box = element.getBoundingClientRect(),
|
|
61
|
+
clientX = box.left + (offsetX || box.width / 2),
|
|
62
|
+
clientY = box.top + (offsetY || box.height / 2),
|
|
63
|
+
target = doc.elementFromPoint(clientX, clientY);
|
|
64
|
+
|
|
65
|
+
if (!(target && element.contains(target))) {
|
|
66
|
+
// Scrolling didn't do the trick, so give up
|
|
67
|
+
var ex = new Error("Element not interactable");
|
|
68
|
+
ex.code = 15;
|
|
69
|
+
throw ex;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
var input = doc.createElement("INPUT");
|
|
74
|
+
input.setAttribute("type", "file");
|
|
75
|
+
input.setAttribute("multiple", "");
|
|
76
|
+
input.setAttribute("style", "position:fixed;z-index:2147483647;left:0;top:0;");
|
|
77
|
+
input.onchange = function (ev) {
|
|
78
|
+
input.parentElement.removeChild(input);
|
|
79
|
+
ev.stopPropagation();
|
|
80
|
+
|
|
81
|
+
var dataTransfer = {
|
|
82
|
+
constructor: DataTransfer,
|
|
83
|
+
effectAllowed: "all",
|
|
84
|
+
dropEffect: "none",
|
|
85
|
+
types: ["Files"],
|
|
86
|
+
files: input.files,
|
|
87
|
+
setData: function setData() {},
|
|
88
|
+
getData: function getData() {},
|
|
89
|
+
clearData: function clearData() {},
|
|
90
|
+
setDragImage: function setDragImage() {},
|
|
66
91
|
};
|
|
67
|
-
|
|
92
|
+
|
|
93
|
+
if (window.DataTransferItemList) {
|
|
94
|
+
dataTransfer.items = Object.setPrototypeOf(
|
|
95
|
+
Array.prototype.map.call(input.files, function (f) {
|
|
96
|
+
return {
|
|
97
|
+
constructor: DataTransferItem,
|
|
98
|
+
kind: "file",
|
|
99
|
+
type: f.type,
|
|
100
|
+
getAsFile: function getAsFile() {
|
|
101
|
+
return f;
|
|
102
|
+
},
|
|
103
|
+
getAsString: function getAsString(callback) {
|
|
104
|
+
var reader = new FileReader();
|
|
105
|
+
reader.onload = function (ev) {
|
|
106
|
+
callback(ev.target.result);
|
|
107
|
+
};
|
|
108
|
+
reader.readAsText(f);
|
|
109
|
+
},
|
|
110
|
+
webkitGetAsEntry: function webkitGetAsEntry() {
|
|
111
|
+
return {
|
|
112
|
+
constructor: window.FileSystemEntry || window.Entry,
|
|
113
|
+
name: f.name,
|
|
114
|
+
fullPath: "/" + f.name,
|
|
115
|
+
isFile: true,
|
|
116
|
+
isDirectory: false,
|
|
117
|
+
file: function file(callback) {
|
|
118
|
+
callback(f);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
124
|
+
{
|
|
125
|
+
constructor: DataTransferItemList,
|
|
126
|
+
add: function add() {},
|
|
127
|
+
clear: function clear() {},
|
|
128
|
+
remove: function remove() {},
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
["dragenter", "dragover", "drop"].forEach(function (type) {
|
|
134
|
+
var event = doc.createEvent("DragEvent");
|
|
135
|
+
event.initMouseEvent(
|
|
136
|
+
type,
|
|
137
|
+
true,
|
|
138
|
+
true,
|
|
139
|
+
doc.defaultView,
|
|
140
|
+
0,
|
|
141
|
+
0,
|
|
142
|
+
0,
|
|
143
|
+
clientX,
|
|
144
|
+
clientY,
|
|
145
|
+
false,
|
|
146
|
+
false,
|
|
147
|
+
false,
|
|
148
|
+
false,
|
|
149
|
+
0,
|
|
150
|
+
null
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
Object.setPrototypeOf(event, null);
|
|
154
|
+
event.dataTransfer = dataTransfer;
|
|
155
|
+
Object.setPrototypeOf(event, DragEvent.prototype);
|
|
156
|
+
|
|
157
|
+
target.dispatchEvent(event);
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
doc.documentElement.appendChild(input);
|
|
162
|
+
input.getBoundingClientRect(); /* force reflow for Firefox */
|
|
68
163
|
return input;
|
|
69
164
|
"""
|
|
70
165
|
|
cucu/steps/webserver_steps.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import socket
|
|
1
2
|
from functools import partial
|
|
2
3
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
3
4
|
from threading import Thread
|
|
4
5
|
|
|
5
6
|
from behave import step
|
|
6
7
|
|
|
7
|
-
from cucu import register_after_this_scenario_hook
|
|
8
|
+
from cucu import logger, register_after_this_scenario_hook
|
|
8
9
|
from cucu.config import CONFIG
|
|
9
10
|
|
|
10
11
|
|
|
@@ -33,6 +34,9 @@ def run_webserver_for_scenario(ctx, directory, variable):
|
|
|
33
34
|
_, port = httpd.server_address
|
|
34
35
|
CONFIG[variable] = str(port)
|
|
35
36
|
|
|
37
|
+
with socket.create_connection(("localhost", port), timeout=5):
|
|
38
|
+
logger.debug(f"Webserver is running at {port=}port")
|
|
39
|
+
|
|
36
40
|
def shutdown_webserver(_):
|
|
37
41
|
httpd.shutdown()
|
|
38
42
|
thread.join()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cucu
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.7
|
|
4
4
|
Summary: Easy BDD web testing
|
|
5
5
|
Project-URL: Homepage, https://github.com/dominodatalab/cucu
|
|
6
6
|
Project-URL: Download, https://pypi.org/project/cucu/
|
|
@@ -34,14 +34,14 @@ Requires-Dist: geckodriver-autoinstaller~=0.1.0
|
|
|
34
34
|
Requires-Dist: humanize~=4.8.0
|
|
35
35
|
Requires-Dist: importlib-metadata~=8.0.0
|
|
36
36
|
Requires-Dist: ipdb~=0.13.13
|
|
37
|
-
Requires-Dist: jellyfish~=1.
|
|
37
|
+
Requires-Dist: jellyfish~=1.1.3
|
|
38
38
|
Requires-Dist: jinja2~=3.1.3
|
|
39
39
|
Requires-Dist: lsprotocol~=2023.0.1
|
|
40
40
|
Requires-Dist: mpire~=2.10.2
|
|
41
41
|
Requires-Dist: psutil~=6.0.0
|
|
42
42
|
Requires-Dist: pygls~=1.3.1
|
|
43
43
|
Requires-Dist: pyyaml~=6.0.1
|
|
44
|
-
Requires-Dist: requests
|
|
44
|
+
Requires-Dist: requests<3.0.0,>=2.31.0
|
|
45
45
|
Requires-Dist: selenium~=4.15
|
|
46
46
|
Requires-Dist: tabulate~=0.9.0
|
|
47
47
|
Requires-Dist: tenacity~=9.0.0
|
|
@@ -14,7 +14,7 @@ cucu/browser/frames.py,sha256=IW7kzRJn5PkbMaovIelAeCWO-T-2sOTwqaYBw-0-LKU,3545
|
|
|
14
14
|
cucu/browser/selenium.py,sha256=c3B6IShD8Es-TGW-dxxNibGItJ4WEQI_xJpgc6uL6-E,11613
|
|
15
15
|
cucu/browser/selenium_tweaks.py,sha256=oUIhWVhBZbc9qsmQUJMpIr9uUWKxtgZBcnySWU6Yttk,879
|
|
16
16
|
cucu/cli/__init__.py,sha256=uXX5yVG1konJ_INdlrcfMg-Tt_5_cSx29Ed8R8v908A,62
|
|
17
|
-
cucu/cli/core.py,sha256=
|
|
17
|
+
cucu/cli/core.py,sha256=qFEEmTrj2avKoyYtPqQjd-tgvHp8R21NCtP_pknwEos,24225
|
|
18
18
|
cucu/cli/run.py,sha256=e2JR77YF-7YSC4nCjogPcIsfoH7T43dAz5x_eeeue6k,5906
|
|
19
19
|
cucu/cli/steps.py,sha256=hxsLymlYvF0uqUkDVq3s6heABkYnRo_SdQCpBdpb0e0,4009
|
|
20
20
|
cucu/cli/thread_dumper.py,sha256=Z3XnYSxidx6pqjlQ7zu-TKMIYZWk4z9c5YLdPkcemiU,1593
|
|
@@ -60,7 +60,7 @@ cucu/steps/command_steps.py,sha256=nVCc8-TEitetk-zhk4z1wa0owqLQyHeQVH5THioUw-k,5
|
|
|
60
60
|
cucu/steps/comment_steps.py,sha256=KcU0Ya8XSjYEh8gdUGh0qsAxgB_Ru977E84yJaXrvz0,379
|
|
61
61
|
cucu/steps/draggable_steps.py,sha256=lnQLicp0GZJaxD_Qm2P13ruUZAsl3mptwaI5-SQ6XJ0,4655
|
|
62
62
|
cucu/steps/dropdown_steps.py,sha256=abykG--m79kDQ4LU1tm73fNLFPmrKDavyFzJb2MYCu0,15601
|
|
63
|
-
cucu/steps/file_input_steps.py,sha256=
|
|
63
|
+
cucu/steps/file_input_steps.py,sha256=LLMAozVpceLMD-kJOE-auKHAdWLbNprH8eCfVQuNoGg,5523
|
|
64
64
|
cucu/steps/filesystem_steps.py,sha256=8l37A-yPxT4Mzdi1JNSTShZkwyFxgSwnh6C0V-hM_RA,4741
|
|
65
65
|
cucu/steps/flow_control_steps.py,sha256=vlW0CsphVS9NvrOnpT8wSS2ngHmO3Z87H9siKIQwsAw,6365
|
|
66
66
|
cucu/steps/image_steps.py,sha256=4X6bdumsIybcJBuao83TURxWAIshZyCvKi1uTJEoy1k,941
|
|
@@ -75,9 +75,9 @@ cucu/steps/table_steps.py,sha256=XB-NTwuue5LCXGlLZLex0QcRKOSrc4UA_F4Kj6l-J78,137
|
|
|
75
75
|
cucu/steps/tables.js,sha256=Os2a7Fo-cg03XVli7USvcnBVad4N7idXr-HBuzdLvVQ,945
|
|
76
76
|
cucu/steps/text_steps.py,sha256=Jj_GHoHeemNwVdUOdqcehArNp7WM-WMjljA4w0pLXuw,2576
|
|
77
77
|
cucu/steps/variable_steps.py,sha256=WSctH3_xcxjijGPYZlxp-foC_SIAAKtF__saNtgZJbk,2966
|
|
78
|
-
cucu/steps/webserver_steps.py,sha256=
|
|
79
|
-
cucu-1.0.
|
|
80
|
-
cucu-1.0.
|
|
81
|
-
cucu-1.0.
|
|
82
|
-
cucu-1.0.
|
|
83
|
-
cucu-1.0.
|
|
78
|
+
cucu/steps/webserver_steps.py,sha256=wWkpSvcSMdiskPkh4cqlepWx1nkvEpTU2tRXQmPDbyo,1410
|
|
79
|
+
cucu-1.0.7.dist-info/METADATA,sha256=EGuVc5vJmj6BjOYIEwPQByWkoXhr1ip91MbnPcVzAZk,16245
|
|
80
|
+
cucu-1.0.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
81
|
+
cucu-1.0.7.dist-info/entry_points.txt,sha256=YEXTyEfIZbcV0GJ9R3Gfu3j6DcOJJK7_XHkJqE3Yiao,39
|
|
82
|
+
cucu-1.0.7.dist-info/licenses/LICENSE,sha256=WfgJYF9EaQoL_OeWr2Qd0MxhhFegDfzWSUmvDTwFxis,1721
|
|
83
|
+
cucu-1.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|