cucu 1.3.12__tar.gz → 1.3.21__tar.gz

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.

Files changed (95) hide show
  1. {cucu-1.3.12 → cucu-1.3.21}/PKG-INFO +3 -3
  2. {cucu-1.3.12 → cucu-1.3.21}/pyproject.toml +3 -3
  3. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/browser/core.py +3 -0
  4. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/browser/selenium.py +7 -0
  5. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/cli/core.py +99 -92
  6. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/cli/run.py +6 -7
  7. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/cli/steps.py +1 -1
  8. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/config.py +2 -1
  9. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/db.py +138 -62
  10. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/environment.py +14 -16
  11. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/formatter/rundb.py +4 -6
  12. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/fuzzy/core.py +2 -2
  13. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/language_server/core.py +1 -1
  14. cucu-1.3.21/src/cucu/reporter/external/manage_scenario_highlights.js +69 -0
  15. cucu-1.3.21/src/cucu/reporter/html.py +380 -0
  16. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/templates/feature.html +2 -2
  17. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/templates/flat.html +8 -8
  18. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/templates/index.html +18 -18
  19. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/templates/scenario.html +52 -34
  20. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/browser_steps.py +19 -1
  21. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/filesystem_steps.py +16 -0
  22. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/table_steps.py +4 -7
  23. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/webserver_steps.py +1 -1
  24. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/utils.py +71 -43
  25. cucu-1.3.12/src/cucu/reporter/html.py +0 -535
  26. {cucu-1.3.12 → cucu-1.3.21}/README.md +0 -0
  27. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/__init__.py +0 -0
  28. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/ansi_parser.py +0 -0
  29. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/behave_tweaks.py +0 -0
  30. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/browser/__init__.py +0 -0
  31. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/browser/frames.py +0 -0
  32. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/browser/selenium_tweaks.py +0 -0
  33. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/cli/__init__.py +0 -0
  34. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/cli/thread_dumper.py +0 -0
  35. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/edgedriver_autoinstaller/README.md +0 -0
  36. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/edgedriver_autoinstaller/__init__.py +0 -0
  37. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/edgedriver_autoinstaller/utils.py +0 -0
  38. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/external/jquery/jquery-3.5.1.min.js +0 -0
  39. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/formatter/__init__.py +0 -0
  40. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/formatter/cucu.py +0 -0
  41. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/formatter/json.py +0 -0
  42. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/formatter/junit.py +0 -0
  43. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/fuzzy/__init__.py +0 -0
  44. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/fuzzy/fuzzy.js +0 -0
  45. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/helpers.py +0 -0
  46. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/hooks.py +0 -0
  47. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/.gitignore +0 -0
  48. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/README.md +0 -0
  49. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/cucurc.yml +0 -0
  50. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/data/www/example.html +0 -0
  51. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/features/cucurc.yml +0 -0
  52. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/features/environment.py +0 -0
  53. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/features/example.feature +0 -0
  54. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/features/lint_rules/sid.yaml +0 -0
  55. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/features/steps/__init__.py +0 -0
  56. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/init_data/features/steps/my_steps.py +0 -0
  57. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/language_server/__init__.py +0 -0
  58. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/lint/__init__.py +0 -0
  59. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/lint/linter.py +0 -0
  60. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/lint/rules/format.yaml +0 -0
  61. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/logger.py +0 -0
  62. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/matcher/__init__.py +0 -0
  63. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/matcher/core.py +0 -0
  64. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/page_checks.py +0 -0
  65. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/__init__.py +0 -0
  66. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/bootstrap.min.css +0 -0
  67. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/bootstrap.min.js +0 -0
  68. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/dataTables.bootstrap.min.css +0 -0
  69. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/dataTables.bootstrap.min.js +0 -0
  70. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/jquery-3.5.1.min.js +0 -0
  71. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/jquery.dataTables.min.js +0 -0
  72. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/external/popper.min.js +0 -0
  73. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/favicon.png +0 -0
  74. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/reporter/templates/layout.html +0 -0
  75. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/__init__.py +0 -0
  76. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/base_steps.py +0 -0
  77. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/button_steps.py +0 -0
  78. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/checkbox_steps.py +0 -0
  79. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/command_steps.py +0 -0
  80. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/draggable_steps.py +0 -0
  81. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/dropdown_steps.py +0 -0
  82. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/file_input_steps.py +0 -0
  83. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/flow_control_steps.py +0 -0
  84. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/image_steps.py +0 -0
  85. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/input_steps.py +0 -0
  86. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/link_steps.py +0 -0
  87. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/menuitem_steps.py +0 -0
  88. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/platform_steps.py +0 -0
  89. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/radio_steps.py +0 -0
  90. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/section_steps.py +0 -0
  91. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/step_utils.py +0 -0
  92. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/tab_steps.py +0 -0
  93. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/tables.js +0 -0
  94. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/text_steps.py +0 -0
  95. {cucu-1.3.12 → cucu-1.3.21}/src/cucu/steps/variable_steps.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cucu
