cli-test-framework 0.9.3__tar.gz → 1.0.1__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.
Files changed (220) hide show
  1. {cli_test_framework-0.9.3/src/cli_test_framework.egg-info → cli_test_framework-1.0.1}/PKG-INFO +4 -1
  2. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/docs/user_manual.md +123 -0
  3. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/setup.py +4 -1
  4. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/cli.py +21 -0
  5. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/config_loader.py +63 -38
  6. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/test_case.py +17 -1
  7. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/config_runner.py +1 -9
  8. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/parallel_config_runner.py +1 -9
  9. cli_test_framework-1.0.1/src/cli_test_framework/tui/__init__.py +11 -0
  10. cli_test_framework-1.0.1/src/cli_test_framework/tui/app.py +78 -0
  11. cli_test_framework-1.0.1/src/cli_test_framework/tui/controllers/__init__.py +0 -0
  12. cli_test_framework-1.0.1/src/cli_test_framework/tui/controllers/case_controller.py +270 -0
  13. cli_test_framework-1.0.1/src/cli_test_framework/tui/screens/__init__.py +0 -0
  14. cli_test_framework-1.0.1/src/cli_test_framework/tui/screens/case_editor.py +233 -0
  15. cli_test_framework-1.0.1/src/cli_test_framework/tui/screens/case_list.py +234 -0
  16. cli_test_framework-1.0.1/src/cli_test_framework/tui/widgets/__init__.py +0 -0
  17. cli_test_framework-1.0.1/src/cli_test_framework/tui/widgets/case_table.py +113 -0
  18. cli_test_framework-1.0.1/src/cli_test_framework/tui/widgets/expected_editor.py +159 -0
  19. cli_test_framework-1.0.1/src/cli_test_framework/tui/widgets/search_bar.py +160 -0
  20. cli_test_framework-1.0.1/src/cli_test_framework/tui/widgets/steps_editor.py +233 -0
  21. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1/src/cli_test_framework.egg-info}/PKG-INFO +4 -1
  22. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework.egg-info/SOURCES.txt +34 -0
  23. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework.egg-info/requires.txt +3 -0
  24. cli_test_framework-1.0.1/tests/README.md +25 -0
  25. cli_test_framework-1.0.1/tests/unit/__pycache__/test_binary_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
  26. cli_test_framework-1.0.1/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
  27. cli_test_framework-1.0.1/tests/unit/__pycache__/test_result.cpython-312-pytest-9.0.3.pyc +0 -0
  28. cli_test_framework-1.0.1/tests/unit/test_binary_comparator.py +426 -0
  29. cli_test_framework-1.0.1/tests/unit/test_cli.py +693 -0
  30. cli_test_framework-1.0.1/tests/unit/test_result.py +405 -0
  31. cli_test_framework-1.0.1/tests/unit/tui/__init__.py +0 -0
  32. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/__init__.cpython-312.pyc +0 -0
  33. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
  34. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_app.cpython-312-pytest-7.4.4.pyc +0 -0
  35. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_app.cpython-312-pytest-9.0.3.pyc +0 -0
  36. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_case_controller.cpython-312-pytest-7.4.4.pyc +0 -0
  37. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_case_controller.cpython-312-pytest-9.0.3.pyc +0 -0
  38. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_search_functions.cpython-312-pytest-7.4.4.pyc +0 -0
  39. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_search_functions.cpython-312-pytest-9.0.3.pyc +0 -0
  40. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_tui_run_test.cpython-312-pytest-9.0.3.pyc +0 -0
  41. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_widgets_data.cpython-312-pytest-7.4.4.pyc +0 -0
  42. cli_test_framework-1.0.1/tests/unit/tui/__pycache__/test_widgets_data.cpython-312-pytest-9.0.3.pyc +0 -0
  43. cli_test_framework-1.0.1/tests/unit/tui/conftest.py +73 -0
  44. cli_test_framework-1.0.1/tests/unit/tui/test_app.py +106 -0
  45. cli_test_framework-1.0.1/tests/unit/tui/test_case_controller.py +425 -0
  46. cli_test_framework-1.0.1/tests/unit/tui/test_search_functions.py +187 -0
  47. cli_test_framework-1.0.1/tests/unit/tui/test_tui_run_test.py +384 -0
  48. cli_test_framework-1.0.1/tests/unit/tui/test_widgets_data.py +175 -0
  49. cli_test_framework-0.9.3/tests/README.md +0 -15
  50. cli_test_framework-0.9.3/tests/unit/__pycache__/test_cli.cpython-312-pytest-9.0.3.pyc +0 -0
  51. cli_test_framework-0.9.3/tests/unit/test_cli.py +0 -157
  52. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/MANIFEST.in +0 -0
  53. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/README.md +0 -0
  54. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/docs/design.md +0 -0
  55. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/docs/design_en.md +0 -0
  56. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/docs/tui_and_config_split_design.md +0 -0
  57. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/docs/user_manual_en.md +0 -0
  58. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/pyproject.toml +0 -0
  59. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/setup.cfg +0 -0
  60. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/__init__.py +0 -0
  61. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/commands/__init__.py +0 -0
  62. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/commands/compare.py +0 -0
  63. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/config/__init__.py +0 -0
  64. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/config/config_io.py +0 -0
  65. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/config/import_expander.py +0 -0
  66. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/__init__.py +0 -0
  67. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/assertions.py +0 -0
  68. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/base_runner.py +0 -0
  69. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/execution.py +0 -0
  70. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/history_store.py +0 -0
  71. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/parallel_runner.py +0 -0
  72. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/process_worker.py +0 -0
  73. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/setup.py +0 -0
  74. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/core/types.py +0 -0
  75. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/__init__.py +0 -0
  76. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/base_comparator.py +0 -0
  77. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/binary_comparator.py +0 -0
  78. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/csv_comparator.py +0 -0
  79. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/factory.py +0 -0
  80. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/h5_comparator.py +0 -0
  81. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/json_comparator.py +0 -0
  82. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/result.py +0 -0
  83. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/text_comparator.py +0 -0
  84. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/file_comparator/xml_comparator.py +0 -0
  85. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/logging_config.py +0 -0
  86. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/__init__.py +0 -0
  87. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/json_runner.py +0 -0
  88. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/parallel_json_runner.py +0 -0
  89. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/parallel_yaml_runner.py +0 -0
  90. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/runners/yaml_runner.py +0 -0
  91. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/utils/__init__.py +0 -0
  92. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/utils/junit_xml_writer.py +0 -0
  93. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/utils/path_resolver.py +0 -0
  94. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework/utils/report_generator.py +0 -0
  95. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework.egg-info/dependency_links.txt +0 -0
  96. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework.egg-info/entry_points.txt +0 -0
  97. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/src/cli_test_framework.egg-info/top_level.txt +0 -0
  98. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__init__.py +0 -0
  99. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  100. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/__init__.cpython-39.pyc +0 -0
  101. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/conftest.cpython-312-pytest-7.4.4.pyc +0 -0
  102. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc +0 -0
  103. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc +0 -0
  104. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/run_all.cpython-312.pyc +0 -0
  105. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/run_all.cpython-39.pyc +0 -0
  106. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
  107. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/__pycache__/test_setup_module.cpython-312-pytest-7.4.4.pyc +0 -0
  108. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/conftest.py +0 -0
  109. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/demos/h5_filter_demo.py +0 -0
  110. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/demos/manual_report_example.py +0 -0
  111. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/demos/perf_parallel.py +0 -0
  112. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/__init__.py +0 -0
  113. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/__pycache__/__init__.cpython-312.pyc +0 -0
  114. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/__pycache__/__init__.cpython-39.pyc +0 -0
  115. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-7.4.4.pyc +0 -0
  116. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/__pycache__/test_user_flows.cpython-312-pytest-9.0.3.pyc +0 -0
  117. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/__pycache__/test_user_flows.cpython-39-pytest-8.3.4.pyc +0 -0
  118. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/e2e/test_user_flows.py +0 -0
  119. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/circular_config_a.json +0 -0
  120. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/circular_config_b.json +0 -0
  121. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/main_config.json +0 -0
  122. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/missing_fields.json +0 -0
  123. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/setup_main.json +0 -0
  124. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/setup_sub.json +0 -0
  125. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/sub_json_tests.yaml +0 -0
  126. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/sub_text_tests.json +0 -0
  127. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/test_cases.json +0 -0
  128. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/test_cases.yaml +0 -0
  129. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/test_cases1.json +0 -0
  130. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/test_with_setup.json +0 -0
  131. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/fixtures/test_with_setup.yaml +0 -0
  132. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-7.4.4.pyc +0 -0
  133. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/__pycache__/test_history_feature.cpython-312-pytest-9.0.3.pyc +0 -0
  134. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-7.4.4.pyc +0 -0
  135. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/__pycache__/test_real_command_execution.cpython-312-pytest-9.0.3.pyc +0 -0
  136. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-7.4.4.pyc +0 -0
  137. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/__pycache__/test_sequence.cpython-312-pytest-9.0.3.pyc +0 -0
  138. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-7.4.4.pyc +0 -0
  139. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-312-pytest-9.0.3.pyc +0 -0
  140. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_binary_compare.cpython-39-pytest-8.3.4.pyc +0 -0
  141. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-7.4.4.pyc +0 -0
  142. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_csv_compare.cpython-312-pytest-9.0.3.pyc +0 -0
  143. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-7.4.4.pyc +0 -0
  144. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-312-pytest-9.0.3.pyc +0 -0
  145. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_h5_compare.cpython-39-pytest-8.3.4.pyc +0 -0
  146. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-7.4.4.pyc +0 -0
  147. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-312-pytest-9.0.3.pyc +0 -0
  148. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_json_compare.cpython-39-pytest-8.3.4.pyc +0 -0
  149. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-7.4.4.pyc +0 -0
  150. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-312-pytest-9.0.3.pyc +0 -0
  151. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_text_compare.cpython-39-pytest-8.3.4.pyc +0 -0
  152. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-7.4.4.pyc +0 -0
  153. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/__pycache__/test_xml_compare.cpython-312-pytest-9.0.3.pyc +0 -0
  154. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/test_binary_compare.py +0 -0
  155. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/test_csv_compare.py +0 -0
  156. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/test_h5_compare.py +0 -0
  157. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/test_json_compare.py +0 -0
  158. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/test_text_compare.py +0 -0
  159. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/file_compare/test_xml_compare.py +0 -0
  160. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
  161. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-312-pytest-9.0.3.pyc +0 -0
  162. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/parallel/__pycache__/test_parallel_runner.cpython-39-pytest-8.3.4.pyc +0 -0
  163. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/parallel/test_parallel_runner.py +0 -0
  164. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-7.4.4.pyc +0 -0
  165. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-312-pytest-9.0.3.pyc +0 -0
  166. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/path_handling/__pycache__/test_spaces_in_paths.cpython-39-pytest-8.3.4.pyc +0 -0
  167. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/path_handling/test_spaces_in_paths.py +0 -0
  168. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/test_history_feature.py +0 -0
  169. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/test_real_command_execution.py +0 -0
  170. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/integration/test_sequence.py +0 -0
  171. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/run_all.py +0 -0
  172. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/test_report.txt +0 -0
  173. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_cli.cpython-312-pytest-7.4.4.pyc +0 -0
  174. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_config_io.cpython-312-pytest-9.0.3.pyc +0 -0
  175. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-7.4.4.pyc +0 -0
  176. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_h5_comparator.cpython-312-pytest-9.0.3.pyc +0 -0
  177. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_import_expander.cpython-312-pytest-7.4.4.pyc +0 -0
  178. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_import_expander.cpython-312-pytest-9.0.3.pyc +0 -0
  179. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-7.4.4.pyc +0 -0
  180. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/__pycache__/test_run_all.cpython-312-pytest-9.0.3.pyc +0 -0
  181. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-7.4.4.pyc +0 -0
  182. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/commands/__pycache__/test_compare_command.cpython-312-pytest-9.0.3.pyc +0 -0
  183. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/commands/test_compare_command.py +0 -0
  184. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-7.4.4.pyc +0 -0
  185. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_assertions.cpython-312-pytest-9.0.3.pyc +0 -0
  186. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_config_loader.cpython-312-pytest-9.0.3.pyc +0 -0
  187. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_execution.cpython-312-pytest-9.0.3.pyc +0 -0
  188. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-7.4.4.pyc +0 -0
  189. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_history_store.cpython-312-pytest-9.0.3.pyc +0 -0
  190. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-7.4.4.pyc +0 -0
  191. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_process_worker.cpython-312-pytest-9.0.3.pyc +0 -0
  192. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-7.4.4.pyc +0 -0
  193. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_setup.cpython-312-pytest-9.0.3.pyc +0 -0
  194. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/__pycache__/test_setup.cpython-39-pytest-8.3.4.pyc +0 -0
  195. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/test_assertions.py +0 -0
  196. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/test_config_loader.py +0 -0
  197. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/test_execution.py +0 -0
  198. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/test_history_store.py +0 -0
  199. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/test_process_worker.py +0 -0
  200. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/core/test_setup.py +0 -0
  201. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-7.4.4.pyc +0 -0
  202. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312-pytest-9.0.3.pyc +0 -0
  203. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-312.pyc +0 -0
  204. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_json_yaml_runner.cpython-39-pytest-8.3.4.pyc +0 -0
  205. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-7.4.4.pyc +0 -0
  206. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_test_case_filter.cpython-312-pytest-9.0.3.pyc +0 -0
  207. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/__pycache__/test_test_case_tag_filter.cpython-312-pytest-9.0.3.pyc +0 -0
  208. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/test_json_yaml_runner.py +0 -0
  209. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/test_test_case_filter.py +0 -0
  210. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/runners/test_test_case_tag_filter.py +0 -0
  211. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/test_config_io.py +0 -0
  212. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/test_h5_comparator.py +0 -0
  213. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/test_import_expander.py +0 -0
  214. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/test_run_all.py +0 -0
  215. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-7.4.4.pyc +0 -0
  216. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/utils/__pycache__/test_junit_xml_writer.cpython-312-pytest-9.0.3.pyc +0 -0
  217. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-7.4.4.pyc +0 -0
  218. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/utils/__pycache__/test_report_generator.cpython-312-pytest-9.0.3.pyc +0 -0
  219. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/utils/test_junit_xml_writer.py +0 -0
  220. {cli_test_framework-0.9.3 → cli_test_framework-1.0.1}/tests/unit/utils/test_report_generator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-test-framework
