cucu 1.0.2__tar.gz → 1.0.7__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.
- {cucu-1.0.2 → cucu-1.0.7}/.circleci/config.yml +11 -4
- {cucu-1.0.2 → cucu-1.0.7}/.github/workflows/build.yml +1 -1
- {cucu-1.0.2 → cucu-1.0.7}/CHANGELOG.md +51 -31
- {cucu-1.0.2 → cucu-1.0.7}/PKG-INFO +4 -4
- cucu-1.0.7/data/www/drag_drop.html +89 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/file_upload.feature +6 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/report_basics.feature +1 -1
- {cucu-1.0.2 → cucu-1.0.7}/pyproject.toml +3 -3
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/cli/core.py +42 -11
- cucu-1.0.7/src/cucu/steps/file_input_steps.py +184 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/webserver_steps.py +5 -1
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/utils.py +4 -0
- {cucu-1.0.2 → cucu-1.0.7}/uv.lock +73 -66
- cucu-1.0.2/src/cucu/steps/file_input_steps.py +0 -80
- {cucu-1.0.2 → cucu-1.0.7}/.env +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.github/CODEOWNERS +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.github/workflows/create_a_new_release.yml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.github/workflows/publish-production.yml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.github/workflows/publish-test.yml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.gitignore +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.pre-commit-config.yaml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/.python-version +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/CODE_OF_CONDUCT.md +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/CONTRIBUTING.md +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/CONTRIBUTORS.md +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/LICENSE +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/Makefile +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/README.md +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/bin/cucu +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/bin/start_seleniarm_hub.sh +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/bin/start_selenium_hub.sh +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/cortex.yaml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/custom_step_test/custom_step.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/custom_step_test/steps/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/custom_step_test/steps/cucu_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/echo.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/environment.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_skipping_scenarios_by_file_presence.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_background.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_background_using_substeps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_failing_scenario.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_failing_scenario_with_table.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_failing_scenario_with_web.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_logging.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_long_names.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_mixed_results.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_multilines_and_tables.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_passing_scenario.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_passing_scenario_with_web.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_platform_specific_scenarios.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_scenario_outline.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_scenario_using_nth_type.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_tagging.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/feature_with_xml_tags.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/multiple_scenarios_with_browser_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/scenario_stop_retry_early.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/scenario_that_fails_with_after_scenario_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/scenario_that_passes_with_after_scenario_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/scenario_with_console_logs.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/scenario_with_substeps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/scenario_with_substeps_and_logging.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/slow_features/slow_feature1.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/slow_features/slow_feature2.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/slow_features/slow_feature3.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/slow_features/slow_feature4.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/slow_features/slow_feature5.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/steps/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/steps/cucu_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/tagged_features/tagged_feature1.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/tagged_features/tagged_feature2.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/tagged_features/tagged_feature3.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/with_secret/cucurc.yml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/features/with_secret/scenario_with_comments.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/unit/ansi.log +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/unit/ansi.log.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/broken_images.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/buttons.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/checkboxes.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/clipboard.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/comboboxes.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/console_logging.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/cookies.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/counter.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/draggables.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/dropdowns.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/duplicate_buttons.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/duplicate_inputs.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/dynamic_dropdown.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/file.txt +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/files.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/frames.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/fuzzy_rules.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/images.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/inputs.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/js/util.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/links.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/nothing_to_see_here.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/page_that_loads_forever.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/radio_buttons.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/stars.png +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/tables.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/tabs.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/data/www/text.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/browser_tab_managemenet.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/browser_window_managemenet.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/buttons.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/checkboxes.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/clipboard.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/comboboxes.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/console_logs.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/cookies.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/draggables.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/dropdowns.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/duplicate_buttons.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/duplicate_draggables.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/duplicate_inputs.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/file_downloads.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/frames.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/fuzzy_rules.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/helpers.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/images.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/inputs.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/inputs_limits.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/links.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/mht.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/page_checks.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/radio_buttons.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/tables.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/tabs.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/browser/text.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/config.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/help.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/internals.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/ipdb.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/lint.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/lsp.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/report_search_and_sorting.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/report_with_custom_failure_handler.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/report_with_custom_subheader.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/report_with_custom_tags.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_outputs.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_hooks.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_junit.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_logging.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_report.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_tags.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_thread_dumper.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/run_with_workers.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/secrets.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/cli/vars.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/environment.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/before_retry_handlers.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/repeating_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/run_after_scenario_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/run_and_measure.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/run_other_features.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/run_scenarios_conditionally.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/run_steps_conditionally.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/run_steps_on_element_visibilty.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/flow_control/wait_for_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/os/command_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/os/file_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/os/platform_steps.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/os/screenshot.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/os/variables.feature +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/steps/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/features/steps/cucu_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/logo.png +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/renovate.json +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/sonar-project.properties +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/ansi_parser.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/behave_tweaks.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/browser/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/browser/core.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/browser/frames.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/browser/selenium.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/browser/selenium_tweaks.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/cli/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/cli/run.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/cli/steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/cli/thread_dumper.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/config.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/edgedriver_autoinstaller/README.md +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/edgedriver_autoinstaller/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/edgedriver_autoinstaller/utils.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/environment.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/external/jquery/jquery-3.5.1.min.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/formatter/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/formatter/cucu.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/formatter/json.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/formatter/junit.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/fuzzy/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/fuzzy/core.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/fuzzy/fuzzy.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/helpers.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/hooks.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/language_server/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/language_server/core.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/lint/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/lint/linter.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/lint/rules/format.yaml +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/logger.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/matcher/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/matcher/core.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/page_checks.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/bootstrap.min.css +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/bootstrap.min.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/dataTables.bootstrap.min.css +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/dataTables.bootstrap.min.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/jquery-3.5.1.min.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/jquery.dataTables.min.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/external/popper.min.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/favicon.png +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/html.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/templates/feature.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/templates/flat.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/templates/index.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/templates/layout.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/reporter/templates/scenario.html +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/base_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/browser_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/button_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/checkbox_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/command_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/comment_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/draggable_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/dropdown_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/filesystem_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/flow_control_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/image_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/input_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/link_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/menuitem_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/platform_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/radio_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/step_utils.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/tab_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/table_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/tables.js +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/text_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/src/cucu/steps/variable_steps.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/__init__.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_behave_tweaks.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_browser.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_config.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_core.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_language_server.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_reporter_parser.py +0 -0
- {cucu-1.0.2 → cucu-1.0.7}/tests/test_table_steps.py +0 -0
|
@@ -51,9 +51,14 @@ jobs:
|
|
|
51
51
|
default: "chrome"
|
|
52
52
|
docker:
|
|
53
53
|
- image: cimg/python:3.12
|
|
54
|
-
- image: selenium/standalone-<<parameters.browser>>:124.0
|
|
55
54
|
environment:
|
|
55
|
+
# Starting with python 3.12 coverage could be 2x slower so use experimental COVERAGE_CORE=sysmon
|
|
56
|
+
COVERAGE_CORE: sysmon
|
|
57
|
+
- image: selenium/standalone-<<parameters.browser>>:125.0
|
|
58
|
+
environment:
|
|
59
|
+
SE_ENABLE_TRACING: false
|
|
56
60
|
SE_NODE_MAX_SESSIONS: 12
|
|
61
|
+
SE_NODE_OVERRIDE_MAX_SESSIONS: true
|
|
57
62
|
SE_NODE_SESSION_TIMEOUT: 300
|
|
58
63
|
SCREEN_WIDTH: 1366
|
|
59
64
|
SCREEN_HEIGHT: 768
|
|
@@ -77,7 +82,7 @@ jobs:
|
|
|
77
82
|
command: curl --retry 60 --retry-delay 5 --retry-connrefused http://localhost:4444
|
|
78
83
|
- run:
|
|
79
84
|
name: run_functional_tests
|
|
80
|
-
command: uv run cucu run features --workers
|
|
85
|
+
command: uv run cucu run features --workers 8 --selenium-remote-url http://localhost:4444 --generate-report --junit junit_results --browser "<<parameters.browser>>"
|
|
81
86
|
environment:
|
|
82
87
|
COVERAGE_PROCESS_START: pyproject.toml # set to config file
|
|
83
88
|
- run:
|
|
@@ -125,12 +130,14 @@ orbs:
|
|
|
125
130
|
- run:
|
|
126
131
|
name: Install uv
|
|
127
132
|
command: |
|
|
133
|
+
echo 'export UV_INSTALL_DIR=$HOME/.uv' >> $BASH_ENV
|
|
134
|
+
source $BASH_ENV
|
|
128
135
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
129
|
-
echo 'source $
|
|
136
|
+
echo 'source $UV_INSTALL_DIR/env' >> $BASH_ENV
|
|
130
137
|
source $BASH_ENV
|
|
131
138
|
- save_cache:
|
|
132
139
|
paths:
|
|
133
|
-
- $
|
|
140
|
+
- $UV_INSTALL_DIR/env
|
|
134
141
|
key: v1-uv-cache-{{ checksum "uv.lock" }}
|
|
135
142
|
- run:
|
|
136
143
|
name: Verify uv installation
|
|
@@ -5,62 +5,82 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project closely adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 1.0.7
|
|
9
|
+
- Fix - stablize CI runs (increase workers, add envvar tweaks)
|
|
10
|
+
- Fix - parse --workers correctly
|
|
11
|
+
- Fix - Ctrl-C to terminate for multi-workers
|
|
12
|
+
- Fix - ensure testing webserver is online
|
|
13
|
+
- Change - use fork when not MacOS for multi-worker
|
|
14
|
+
- Chore - bump jellyfish to 1.1.3
|
|
15
|
+
|
|
16
|
+
## 1.0.6
|
|
17
|
+
- Chore - relax required versions for requests library
|
|
18
|
+
|
|
19
|
+
## 1.0.5
|
|
20
|
+
- Fix - Resolved issues with the drag-and-drop feature to ensure compatibility with all input options.
|
|
21
|
+
|
|
22
|
+
## 1.0.4
|
|
23
|
+
- Change - for retry steps, also log before and after call messages
|
|
24
|
+
|
|
25
|
+
## 1.0.3
|
|
26
|
+
- Add - functional test for drag and drop file functionality
|
|
27
|
+
|
|
8
28
|
## 1.0.2
|
|
9
|
-
-
|
|
29
|
+
- Chore - fix logo for pypi
|
|
10
30
|
|
|
11
31
|
## 1.0.1
|
|
12
|
-
-
|
|
13
|
-
-
|
|
32
|
+
- Chore - fix pyproject.toml authors and urls
|
|
33
|
+
- Change - add pre-commit to makefile
|
|
14
34
|
|
|
15
35
|
## 1.0.0
|
|
16
|
-
-
|
|
17
|
-
-
|
|
36
|
+
- Change to 1.0.0!
|
|
37
|
+
- Chore - fix GH workflow for test publish
|
|
18
38
|
|
|
19
39
|
## 0.207.0
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
40
|
+
- Chore - prep for publish - p2
|
|
41
|
+
- Replace poetry with uv
|
|
42
|
+
- Set default local python to 3.12
|
|
43
|
+
- Run pre-commit with uv
|
|
44
|
+
- Fix isort with ruff
|
|
25
45
|
|
|
26
46
|
## 0.206.0
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
47
|
+
- Chore - prep for publish
|
|
48
|
+
- Replace black with ruff
|
|
49
|
+
- Remove detect-secrets to be replaced by GH repo security
|
|
50
|
+
- Remove bandit to be replaced by GH CodeQL
|
|
51
|
+
- Cleanup makefile
|
|
32
52
|
|
|
33
53
|
## 0.205.0
|
|
34
|
-
-
|
|
54
|
+
- Change - increase information logged for failing before_scenario_hooks
|
|
35
55
|
|
|
36
56
|
## 0.204.0
|
|
37
|
-
-
|
|
38
|
-
-
|
|
57
|
+
- Chore - add gh workflows for publishing
|
|
58
|
+
- Chore - fix project metadata
|
|
39
59
|
|
|
40
60
|
## 0.203.0
|
|
41
|
-
-
|
|
42
|
-
-
|
|
61
|
+
- Chore - move repo GH org locations
|
|
62
|
+
- Chore - remove sonarqube
|
|
43
63
|
|
|
44
64
|
## 0.202.0
|
|
45
|
-
-
|
|
65
|
+
- Add - wait step to saving values from any table cells to a variable
|
|
46
66
|
|
|
47
67
|
## 0.201.0
|
|
48
|
-
-
|
|
68
|
+
- Change - add report path to the JUnit files when available
|
|
49
69
|
|
|
50
70
|
## 0.200.0
|
|
51
|
-
-
|
|
71
|
+
- Change - increase the log level of scheduling a feature file
|
|
52
72
|
|
|
53
73
|
## 0.199.0
|
|
54
|
-
-
|
|
55
|
-
-
|
|
74
|
+
- Chore - remove deprecated CI
|
|
75
|
+
- Chore - add PR template
|
|
56
76
|
|
|
57
77
|
## 0.198.0
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
78
|
+
- Chore - add sonarqube
|
|
79
|
+
- Chore - refactor circle config (config-drive executor, embed commands, move workflows up)
|
|
80
|
+
- Chore - configure renovate
|
|
81
|
+
- Chore - remove dependabot config (duplicates renovate)
|
|
82
|
+
- Chore - remove safety (duplicates sonarqube)
|
|
83
|
+
- Change - swap pebble for mpire, kill procs on run timeout
|
|
64
84
|
|
|
65
85
|
## 0.197.0
|
|
66
86
|
- Fix - before all hooks are now executed
|
|
@@ -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
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Drag and Drop File Upload Test with Validation</title>
|
|
7
|
+
<style>
|
|
8
|
+
#drop-zone {
|
|
9
|
+
width: 500px;
|
|
10
|
+
height: 100px;
|
|
11
|
+
border: 2px dashed #0088cc;
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
align-items: center;
|
|
15
|
+
margin: 20px auto;
|
|
16
|
+
font-family: Arial, sans-serif;
|
|
17
|
+
color: #0088cc;
|
|
18
|
+
background-color: #f9f9f9;
|
|
19
|
+
}
|
|
20
|
+
#drop-zone.hover {
|
|
21
|
+
background-color: #f0f0f0;
|
|
22
|
+
}
|
|
23
|
+
#message {
|
|
24
|
+
text-align: center;
|
|
25
|
+
margin-top: 20px;
|
|
26
|
+
font-family: Arial, sans-serif;
|
|
27
|
+
color: #ff0000;
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
|
|
33
|
+
<h2>Drag and Drop File Upload Test</h2>
|
|
34
|
+
|
|
35
|
+
<!-- Drag and Drop Zone -->
|
|
36
|
+
<div id="drop-zone">
|
|
37
|
+
Click or drag file to this area to upload
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<input type="file" id="myFile" style="display: none;">
|
|
41
|
+
|
|
42
|
+
<!-- Validation Message Area -->
|
|
43
|
+
<div id="message"></div>
|
|
44
|
+
|
|
45
|
+
<script>
|
|
46
|
+
const dropZone = document.getElementById('drop-zone');
|
|
47
|
+
const fileInput = document.getElementById('myFile');
|
|
48
|
+
const messageDiv = document.getElementById('message');
|
|
49
|
+
|
|
50
|
+
// Function to display validation messages
|
|
51
|
+
function showMessage(message, isError = false) {
|
|
52
|
+
messageDiv.textContent = message;
|
|
53
|
+
messageDiv.style.color = isError ? '#ff0000' : '#008000'; // Red for errors, green for success
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Trigger file input click when the drop zone is clicked
|
|
57
|
+
dropZone.addEventListener('click', function() {
|
|
58
|
+
fileInput.click();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Handle drag over
|
|
62
|
+
dropZone.addEventListener('dragover', function(e) {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
dropZone.classList.add('hover');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Handle drag leave
|
|
68
|
+
dropZone.addEventListener('dragleave', function() {
|
|
69
|
+
dropZone.classList.remove('hover');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Handle file drop
|
|
73
|
+
dropZone.addEventListener('drop', function(e) {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
dropZone.classList.remove('hover');
|
|
76
|
+
const files = e.dataTransfer.files;
|
|
77
|
+
|
|
78
|
+
if (files.length > 0) {
|
|
79
|
+
showMessage(`File dropped: ${files[0].name}`, false);
|
|
80
|
+
fileInput.files = files;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
showMessage('No file was dropped.', true);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
</body>
|
|
89
|
+
</html>
|
|
@@ -9,3 +9,9 @@ Feature: File upload
|
|
|
9
9
|
And I click the button "upload!"
|
|
10
10
|
And I save the current url to the variable "URL"
|
|
11
11
|
Then I should see "{URL}" matches ".*\?filename=file_upload.feature"
|
|
12
|
+
|
|
13
|
+
Scenario: User can drag and drop to upload a file
|
|
14
|
+
Given I start a webserver at directory "data/www" and save the port to the variable "PORT"
|
|
15
|
+
And I open a browser at the url "http://{HOST_ADDRESS}:{PORT}/drag_drop.html"
|
|
16
|
+
When I wait to drag and drop the file "features/browser/file_upload.feature" to "Click or drag file to this area to upload"
|
|
17
|
+
Then I wait to see the text "File dropped: file_upload.feature"
|
|
@@ -204,7 +204,7 @@ Feature: Report basics
|
|
|
204
204
|
@runtime-timeout
|
|
205
205
|
Scenario: User can run with a runtime timeout and still produce a valid report
|
|
206
206
|
Given I run the command "cucu run data/features/slow_features --runtime-timeout 9 --results {CUCU_RESULTS_DIR}/runtime_timeout_reporting_results" and expect exit code "1"
|
|
207
|
-
Then I should see the previous step took less than "
|
|
207
|
+
Then I should see the previous step took less than "14" seconds
|
|
208
208
|
When I run the command "cucu report {CUCU_RESULTS_DIR}/runtime_timeout_reporting_results --output {CUCU_RESULTS_DIR}/runtime_timeout_reporting_report" and expect exit code "0"
|
|
209
209
|
And I start a webserver at directory "{CUCU_RESULTS_DIR}/runtime_timeout_reporting_report/" and save the port to the variable "PORT"
|
|
210
210
|
And I open a browser at the url "http://{HOST_ADDRESS}:{PORT}/index.html"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cucu"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.7"
|
|
4
4
|
description = "Easy BDD web testing"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "The Clear BSD License" }
|
|
@@ -51,14 +51,14 @@ dependencies = [
|
|
|
51
51
|
"humanize~=4.8.0",
|
|
52
52
|
"importlib-metadata~=8.0.0",
|
|
53
53
|
"ipdb~=0.13.13",
|
|
54
|
-
"jellyfish~=1.
|
|
54
|
+
"jellyfish~=1.1.3",
|
|
55
55
|
"jinja2~=3.1.3",
|
|
56
56
|
"lsprotocol~=2023.0.1",
|
|
57
57
|
"mpire~=2.10.2",
|
|
58
58
|
"psutil~=6.0.0",
|
|
59
59
|
"pygls~=1.3.1",
|
|
60
60
|
"pyyaml~=6.0.1",
|
|
61
|
-
"requests
|
|
61
|
+
"requests>=2.31.0,<3.0.0",
|
|
62
62
|
"selenium~=4.15",
|
|
63
63
|
"tabulate~=0.9.0",
|
|
64
64
|
"tenacity~=9.0.0",
|
|
@@ -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
|
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import humanize
|
|
4
|
+
|
|
5
|
+
from cucu import fuzzy, logger, retry, step
|
|
6
|
+
from cucu.utils import take_saw_element_screenshot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def find_file_input(ctx, name, index=0):
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
* <input type="file">
|
|
13
|
+
|
|
14
|
+
parameters:
|
|
15
|
+
ctx(object): behave context object used to share data between steps
|
|
16
|
+
name(str): name that identifies the desired button on screen
|
|
17
|
+
index(str): the index of the button if there are duplicates
|
|
18
|
+
|
|
19
|
+
returns:
|
|
20
|
+
the WebElement that matches the provided arguments.
|
|
21
|
+
"""
|
|
22
|
+
ctx.check_browser_initialized()
|
|
23
|
+
element = fuzzy.find(
|
|
24
|
+
ctx.browser, name, ['input[type="file"]'], index=index
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
prefix = "" if index == 0 else f"{humanize.ordinal(index)} "
|
|
28
|
+
|
|
29
|
+
take_saw_element_screenshot(ctx, "file input", name, index, element)
|
|
30
|
+
|
|
31
|
+
if element is None:
|
|
32
|
+
raise RuntimeError(f'unable to find the {prefix}file input "{name}"')
|
|
33
|
+
|
|
34
|
+
return element
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@step('I upload the file "{filepath}" to the file input "{name}"')
|
|
38
|
+
def upload_file_to_input(ctx, filepath, name):
|
|
39
|
+
_input = find_file_input(ctx, name)
|
|
40
|
+
_input.send_keys(os.path.abspath(filepath))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
JS_DROP_FILE = """
|
|
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() {},
|
|
91
|
+
};
|
|
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 */
|
|
163
|
+
return input;
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def drag_and_drop_file(ctx, name, filepath):
|
|
168
|
+
drop_target = fuzzy.find(ctx.browser, name, ["*"])
|
|
169
|
+
drop_target_html = drop_target.get_attribute("outerHTML")
|
|
170
|
+
logger.debug(
|
|
171
|
+
f'looked for drag & drop target "{name}" and found "{drop_target_html}"'
|
|
172
|
+
)
|
|
173
|
+
file_input = ctx.browser.execute(JS_DROP_FILE, drop_target, 0, 0)
|
|
174
|
+
file_input.send_keys(os.path.abspath(filepath))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@step('I drag and drop the file "{filepath}" to "{name}"')
|
|
178
|
+
def should_drag_and_drop_file(ctx, filepath, name):
|
|
179
|
+
drag_and_drop_file(ctx, name, filepath)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@step('I wait to drag and drop the file "{filepath}" to "{name}"')
|
|
183
|
+
def wait_to_drag_and_drop_file(ctx, filepath, name):
|
|
184
|
+
retry(drag_and_drop_file)(ctx, name, filepath)
|
|
@@ -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()
|
|
@@ -11,6 +11,8 @@ import shutil
|
|
|
11
11
|
import humanize
|
|
12
12
|
from tabulate import DataRow, TableFormat, tabulate
|
|
13
13
|
from tenacity import (
|
|
14
|
+
after_log,
|
|
15
|
+
before_log,
|
|
14
16
|
before_sleep_log,
|
|
15
17
|
retry_if_not_exception_type,
|
|
16
18
|
stop_after_delay,
|
|
@@ -119,6 +121,8 @@ def retry(func, wait_up_to_s=None, retry_after_s=None):
|
|
|
119
121
|
stop=stop_after_delay(wait_up_to_s),
|
|
120
122
|
wait=wait_fixed(retry_after_s),
|
|
121
123
|
retry=retry_if_not_exception_type(StopRetryException),
|
|
124
|
+
before=before_log(logger, logging.DEBUG),
|
|
125
|
+
after=after_log(logger, logging.DEBUG),
|
|
122
126
|
before_sleep=before_sleep_log(logger, logging.DEBUG),
|
|
123
127
|
)
|
|
124
128
|
def new_decorator(*args, **kwargs):
|