3
- Version: 1.3.12
3
+ Version: 1.3.21
4
4
  Summary: Easy BDD web testing
5
5
  Keywords: cucumber,selenium,behave
6
6
  Author: Domino Data Lab, Rodney Gomes, Cedric Young, Xin Dong, Kavya Yakkati, Kevin Garton, Joy Liao
@@ -23,11 +23,11 @@ Requires-Dist: humanize~=4.13.0
23
23
  Requires-Dist: importlib-metadata~=8.7.0
24
24
  Requires-Dist: jellyfish>=1.1
25
25
  Requires-Dist: jinja2~=3.1.3
26
- Requires-Dist: lsprotocol~=2023.0.1
26
+ Requires-Dist: lsprotocol>=2025.0.0
27
27
  Requires-Dist: mpire~=2.10.2
28
28
  Requires-Dist: peewee>=3.18.2
29
29
  Requires-Dist: psutil>=6.0
30
- Requires-Dist: pygls~=1.3.1
30
+ Requires-Dist: pygls>=2.0.0
31
31
  Requires-Dist: pyyaml~=6.0.1
32
32
  Requires-Dist: requests>=2.31.0,<3.0.0
33
33
  Requires-Dist: selenium~=4.31
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cucu"
3
- version = "1.3.12"
3
+ version = "1.3.21"
4
4
  description = "Easy BDD web testing"
5
5
  readme = "README.md"
6
6
  license = { text = "The Clear BSD License" }