3
- Version: 0.9.3
3
+ Version: 1.0.1
4
4
  Summary: A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.
5
5
  Home-page: https://github.com/ozil111/cli-test-framework
6
6
  Author: Xiaotong Wang
@@ -23,6 +23,8 @@ Requires-Dist: numpy>=1.21.0; python_version < "3.12"
23
23
  Requires-Dist: numpy>=1.26.0; python_version >= "3.12"
24
24
  Requires-Dist: setuptools>=75.8.0
25
25
  Requires-Dist: wheel>=0.45.1
26
+ Provides-Extra: tui
27
+ Requires-Dist: textual>=0.40.0; extra == "tui"
26
28
  Dynamic: author
27
29
  Dynamic: author-email
28
30
  Dynamic: classifier
@@ -30,6 +32,7 @@ Dynamic: description
30
32
  Dynamic: description-content-type
31
33
  Dynamic: home-page
32
34
  Dynamic: project-url
35
+ Dynamic: provides-extra
33
36
  Dynamic: requires-dist
34
37
  Dynamic: requires-python
35
38
  Dynamic: summary
@@ -6,6 +6,7 @@
6
6
  - [测试用例定义](#测试用例定义)
7
7
  - [配置拆分机制](#配置拆分机制)
8
8
  - [配置校验](#配置校验)
9
+ - [TUI 交互式管理器](#tui-交互式管理器)
9
10
  - [运行测试](#运行测试)
10
11
  - [占位符(变量替换)](#占位符变量替换)
11
12
  - [标签过滤](#标签过滤)
@@ -304,6 +305,128 @@ cli-test validate test_cases.json --workspace /path/to/project
304
305
  [FAIL] Import target not found: /project/cases/nonexistent.json
305
306
  ```
306
307
 
308
+ ## TUI 交互式管理器
309
+
310
+ 当测试用例数量增长到几十甚至上百条时,纯手工编辑 JSON/YAML 配置文件容易出错且效率低下。TUI(Terminal User Interface)交互式管理器提供了一个终端内的可视化界面,支持浏览、搜索、编辑、运行测试用例,所有操作通过键盘快捷键完成,无需切换窗口或打开编辑器。
311
+
312
+ ### 安装
313
+
314
+ TUI 依赖 `textual` 库,作为可选依赖提供,不影响核心框架的安装体积:
315
+
316
+ ```bash
317
+ # 安装时附带 TUI 支持
318
+ pip install cli-test-framework[tui]
319
+
320
+ # 或者在已有框架上单独安装 textual
321
+ pip install textual
322
+ ```
323
+
324
+ 如果未安装 `textual` 就直接执行 `cli-test tui`,框架会给出友好的安装提示。
325
+
326
+ ### 启动
327
+
328
+ ```bash
329
+ # 打开 TUI 编辑测试用例
330
+ cli-test tui test_cases.json
331
+
332
+ # YAML 文件同样支持
333
+ cli-test tui test_cases.yaml
334
+
335
+ # 指定工作目录
336
+ cli-test tui test_cases.json --workspace /path/to/project
337
+
338
+ # 打开带 import 的主配置文件(自动展开子文件中的所有用例)
339
+ cli-test tui main_config.json
340
+ ```
341
+
342
+ TUI 启动时会自动通过[配置拆分机制](#配置拆分机制)展开 `import` 引用,将所有用例加载到界面中统一管理。
343
+
344
+ ### 界面概览
345
+
346
+ TUI 启动后显示**用例列表主界面**:
347
+
348
+ - **顶部状态栏**:当前文件名、用例总数
349
+ - **搜索栏**:`/` 键聚焦搜索框,支持子串/模糊/正则三种模式
350
+ - **用例表格**:六列(序号、名称、命令、标签、超时、模式),支持键盘导航
351
+ - **底部快捷键栏**:显示所有可用操作
352
+
353
+ ### 快捷键
354
+
355
+ | 快捷键 | 功能 |
356
+ |---|---|
357
+ | `a` | 新增用例 |
358
+ | `e` | 编辑选中的用例 |
359
+ | `d` | 删除选中的用例 |
360
+ | `u` | 复制选中的用例(名称追加 `_copy` 后缀) |
361
+ | `r` | 运行选中的用例,显示执行结果 |
362
+ | `F6` / `Ctrl+S` | 保存修改到文件 |
363
+ | `/` | 聚焦搜索框 |
364
+ | `Esc` | 清除搜索,恢复完整列表 |
365
+ | `Alt+S` | 切换子串搜索模式(大小写不敏感) |
366
+ | `Alt+F` | 切换模糊搜索模式(容忍拼写差异和缩写) |
367
+ | `Alt+R` | 切换正则搜索模式 |
368
+ | `q` / `Ctrl+Q` | 退出 |
369
+ | `↑` / `↓` / `j` / `k` | 上下移动光标 |
370
+
371
+ 搜索时会同时匹配 `name`、`command`、`args`、`tags`、`description` 等字段,匹配结果在表格中高亮显示。
372
+
373
+ ### 编辑用例
374
+
375
+ 选中用例后按 `e` 进入编辑界面。编辑界面根据用例类型有两种模式:
376
+
377
+ #### 单命令模式
378
+
379
+ 编辑表单包含以下字段:
380
+
381
+ | 字段 | 说明 |
382
+ |---|---|
383
+ | `Name` | 用例名称(必填) |
384
+ | `Command` | 要执行的命令 |
385
+ | `Args` | 命令参数,每行一个 |
386
+ | `Tags` | 标签列表,每行一个 |
387
+ | `Description` | 用例描述 |
388
+ | `Timeout` | 超时秒数 |
389
+ | Expected | 期望断言的嵌套子表单(见下文) |
390
+
391
+ #### 步骤序列模式
392
+
393
+ 当用例包含多个有序步骤时,切换到此模式。每个步骤有独立的 `Command`、`Args`、`Expected` 和 `Timeout`,支持添加、删除、编辑和上下移动步骤。
394
+
395
+ 两种模式可通过编辑界面内的快捷键切换,切换时会提示确认以免丢失数据。
396
+
397
+ ### `expected` 字段编辑
398
+
399
+ `expected` 字段是一个嵌套字典,编辑器提供结构化输入:
400
+
401
+ | 字段 | 输入方式 |
402
+ |---|---|
403
+ | `return_code` | 数字输入框 |
404
+ | `output_contains` | 多行文本输入,一行一个匹配字符串 |
405
+ | `output_matches` | 正则表达式文本输入 |
406
+ | `compare_files` | 每行一个 JSON 对象,如 `{"actual":"out.txt","baseline":"base.txt","type":"text"}` |
407
+
408
+ 除上述已知字段外,还支持通过 `+ Add` 按钮添加自定义 key=value 对,value 为字符串或 JSON 文本。更多含义见[测试用例定义](#测试用例定义)。
409
+
410
+ ### 运行用例
411
+
412
+ 在列表中选中某条用例,按 `r` 即可实时调用框架执行引擎运行该用例。运行结束后弹出结果面板,显示:
413
+
414
+ - 通过/失败状态
415
+ - 返回码
416
+ - 耗时
417
+ - 命令输出(stdout/stderr)
418
+
419
+ 结果面板仅展示、不修改配置文件。
420
+
421
+ ### 保存
422
+
423
+ 对用例的任何增删改操作都在**内存中**进行,不会立即写入磁盘。
424
+
425
+ - 按 `F6` 或 `Ctrl+S` **保存**:将当前全部用例写回原配置文件。
426
+ - 通过 `save_as` 可**另存为**新文件(通过界面菜单操作)。
427
+
428
+ 退出 TUI 时如有未保存的修改,会弹出确认提示。
429
+
307
430
  ## 运行测试
308
431
 
309
432
  ### 命令行
@@ -8,7 +8,7 @@ with open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
8
8
 
9
9
  setup(
10
10
  name="cli-test-framework",
11
- version="0.9.3",
11
+ version="1.0.1",
12
12
  author="Xiaotong Wang",
13
13
  author_email="xiaotongwang98@gmail.com",
14
14
  description="A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.",
@@ -25,6 +25,9 @@ setup(
25
25
  "setuptools>=75.8.0",
26
26
  "wheel>=0.45.1"
27
27
  ],
28
+ extras_require={
29
+ "tui": ["textual>=0.40.0"],
30
+ },
28
31
  entry_points={
29
32
  'console_scripts': [
30
33
  'cli-test=cli_test_framework.cli:main',
@@ -44,6 +44,7 @@ Examples:
44
44
  cli-test run test_cases.json
45
45
  cli-test run test_cases.json --parallel --workers 4
46
46
  cli-test run test_cases.yaml --workspace /path/to/project
47
+ cli-test tui test_cases.json
47
48
  cli-test validate main_config.json
48
49
  cli-test compare file1.json file2.json
49
50
  cli-test compare file1.txt file2.txt --output-format json
@@ -80,6 +81,17 @@ Examples:
80
81
  'e.g. --var solver=/path/to/solver '
81
82
  '(can be used multiple times)')
82
83
 
84
+ # ---- TUI command ----
85
+ tui_parser = subparsers.add_parser(
86
+ 'tui', help='Launch interactive TUI for managing test cases'
87
+ )
88
+ tui_parser.add_argument(
89
+ 'config_file', help='Path to the test configuration file (JSON or YAML)'
90
+ )
91
+ tui_parser.add_argument(
92
+ '--workspace', '-w', help='Working directory'
93
+ )
94
+
83
95
  # ---- Validate command ----
84
96
  validate_parser = subparsers.add_parser(
85
97
  'validate', help='Validate test configuration without running tests'
@@ -299,6 +311,13 @@ def run_compare(args):
299
311
  return bool(exit_code == 0)
300
312
 
301
313
 
314
+ def run_tui(args):
315
+ """Launch the TUI manager."""
316
+ from .tui.app import run_tui as _run_tui
317
+
318
+ _run_tui(args.config_file, args.workspace)
319
+
320
+
302
321
  def run_validate(args):
303
322
  """Validate test configuration without running tests."""
304
323
  from .config.config_io import validate_config
@@ -348,6 +367,8 @@ def main():
348
367
  if args.command == 'run':
349
368
  success = run_tests(args)
350
369
  sys.exit(0 if success else 1)
370
+ elif args.command == 'tui':
371
+ run_tui(args)
351
372
  elif args.command == 'validate':
352
373
  success = run_validate(args)
353
374
  sys.exit(0 if success else 1)
@@ -80,37 +80,51 @@ def _split_and_resolve(
80
80
 
81
81
  def parse_test_cases(
82
82
  config: Dict[str, Any],
83
- workspace: Path,
84
- path_resolver: Any,
83
+ workspace: Optional[Path] = None,
84
+ path_resolver: Any = None,
85
85
  ) -> List[TestCase]:
86
86
  """Parse ``config['test_cases']`` into a list of ``TestCase`` objects.
87
87
 
88
88
  Supports both single-command mode and sequence (``steps``) mode.
89
+
90
+ When *workspace* and *path_resolver* are provided (Runner mode),
91
+ required fields are validated and command/args paths are resolved.
92
+ When omitted (TUI mode), missing fields get sensible defaults and
93
+ raw values are kept as-is for display purposes.
89
94
  """
90
95
  cases: List[TestCase] = []
96
+ resolve = workspace is not None and path_resolver is not None
91
97
 
92
- for case in config["test_cases"]:
98
+ for case in config.get("test_cases", []):
93
99
  if "steps" in case:
94
100
  # ── Sequence mode ──
95
101
  steps: List[TestCaseStep] = []
96
- for step in case["steps"]:
97
- step_required = ["command", "args", "expected"]
98
- if not all(field in step for field in step_required):
99
- raise ValueError(
100
- f"Step in test case '{case.get('name', 'unnamed')}' "
101
- f"is missing required fields"
102
+ for step in case.get("steps", []):
103
+ if resolve:
104
+ step_required = ["command", "args", "expected"]
105
+ if not all(field in step for field in step_required):
106
+ raise ValueError(
107
+ f"Step in test case '{case.get('name', 'unnamed')}' "
108
+ f"is missing required fields"
109
+ )
110
+ executable, resolved_args = _split_and_resolve(
111
+ step["command"], step["args"], workspace, path_resolver
102
112
  )
103
- executable, resolved_args = _split_and_resolve(
104
- step["command"], step["args"], workspace, path_resolver
105
- )
106
- steps.append(TestCaseStep(
107
- command=executable,
108
- args=resolved_args,
109
- expected=step["expected"],
110
- timeout=step.get("timeout"),
111
- ))
113
+ steps.append(TestCaseStep(
114
+ command=executable,
115
+ args=resolved_args,
116
+ expected=step["expected"],
117
+ timeout=step.get("timeout"),
118
+ ))
119
+ else:
120
+ steps.append(TestCaseStep(
121
+ command=step.get("command", ""),
122
+ args=step.get("args", []),
123
+ expected=step.get("expected", {}),
124
+ timeout=step.get("timeout"),
125
+ ))
112
126
  cases.append(TestCase(
113
- name=case["name"],
127
+ name=case.get("name", ""),
114
128
  steps=steps,
115
129
  description=case.get("description", ""),
116
130
  resources=case.get("resources"),
@@ -118,26 +132,37 @@ def parse_test_cases(
118
132
  ))
119
133
  else:
120
134
  # ── Single-command mode (backward-compatible) ──
121
- required_fields = ["name", "command", "args", "expected"]
122
- if not all(field in case for field in required_fields):
123
- raise ValueError(
124
- f"Test case {case.get('name', 'unnamed')} "
125
- f"is missing required fields"
135
+ if resolve:
136
+ required_fields = ["name", "command", "args", "expected"]
137
+ if not all(field in case for field in required_fields):
138
+ raise ValueError(
139
+ f"Test case {case.get('name', 'unnamed')} "
140
+ f"is missing required fields"
141
+ )
142
+ executable, resolved_args = _split_and_resolve(
143
+ case["command"], case["args"], workspace, path_resolver
126
144
  )
127
-
128
- executable, resolved_args = _split_and_resolve(
129
- case["command"], case["args"], workspace, path_resolver
130
- )
131
- cases.append(TestCase(
132
- name=case["name"],
133
- command=executable,
134
- args=resolved_args,
135
- expected=case["expected"],
136
- description=case.get("description", ""),
137
- timeout=case.get("timeout"),
138
- resources=case.get("resources"),
139
- tags=case.get("tags", []),
140
- ))
145
+ cases.append(TestCase(
146
+ name=case["name"],
147
+ command=executable,
148
+ args=resolved_args,
149
+ expected=case["expected"],
150
+ description=case.get("description", ""),
151
+ timeout=case.get("timeout"),
152
+ resources=case.get("resources"),
153
+ tags=case.get("tags", []),
154
+ ))
155
+ else:
156
+ cases.append(TestCase(
157
+ name=case.get("name", ""),
158
+ command=case.get("command", ""),
159
+ args=case.get("args", []),
160
+ expected=case.get("expected", {}),
161
+ description=case.get("description", ""),
162
+ timeout=case.get("timeout"),
163
+ resources=case.get("resources"),
164
+ tags=case.get("tags", []),
165
+ ))
141
166
 
142
167
  return cases
143
168
 
@@ -44,4 +44,20 @@ class TestCase:
44
44
  }
45
45
  for s in self.steps
46
46
  ]
47
- return result
47
+ return result
48
+
49
+ def to_execution_dict(self) -> Dict[str, Any]:
50
+ """Convert to the dict format expected by ``execute_single_test_case``.
51
+
52
+ Only for single-command mode; sequence cases should use
53
+ ``execute_sequence()`` instead.
54
+ """
55
+ return {
56
+ "name": self.name,
57
+ "command": self.command,
58
+ "args": self.args,
59
+ "expected": self.expected,
60
+ "description": self.description or None,
61
+ "timeout": self.timeout,
62
+ "resources": self.resources,
63
+ }
@@ -67,15 +67,7 @@ class ConfigRunner(BaseRunner):
67
67
  if case.steps:
68
68
  return self._run_sequence(case)
69
69
 
70
- case_data: TestCaseData = {
71
- "name": case.name,
72
- "command": case.command,
73
- "args": case.args,
74
- "expected": case.expected,
75
- "description": case.description or None,
76
- "timeout": case.timeout,
77
- "resources": case.resources,
78
- }
70
+ case_data = case.to_execution_dict()
79
71
 
80
72
  command_preview = (
81
73
  f"{case_data['command']} {' '.join(case_data['args'])}".strip()
@@ -226,15 +226,7 @@ class ParallelConfigRunner(ParallelRunner):
226
226
  if case.steps:
227
227
  result = self._run_sequence(case)
228
228
  else:
229
- case_data: TestCaseData = {
230
- "name": case.name,
231
- "command": case.command,
232
- "args": case.args,
233
- "expected": case.expected,
234
- "description": case.description or None,
235
- "timeout": case.timeout,
236
- "resources": case.resources,
237
- }
229
+ case_data = case.to_execution_dict()
238
230
 
239
231
  command_preview = (
240
232
  f"{case_data['command']} {' '.join(case_data['args'])}".strip()
@@ -0,0 +1,11 @@
1
+ """
2
+ TUI Manager for interactive test case CRUD.
3
+
4
+ Provides a Textual-based terminal UI for browsing, editing and running
5
+ test cases. Relies on :mod:`cli_test_framework.config.config_io` for
6
+ configuration I/O and :mod:`cli_test_framework.core` for execution.
7
+
8
+ Usage::
9
+
10
+ cli-test tui test_cases.json
11
+ """
@@ -0,0 +1,78 @@
1
+ """Textual App entry point for the TUI manager.
2
+
3
+ Usage::
4
+
5
+ from cli_test_framework.tui.app import run_tui
6
+ run_tui("test_cases.json", workspace="/path/to/project")
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ from textual.app import App, ComposeResult
15
+ from textual.binding import Binding
16
+
17
+ from .controllers.case_controller import CaseController
18
+ from .screens.case_list import CaseListScreen
19
+
20
+
21
+ class CaseManagerApp(App):
22
+ """Terminal UI for browsing / editing / running test cases."""
23
+
24
+ TITLE = "CLI Test Framework - Case Manager"
25
+
26
+ BINDINGS = [
27
+ Binding("ctrl+q", "quit", "Quit", show=True, priority=True),
28
+ Binding("q", "quit", "Quit", show=True, priority=True),
29
+ ]
30
+
31
+ CSS = """
32
+ Screen {
33
+ background: $surface;
34
+ }
35
+ """
36
+
37
+ def __init__(self, config_file: str, workspace: Optional[str] = None):
38
+ super().__init__()
39
+ self._config_file = config_file
40
+ self._workspace = workspace
41
+ self._controller = CaseController()
42
+
43
+ def on_mount(self) -> None:
44
+ try:
45
+ count = self._controller.load(self._config_file, self._workspace)
46
+ self.notify(f"Loaded {count} test cases from {Path(self._config_file).name}")
47
+ except Exception as e:
48
+ self.notify(f"Failed to load config: {e}", severity="error")
49
+
50
+ self.push_screen(CaseListScreen(self._controller))
51
+
52
+
53
+ # ---------------------------------------------------------------------------
54
+ # Public entry point
55
+ # ---------------------------------------------------------------------------
56
+
57
+
58
+ def run_tui(config_file: str, workspace: Optional[str] = None) -> None:
59
+ """Launch the TUI manager.
60
+
61
+ Safe to call without installing textual — raises :exc:`ImportError`
62
+ with a friendly message if textual is not available.
63
+ """
64
+ try:
65
+ import textual # noqa: F401 — verify textual is importable
66
+ except ImportError:
67
+ import sys
68
+
69
+ print(
70
+ "TUI 功能需要安装 textual。请运行:\n"
71
+ " pip install cli-test-framework[tui]\n"
72
+ "或:\n"
73
+ " pip install textual"
74
+ )
75
+ sys.exit(1)
76
+
77
+ app = CaseManagerApp(config_file, workspace)
78
+ app.run()