@@ -45,11 +45,11 @@ dependencies = [
45
45
  "importlib-metadata~=8.7.0",
46
46
  "jellyfish>=1.1",
47
47
  "jinja2~=3.1.3",
48
- "lsprotocol~=2023.0.1",
48
+ "lsprotocol>=2025.0.0",
49
49
  "mpire~=2.10.2",
50
50
  "peewee>=3.18.2",
51
51
  "psutil>=6.0",
52
- "pygls~=1.3.1",
52
+ "pygls>=2.0.0",
53
53
  "pyyaml~=6.0.1",
54
54
  "requests>=2.31.0,<3.0.0",
55
55
  "selenium~=4.31",
@@ -67,6 +67,9 @@ class Browser:
67
67
  def switch_to_tab_that_matches_regex(self, text):
68
68
  raise RuntimeError("implement me")
69
69
 
70
+ def get_session_id(self):
71
+ raise RuntimeError("implement me")
72
+
70
73
  def quit(self):
71
74
  raise RuntimeError("implement me")
72
75
 
@@ -195,6 +195,13 @@ class Selenium(Browser):
195
195
  raise Exception(f"unknown browser {browser}")
196
196
 
197
197
  self.driver.set_window_size(width, height)
198
+ session_id = self.get_session_id()
199
+ logger.debug(f"cucu started Selenium session with ID: {session_id}")
200
+
201
+ def get_session_id(self):
202
+ if self.driver:
203
+ return getattr(self.driver, "session_id", None)
204
+ return None
198
205
 
199
206
  def get_log(self):
200
207
  if config.CONFIG["CUCU_BROWSER"] == "firefox":
@@ -1,9 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
- import glob
3
- import json
4
2
  import os
5
3
  import shutil
6
4
  import signal
5
+ import sqlite3
7
6
  import sys
8
7
  import time
9
8
  import xml.etree.ElementTree as ET
@@ -54,7 +53,9 @@ def main():
54
53
 
55
54
 
56
55
  @main.command()
57
- @click.argument("filepath")
56
+ @click.argument(
57
+ "filepath", default="features", type=click.Path(path_type=Path)
58
+ )
58
59
  @click.option(
59
60
  "-b",
60
61
  "--browser",
@@ -110,6 +111,7 @@ def main():
110
111
  default=None,
111
112
  help="specify the output directory for JUnit XML files, default is "
112
113
  "the same location as --results",
114
+ type=click.Path(path_type=Path),
113
115
  )
114
116
  @click.option(
115
117
  "--junit-with-stacktrace",
@@ -150,18 +152,14 @@ def main():
150
152
  "--report",
151
153
  default="report",
152
154
  help="the location to put the test report when --generate-report is used",
153
- )
154
- @click.option(
155
- "--report-only-failures",
156
- default=False,
157
- is_flag=True,
158
- help="when set the HTML test report will only contain the failed test results",
155
+ type=click.Path(path_type=Path),
159
156
  )
160
157
  @click.option(
161
158
  "-r",
162
159
  "--results",
163
160
  default="results",
164
161
  help="the results directory used by cucu",
162
+ type=click.Path(path_type=Path),
165
163
  )
166
164
  @click.option(
167
165
  "--runtime-timeout",
@@ -224,7 +222,6 @@ def run(
224
222
  preserve_results,
225
223
  record_env_vars,
226
224
  report,
227
- report_only_failures,
228
225
  results,
229
226
  runtime_timeout,
230
227
  feature_timeout,
@@ -247,7 +244,7 @@ def run(
247
244
  # when cucu is already running it means that we're running inside
248
245
  # another cucu process and therefore we should make sure the results
249
246
  # directory isn't the default one and throw an exception otherwise
250
- if results == "results":
247
+ if results == Path("results"):
251
248
  raise Exception(
252
249
  "running within cucu but --results was not used, "
253
250
  "this would lead to some very difficult to debug "
@@ -263,10 +260,10 @@ def run(
263
260
  logger.init_logging(logging_level.upper())
264
261
 
265
262
  if not preserve_results:
266
- if os.path.exists(results):
263
+ if results.exists():
267
264
  shutil.rmtree(results)
268
265
 
269
- os.makedirs(results, exist_ok=True)
266
+ results.mkdir(parents=True, exist_ok=True)
270
267
 
271
268
  if selenium_remote_url is not None:
272
269
  os.environ["CUCU_SELENIUM_REMOTE_URL"] = selenium_remote_url
@@ -287,9 +284,6 @@ def run(
287
284
  if junit_with_stacktrace:
288
285
  os.environ["CUCU_JUNIT_WITH_STACKTRACE"] = "true"
289
286
 
290
- if report_only_failures:
291
- os.environ["CUCU_REPORT_ONLY_FAILURES"] = "true"
292
-
293
287
  if record_env_vars:
294
288
  os.environ["CUCU_RECORD_ENV_VARS"] = "true"
295
289
 
@@ -302,12 +296,15 @@ def run(
302
296
  generate_short_id(worker_id_seed)
303
297
  )
304
298
 
305
- os.environ["CUCU_FILEPATH"] = CONFIG["CUCU_FILEPATH"] = filepath
299
+ os.environ["CUCU_FILEPATH"] = CONFIG["CUCU_FILEPATH"] = str(filepath)
306
300
 
307
301
  create_run(results, filepath)
308
302
 
309
303
  try:
310
304
  if workers is None or workers == 1:
305
+ logger.debug(
306
+ f"Starting cucu_run {CONFIG['CUCU_RUN_ID']} with single worker"
307
+ )
311
308
  if runtime_timeout:
312
309
  logger.debug("setting up runtime timeout timer")
313
310
 
@@ -347,9 +344,11 @@ def run(
347
344
  raise ClickException("test run failed, see above for details")
348
345
 
349
346
  else:
350
- if os.path.isdir(filepath):
351
- basepath = os.path.join(filepath, "**/*.feature")
352
- feature_filepaths = list(glob.iglob(basepath, recursive=True))
347
+ logger.debug(
348
+ f"Starting cucu_run {CONFIG['CUCU_RUN_ID']} with multiple workers: {workers}"
349
+ )
350
+ if filepath.is_dir():
351
+ feature_filepaths = list(filepath.rglob("*.feature"))
353
352
  else:
354
353
  feature_filepaths = [filepath]
355
354
 
@@ -486,8 +485,10 @@ def run(
486
485
  task_failed.update(async_results)
487
486
 
488
487
  if task_failed:
489
- failing_features = "\n".join(task_failed.keys())
490
- logger.error(f"Failing Features:\n{failing_features}")
488
+ failing_features = [str(x) for x in task_failed.keys()]
489
+ logger.error(
490
+ f"Failing Features:\n{'\n'.join(failing_features)}"
491
+ )
491
492
  raise RuntimeError(
492
493
  "there are failures, see above for details"
493
494
  )
@@ -495,65 +496,54 @@ def run(
495
496
  if dumper is not None:
496
497
  dumper.stop()
497
498
 
498
- if os.path.exists(results):
499
+ if results.exists():
499
500
  finish_worker_record(worker_run_id=CONFIG.get("WORKER_PARENT_ID"))
500
501
  consolidate_database_files(results)
501
502
 
502
503
  if generate_report:
503
504
  _generate_report(
504
- results,
505
- report,
506
- only_failures=report_only_failures,
507
- junit=junit,
505
+ results_dir=results,
506
+ report_folder=report,
507
+ junit_folder=junit,
508
508
  )
509
509
 
510
510
 
511
511
  def _generate_report(
512
- results_dir: str,
513
- output: str,
514
- only_failures: False,
515
- junit: str | None = None,
512
+ results_dir: Path,
513
+ report_folder: Path,
514
+ junit_folder: Path | None = None,
515
+ combine: bool = False,
516
516
  ):
517
- if os.path.exists(output):
518
- shutil.rmtree(output)
517
+ if report_folder.exists():
518
+ shutil.rmtree(report_folder)
519
519
 
520
- os.makedirs(output)
520
+ report_folder.mkdir(parents=True, exist_ok=True)
521
521
 
522
- if os.path.exists(results_dir):
523
- consolidate_database_files(results_dir)
522
+ if results_dir.exists():
523
+ consolidate_database_files(results_dir, combine)
524
524
 
525
- report_location = reporter.generate(
526
- results_dir, output, only_failures=only_failures
527
- )
525
+ report_location = reporter.generate(results_dir, report_folder)
528
526
  print(f"HTML test report at {report_location}")
529
527
 
530
- if junit:
531
- _add_report_path_in_junit(junit, output)
532
-
533
-
534
- def _add_report_path_in_junit(junit_folder, report_folder):
535
- for junit_file in glob.glob(f"{junit_folder}/*.xml", recursive=True):
536
- junit = ET.parse(junit_file)
537
- test_suite = junit.getroot()
538
- ts_folder = test_suite.get("foldername")
539
- for test_case in test_suite.iter("testcase"):
540
- report_path = os.path.join(
541
- report_folder,
542
- ts_folder,
543
- test_case.get("foldername"),
544
- "index.html",
545
- )
546
- test_case.set("report_path", report_path)
547
- junit.write(junit_file, encoding="utf-8", xml_declaration=False)
528
+ if junit_folder:
529
+ for junit_file in junit_folder.rglob("*.xml"):
530
+ junit = ET.parse(junit_file)
531
+ test_suite = junit.getroot()
532
+ ts_folder = test_suite.get("foldername")
533
+ for test_case in test_suite.iter("testcase"):
534
+ report_path = os.path.join(
535
+ report_folder,
536
+ ts_folder,
537
+ test_case.get("foldername"),
538
+ "index.html",
539
+ )
540
+ test_case.set("report_path", report_path)
541
+ junit.write(junit_file, encoding="utf-8", xml_declaration=False)
548
542
 
549
543
 
550
544
  @main.command()
551
- @click.argument("results_dir", default="results")
552
- @click.option(
553
- "--only-failures",
554
- default=False,
555
- is_flag=True,
556
- help="when set the HTML test report will only contain the failed test results",
545
+ @click.argument(
546
+ "results_dir", default="results", type=click.Path(path_type=Path)
557
547
  )
558
548
  @click.option(
559
549
  "-l",
@@ -567,21 +557,33 @@ def _add_report_path_in_junit(junit_folder, report_folder):
567
557
  is_flag=True,
568
558
  help="when set skips are shown",
569
559
  )
570
- @click.option("-o", "--output", default="report")
560
+ @click.option(
561
+ "-o",
562
+ "--output",
563
+ default="report",
564
+ type=click.Path(path_type=Path),
565
+ )
571
566
  @click.option(
572
567
  "-j",
573
568
  "--junit",
574
569
  default=None,
575
570
  help="specify the output directory for JUnit XML files, default is "
576
571
  "the same location as --results",
572
+ type=click.Path(path_type=Path),
573
+ )
574
+ @click.option(
575
+ "--combine",
576
+ default=False,
577
+ is_flag=True,
578
+ help="combine multiple cucu_runs into a single report",
577
579
  )
578
580
  def report(
579
- results_dir,
580
- only_failures,
581
+ results_dir: Path,
581
582
  logging_level,
582
583
  show_skips,
583
- output,
584
- junit,
584
+ output: Path,
585
+ junit: Path,
586
+ combine: bool,
585
587
  ):
586
588
  """
587
589
  generate a test report from a results directory
@@ -594,26 +596,32 @@ def report(
594
596
  if show_skips:
595
597
  os.environ["CUCU_SHOW_SKIPS"] = "true"
596
598
 
597
- run_details_filepath = os.path.join(results_dir, "run_details.json")
598
-
599
- if os.path.exists(run_details_filepath):
600
- # load the run details at the time of execution for the provided results
601
- # directory
602
- run_details = {}
603
-
604
- with open(run_details_filepath, encoding="utf8") as _input:
605
- run_details = json.loads(_input.read())
606
-
607
- # initialize any underlying custom step code things
608
- behave_init(run_details["filepath"])
599
+ run_db_path = results_dir / "run.db"
600
+ if run_db_path.exists():
601
+ # query cucu_run to get the original filepath used during the run
602
+ with sqlite3.connect(run_db_path) as conn:
603
+ cursor = conn.cursor()
604
+ cursor.execute(
605
+ "SELECT filepath FROM cucu_run ORDER BY start_at DESC LIMIT 1"
606
+ )
607
+ row = cursor.fetchone()
608
+ if row:
609
+ filepath = row[0]
610
+ # initialize any underlying custom step code things
611
+ behave_init(filepath)
609
612
 
610
613
  _generate_report(
611
- results_dir, output, only_failures=only_failures, junit=junit
614
+ results_dir=results_dir,
615
+ report_folder=output,
616
+ junit_folder=junit,
617
+ combine=combine,
612
618
  )
613
619
 
614
620
 
615
621
  @main.command()
616
- @click.argument("filepath", default="features")
622
+ @click.argument(
623
+ "filepath", default="features", type=click.Path(path_type=Path)
624
+ )
617
625
  @click.option(
618
626
  "-f",
619
627
  "--format",
@@ -639,7 +647,7 @@ def steps(filepath, format):
639
647
 
640
648
 
641
649
  @main.command()
642
- @click.argument("filepath", nargs=-1)
650
+ @click.argument("filepath", type=click.Path(path_type=Path), nargs=-1)
643
651
  @click.option(
644
652
  "--fix/--no-fix", default=False, help="fix lint violations, default: False"
645
653
  )
@@ -741,7 +749,9 @@ def lsp(logging_level, port):
741
749
 
742
750
 
743
751
  @main.command()
744
- @click.argument("filepath", default="features")
752
+ @click.argument(
753
+ "filepath", default="features", type=click.Path(path_type=Path)
754
+ )
745
755
  def vars(filepath):
746
756
  """
747
757
  print built-in cucu variables
@@ -766,14 +776,14 @@ def vars(filepath):
766
776
 
767
777
 
768
778
  @main.command()
769
- @click.argument("filepath", default="")
779
+ @click.argument("repo_dir", default="", type=click.Path(path_type=Path))
770
780
  @click.option(
771
781
  "-l",
772
782
  "--logging-level",
773
783
  default="INFO",
774
784
  help="set logging level to one of debug, warn or info (default)",
775
785
  )
776
- def init(filepath, logging_level):
786
+ def init(repo_dir, logging_level):
777
787
  """
778
788
  initialize cucu in the current directory
779
789
 
@@ -785,10 +795,9 @@ def init(filepath, logging_level):
785
795
  init_data_dir = Path(__file__).parent.parent / "init_data"
786
796
 
787
797
  logger.debug(f"cucu init: copy example directory from {init_data_dir=}")
788
- repo_dir = filepath if filepath.strip() else os.path.join(os.getcwd())
789
798
 
790
- features_dir = os.path.join(repo_dir, "features")
791
- if os.path.exists(features_dir):
799
+ features_dir = repo_dir / "features"
800
+ if features_dir.exists():
792
801
  answer = input("Overwrite existing files? [y/N]:")
793
802
  if answer.lower() != "y":
794
803
  print("Aborted!")
@@ -874,9 +883,7 @@ def tags(filepath, logging_level):
874
883
  if not filepath.exists() or not feature_files:
875
884
  raise ClickException("No feature files found.")
876
885
 
877
- file_locations = [
878
- FileLocation(os.path.abspath(str(f))) for f in feature_files
879
- ]
886
+ file_locations = [FileLocation(f.absolute()) for f in feature_files]
880
887
  features = parse_features(file_locations)
881
888
  tag_scenarios = Counter()
882
889
 
@@ -85,8 +85,8 @@ def behave(
85
85
  if debug_on_failure:
86
86
  os.environ["CUCU_DEBUG_ON_FAILURE"] = "true"
87
87
 
88
- os.environ["CUCU_RESULTS_DIR"] = results
89
- os.environ["CUCU_JUNIT_DIR"] = junit
88
+ os.environ["CUCU_RESULTS_DIR"] = str(results)
89
+ os.environ["CUCU_JUNIT_DIR"] = str(junit)
90
90
 
91
91
  if secrets:
92
92
  os.environ["CUCU_SECRETS"] = secrets
@@ -123,7 +123,7 @@ def behave(
123
123
  "--no-logcapture",
124
124
  # generate a JSON file containing the exact details of the whole run
125
125
  "--format=cucu.formatter.json:CucuJSONFormatter",
126
- f"--outfile={Path(results) / run_json_filename}",
126
+ f"--outfile={results / run_json_filename}",
127
127
  # console formatter
128
128
  "--format=cucu.formatter.cucu:CucuFormatter",
129
129
  f"--logging-level={os.environ['CUCU_LOGGING_LEVEL'].upper()}",
@@ -154,7 +154,7 @@ def behave(
154
154
  if redirect_output:
155
155
  feature_name = get_feature_name(filepath)
156
156
  log_filename = f"{feature_name}.log"
157
- log_filepath = Path(results) / log_filename
157
+ log_filepath = results / log_filename
158
158
 
159
159
  CONFIG["__CUCU_PARENT_STDOUT"] = sys.stdout
160
160
 
@@ -185,8 +185,7 @@ def behave(
185
185
  return result
186
186
 
187
187
 
188
- def create_run(results, filepath):
189
- results_path = Path(results)
188
+ def create_run(results_path: Path, filepath: Path):
190
189
  run_json_filepath = results_path / "run_details.json"
191
190
 
192
191
  if run_json_filepath.exists():
@@ -200,7 +199,7 @@ def create_run(results, filepath):
200
199
 
201
200
  run_details = {
202
201
  "cucu_run_id": CONFIG["CUCU_RUN_ID"],
203
- "filepath": filepath,
202
+ "filepath": str(filepath),
204
203
  "full_arguments": sys.argv,
205
204
  "env": env_values,
206
205
  "date": datetime.now().isoformat(),
@@ -131,7 +131,7 @@ def print_human_readable_steps(filepath=None):
131
131
 
132
132
  for step_name in steps:
133
133
  if steps[step_name] is not None:
134
- if filepath in steps[step_name]["location"]["filepath"]:
134
+ if str(filepath) in steps[step_name]["location"]["filepath"]:
135
135
  print(f"custom: {step_name}")
136
136
  else:
137
137
  print(f"cucu: {step_name}")
@@ -3,6 +3,7 @@ import logging
3
3
  import os
4
4
  import re
5
5
  import socket
6
+ from pathlib import Path
6
7
 
7
8
  import yaml
8
9
 
@@ -100,7 +101,7 @@ class Config(dict):
100
101
  else:
101
102
  self[key] = config[key]
102
103
 
103
- def load_cucurc_files(self, filepath):
104
+ def load_cucurc_files(self, filepath: Path):
104
105
  """
105
106
  load in order the ~/.cucurc.yml and then subsequent config files
106
107
  starting from the current working directory to the filepath provided