Repository: seleniumbase/SeleniumBase Branch: master Commit: 72bdaf1a69f2 Files: 686 Total size: 4.5 MB Directory structure: gitextract_chj3u5ci/ ├── .dockerignore ├── .github/ │ ├── FUNDING.yml │ ├── Workflows.md │ └── workflows/ │ ├── pages.yml │ ├── python-nightly-mac.yml │ ├── python-nightly-ubuntu.yml │ ├── python-nightly-windows.yml │ └── python-package.yml ├── .gitignore ├── CHANGELOG.md ├── CNAME ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README.md ├── SECURITY.md ├── _config.yml ├── azure-pipelines.yml ├── examples/ │ ├── ReadMe.md │ ├── __init__.py │ ├── basic_test.py │ ├── behave_bdd/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── behave.ini │ │ └── features/ │ │ ├── __init__.py │ │ ├── behave.ini │ │ ├── calculator.feature │ │ ├── environment.py │ │ ├── fail_page.feature │ │ ├── login_app.feature │ │ ├── realworld.feature │ │ ├── steps/ │ │ │ ├── __init__.py │ │ │ ├── calculator.py │ │ │ ├── fail_page.py │ │ │ ├── imported.py │ │ │ └── swag_labs.py │ │ └── swag_labs.feature │ ├── boilerplates/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── base_test_case.py │ │ ├── boilerplate_test.py │ │ ├── classic_obj_test.py │ │ ├── page_objects.py │ │ ├── samples/ │ │ │ ├── __init__.py │ │ │ ├── file_parsing/ │ │ │ │ ├── __init__.py │ │ │ │ ├── parse_files.py │ │ │ │ ├── qa_login_example.txt │ │ │ │ └── staging_login_example.txt │ │ │ ├── google_objects.py │ │ │ ├── google_test.py │ │ │ ├── sb_swag_test.py │ │ │ ├── swag_labs_test.py │ │ │ └── test_page_objects.py │ │ └── sb_fixture_test.py │ ├── capabilities/ │ │ ├── ReadMe.md │ │ ├── mac_cap_file.py │ │ ├── sample_cap_file_BS.py │ │ ├── sample_cap_file_BS.yml │ │ ├── sample_cap_file_SL.py │ │ ├── selenoid_cap_file.py │ │ └── win10_cap_file.py │ ├── case_plans/ │ │ ├── basic_test.MyTestClass.test_basics.md │ │ ├── my_first_test.MyTestClass.test_swag_labs.md │ │ ├── shadow_root_test.ShadowRootTest.test_shadow_root.md │ │ ├── test_assert_elements.ListAssertTests.test_assert_list_of_elements.md │ │ ├── test_calculator.CalculatorTests.test_6_times_7_plus_12_equals_54.md │ │ ├── test_demo_site.DemoSiteTests.test_demo_site.md │ │ ├── test_login.SwagLabsLoginTests.test_swag_labs_login.md │ │ └── test_mfa_login.TestMFALogin.test_mfa_login.md │ ├── case_summary.md │ ├── cdp_mode/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── playwright/ │ │ │ ├── ReadMe.md │ │ │ ├── __init__.py │ │ │ ├── raw_basic_async.py │ │ │ ├── raw_basic_nested.py │ │ │ ├── raw_basic_sync.py │ │ │ ├── raw_bing_cap_async.py │ │ │ ├── raw_bing_cap_nested.py │ │ │ ├── raw_bing_cap_sync.py │ │ │ ├── raw_cf_cap_sync.py │ │ │ ├── raw_copilot_async.py │ │ │ ├── raw_copilot_nested.py │ │ │ ├── raw_copilot_sync.py │ │ │ ├── raw_footlocker_sync.py │ │ │ ├── raw_gas_info_async.py │ │ │ ├── raw_gas_info_sync.py │ │ │ ├── raw_gitlab_async.py │ │ │ ├── raw_gitlab_nested.py │ │ │ ├── raw_gitlab_sync.py │ │ │ ├── raw_idealista_nested.py │ │ │ ├── raw_nike_sync.py │ │ │ ├── raw_nordstrom_sync.py │ │ │ ├── raw_planetmc_sync.py │ │ │ ├── raw_reddit_sync.py │ │ │ ├── raw_seatgeek_sync.py │ │ │ └── raw_walmart_sync.py │ │ ├── raw_ad_blocking.py │ │ ├── raw_ahrefs.py │ │ ├── raw_albertsons.py │ │ ├── raw_amazon.py │ │ ├── raw_antibot.py │ │ ├── raw_async.py │ │ ├── raw_basic_async.py │ │ ├── raw_basic_cdp.py │ │ ├── raw_basic_mobile.py │ │ ├── raw_bestwestern.py │ │ ├── raw_browserscan.py │ │ ├── raw_canvas.py │ │ ├── raw_cdp.py │ │ ├── raw_cdp_copilot.py │ │ ├── raw_cdp_drivers.py │ │ ├── raw_cdp_extended.py │ │ ├── raw_cdp_gitlab.py │ │ ├── raw_cdp_hyatt.py │ │ ├── raw_cdp_login.py │ │ ├── raw_cdp_methods.py │ │ ├── raw_cdp_mobile.py │ │ ├── raw_cdp_nike.py │ │ ├── raw_cdp_nordstrom.py │ │ ├── raw_cdp_pixelscan.py │ │ ├── raw_cdp_recaptcha.py │ │ ├── raw_cdp_reddit.py │ │ ├── raw_cdp_shadow.py │ │ ├── raw_cdp_tabs.py │ │ ├── raw_cdp_turnstile.py │ │ ├── raw_cdp_walmart.py │ │ ├── raw_cdp_with_sb.py │ │ ├── raw_cf.py │ │ ├── raw_cf_captcha.py │ │ ├── raw_cf_clearance.py │ │ ├── raw_chatgpt.py │ │ ├── raw_consecutive_c.py │ │ ├── raw_cookies_async.py │ │ ├── raw_copilot.py │ │ ├── raw_demo_site.py │ │ ├── raw_drag_and_drop.py │ │ ├── raw_driver.py │ │ ├── raw_easyjet.py │ │ ├── raw_elal.py │ │ ├── raw_facebook.py │ │ ├── raw_fingerprint.py │ │ ├── raw_footlocker.py │ │ ├── raw_form_turnstile.py │ │ ├── raw_gas_records.py │ │ ├── raw_geolocation.py │ │ ├── raw_geolocation_sb.py │ │ ├── raw_gettyimages.py │ │ ├── raw_gitlab.py │ │ ├── raw_glassdoor.py │ │ ├── raw_handle_alerts.py │ │ ├── raw_homedepot.py │ │ ├── raw_hyatt.py │ │ ├── raw_idealista.py │ │ ├── raw_indeed.py │ │ ├── raw_indeed_login.py │ │ ├── raw_kohls.py │ │ ├── raw_linkedin.py │ │ ├── raw_mfa_login.py │ │ ├── raw_mobile_agents.py │ │ ├── raw_mobile_async.py │ │ ├── raw_mobile_gitlab.py │ │ ├── raw_mobile_roblox.py │ │ ├── raw_mouser.py │ │ ├── raw_multi_async.py │ │ ├── raw_multi_captcha.py │ │ ├── raw_multi_cdp.py │ │ ├── raw_mycdp_cookies.py │ │ ├── raw_nevada_search.py │ │ ├── raw_nike.py │ │ ├── raw_nordstrom.py │ │ ├── raw_pixelscan.py │ │ ├── raw_planetmc.py │ │ ├── raw_pokemon.py │ │ ├── raw_priceline.py │ │ ├── raw_print_to_pdf.py │ │ ├── raw_proxy.py │ │ ├── raw_publication.py │ │ ├── raw_radwell.py │ │ ├── raw_ralphlauren.py │ │ ├── raw_reddit.py │ │ ├── raw_reddit_async.py │ │ ├── raw_req_async.py │ │ ├── raw_req_mod.py │ │ ├── raw_req_sb.py │ │ ├── raw_res_nike.py │ │ ├── raw_res_sb.py │ │ ├── raw_reuse_browser.py │ │ ├── raw_science.py │ │ ├── raw_seatgeek.py │ │ ├── raw_socialblade.py │ │ ├── raw_softpedia.py │ │ ├── raw_southwest.py │ │ ├── raw_stopandshop.py │ │ ├── raw_tab_switching.py │ │ ├── raw_theaters.py │ │ ├── raw_tiktok.py │ │ ├── raw_timezone.py │ │ ├── raw_timezone_sb.py │ │ ├── raw_totalwine.py │ │ ├── raw_trails.py │ │ ├── raw_turnstile.py │ │ ├── raw_united.py │ │ ├── raw_walmart.py │ │ ├── raw_wsform.py │ │ ├── raw_xhr_async.py │ │ ├── raw_xhr_sb.py │ │ ├── raw_xpath.py │ │ └── raw_zoro.py │ ├── chart_maker/ │ │ ├── ReadMe.md │ │ ├── chart_presentation.py │ │ ├── my_chart.py │ │ ├── pie_charts.py │ │ ├── test_area_chart.py │ │ ├── test_display_chart.py │ │ ├── test_line_chart.py │ │ ├── test_multi_series.py │ │ └── test_save_chart.py │ ├── coffee_cart_tests.py │ ├── custom_settings.py │ ├── desktop_apps/ │ │ ├── ReadMe.md │ │ └── recorder.py │ ├── dialog_boxes/ │ │ ├── ReadMe.md │ │ └── dialog_box_tour.py │ ├── edge_test.py │ ├── example_config.cfg │ ├── example_logs/ │ │ ├── ReadMe.md │ │ ├── basic_test_info.txt │ │ └── page_source.html │ ├── github_test.py │ ├── gui_test_runner.py │ ├── hack_the_planet.py │ ├── handle_alert_test.py │ ├── iframe_tests.py │ ├── locale_code_test.py │ ├── master_qa/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── basic_masterqa_test_0.py │ │ ├── masterqa_test_1.py │ │ └── pytest.ini │ ├── migration/ │ │ ├── __init__.py │ │ ├── protractor/ │ │ │ ├── ReadMe.md │ │ │ ├── __init__.py │ │ │ ├── example_spec.js │ │ │ ├── example_test.py │ │ │ ├── input_spec.js │ │ │ ├── input_test.py │ │ │ ├── mat_paginator_spec.js │ │ │ └── mat_paginator_test.py │ │ └── raw_selenium/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── flaky_messy_raw.py │ │ ├── long_messy_raw.py │ │ ├── messy_raw.py │ │ ├── pytest.ini │ │ ├── refined_raw.py │ │ └── simple_sbase.py │ ├── multiple_cdp_drivers.py │ ├── my_first_test.py │ ├── nth_child_test.py │ ├── offline_examples/ │ │ ├── __init__.py │ │ ├── demo_page.html │ │ ├── load_html_test.py │ │ ├── test_demo_page.py │ │ ├── test_extended_driver.py │ │ ├── test_handle_alerts.py │ │ ├── test_request_fixture.py │ │ └── test_user_agent.py │ ├── old_wordle_script.py │ ├── parameterized_test.py │ ├── performance_test.py │ ├── presenter/ │ │ ├── ReadMe.md │ │ ├── core_presentation.py │ │ ├── edge_presentation.py │ │ ├── fundamentals.py │ │ ├── hacking_with_cdp.py │ │ ├── multi_uc.py │ │ ├── my_presentation.py │ │ ├── py_virtual_envs.py │ │ ├── uc_presentation.py │ │ ├── uc_presentation_3.py │ │ ├── uc_presentation_4.py │ │ └── web_scraping_on_gha.py │ ├── proxy_test.py │ ├── pytest.ini │ ├── rate_limiting_test.py │ ├── raw_ahrefs.py │ ├── raw_antibot_login.py │ ├── raw_bing_captcha.py │ ├── raw_block.py │ ├── raw_brotector_captcha.py │ ├── raw_call.py │ ├── raw_cdp_drivers.py │ ├── raw_cdp_logging.py │ ├── raw_cf.py │ ├── raw_cookies.py │ ├── raw_detection.py │ ├── raw_driver_context.py │ ├── raw_driver_manager.py │ ├── raw_file_call.py │ ├── raw_form_turnstile.py │ ├── raw_games.py │ ├── raw_gitlab.py │ ├── raw_google.py │ ├── raw_gui_click.py │ ├── raw_hobbit.py │ ├── raw_invisible_captcha.py │ ├── raw_login_context.py │ ├── raw_login_driver.py │ ├── raw_login_sb.py │ ├── raw_main_call.py │ ├── raw_mobile.py │ ├── raw_multi_drivers.py │ ├── raw_multi_sb.py │ ├── raw_no_context_mgr.py │ ├── raw_order_tickets.py │ ├── raw_parameter_script.py │ ├── raw_performance_logs.py │ ├── raw_pixelscan.py │ ├── raw_pyautogui.py │ ├── raw_recaptcha.py │ ├── raw_robot.py │ ├── raw_sb.py │ ├── raw_test_scripts.py │ ├── raw_turnstile.py │ ├── raw_uc_events.py │ ├── raw_uc_mode.py │ ├── sb_fixture_tests.py │ ├── setup.cfg │ ├── shadow_root_test.py │ ├── swag_labs_user_tests.py │ ├── test_3d_apis.py │ ├── test_apple_site.py │ ├── test_assert_elements.py │ ├── test_calculator.py │ ├── test_canvas.py │ ├── test_cdp_ad_blocking.py │ ├── test_checkboxes.py │ ├── test_chinese_pdf.py │ ├── test_chromedriver.py │ ├── test_coffee_cart.py │ ├── test_console_logging.py │ ├── test_contains_selector.py │ ├── test_cycle_elements.py │ ├── test_decryption.py │ ├── test_deferred_asserts.py │ ├── test_demo_site.py │ ├── test_detect_404s.py │ ├── test_docs_site.py │ ├── test_double_click.py │ ├── test_download_files.py │ ├── test_download_images.py │ ├── test_drag_and_drop.py │ ├── test_error_page.py │ ├── test_event_firing.py │ ├── test_fail.py │ ├── test_geolocation.py │ ├── test_get_coffee.py │ ├── test_get_locale_code.py │ ├── test_get_pdf_text.py │ ├── test_get_swag.py │ ├── test_get_user_agent.py │ ├── test_hack_search.py │ ├── test_highlight_elements.py │ ├── test_image_saving.py │ ├── test_inspect_html.py │ ├── test_login.py │ ├── test_markers.py │ ├── test_mfa_login.py │ ├── test_multiple_drivers.py │ ├── test_null.py │ ├── test_override_driver.py │ ├── test_override_sb_fixture.py │ ├── test_parse_soup.py │ ├── test_pdf_asserts.py │ ├── test_pytest_parametrize.py │ ├── test_repeat_tests.py │ ├── test_request_sb_fixture.py │ ├── test_roblox_mobile.py │ ├── test_save_screenshots.py │ ├── test_sb_fixture.py │ ├── test_scrape_bing.py │ ├── test_select_options.py │ ├── test_shadow_dom.py │ ├── test_show_file_choosers.py │ ├── test_simple_login.py │ ├── test_suite.py │ ├── test_swag_labs.py │ ├── test_tinymce.py │ ├── test_todomvc.py │ ├── test_url_asserts.py │ ├── test_usefixtures.py │ ├── test_verify_chromedriver.py │ ├── test_window_switching.py │ ├── test_xfail.py │ ├── test_xkcd.py │ ├── time_limit_test.py │ ├── tour_examples/ │ │ ├── ReadMe.md │ │ ├── bootstrap_google_tour.py │ │ ├── bootstrap_xkcd_tour.py │ │ ├── driverjs_maps_tour.py │ │ ├── google_tour.py │ │ ├── hopscotch_google_tour.py │ │ ├── introjs_google_tour.py │ │ ├── maps_introjs_tour.py │ │ ├── octocat_tour.py │ │ ├── shepherd_google_tour.py │ │ └── xkcd_tour.py │ ├── translations/ │ │ ├── ReadMe.md │ │ ├── chinese_test_1.py │ │ ├── dutch_test_1.py │ │ ├── english_test_1.py │ │ ├── french_test_1.py │ │ ├── italian_test_1.py │ │ ├── japanese_test_1.py │ │ ├── korean_test_1.py │ │ ├── portuguese_test_1.py │ │ ├── pytest.ini │ │ ├── russian_test_1.py │ │ └── spanish_test_1.py │ ├── uc_cdp_events.py │ ├── unit_tests/ │ │ ├── ReadMe.md │ │ └── verify_framework.py │ ├── upgrade_chromedriver.py │ ├── upload_file_test.py │ ├── user_agent_test.py │ ├── verify_undetected.py │ ├── visual_testing/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── case_plans/ │ │ │ ├── layout_test.VisualLayoutTests.test_applitools_layout_change.md │ │ │ ├── python_home_test.VisualLayoutTests.test_python_home_layout_change.md │ │ │ ├── test_layout_fail.VisualLayoutFailureTests.test_applitools_change.md │ │ │ ├── test_layout_fail.VisualLayoutFailureTests.test_xkcd_logo_change.md │ │ │ ├── test_layout_fail.VisualLayout_FixtureTests.test_python_home_change.md │ │ │ └── xkcd_visual_test.VisualLayoutTests.test_xkcd_layout_change.md │ │ ├── layout_test.py │ │ ├── python_home_test.py │ │ ├── test_layout_fail.py │ │ └── xkcd_visual_test.py │ ├── wordle_test.py │ ├── xpath_test.py │ └── youtube_search_test.py ├── help_docs/ │ ├── ReadMe.md │ ├── behave_gui.md │ ├── case_plans.md │ ├── cdp_mode_methods.md │ ├── commander.md │ ├── customizing_test_runs.md │ ├── demo_mode.md │ ├── desired_capabilities.md │ ├── features_list.md │ ├── handling_iframes.md │ ├── happy_customers.md │ ├── hidden_files_info.md │ ├── how_it_works.md │ ├── html_inspector.md │ ├── install.md │ ├── install_python_pip_git.md │ ├── js_package_manager.md │ ├── locale_codes.md │ ├── method_summary.md │ ├── mobile_testing.md │ ├── mysql_installation.md │ ├── recorder_mode.md │ ├── shadow_dom.md │ ├── syntax_formats.md │ ├── thank_you.md │ ├── translations.md │ ├── uc_mode.md │ ├── useful_grep_commands.md │ ├── using_safari_driver.md │ ├── verify_webdriver.md │ ├── virtualenv_instructions.md │ └── webdriver_installation.md ├── install.sh ├── integrations/ │ ├── __init__.py │ ├── azure/ │ │ ├── azure_pipelines/ │ │ │ └── ReadMe.md │ │ └── jenkins/ │ │ └── ReadMe.md │ ├── behave/ │ │ ├── ReadMe.md │ │ ├── behave.ini │ │ └── features/ │ │ ├── __init__.py │ │ ├── behave.ini │ │ ├── calculator.feature │ │ ├── environment.py │ │ ├── fail_page.feature │ │ ├── realworld.feature │ │ ├── steps/ │ │ │ ├── __init__.py │ │ │ ├── calculator.py │ │ │ ├── fail_page.py │ │ │ ├── real_world.py │ │ │ └── swag_labs.py │ │ └── swag_labs.feature │ ├── brython/ │ │ ├── ReadMe.md │ │ ├── index.html │ │ ├── index.py │ │ └── library.html │ ├── docker/ │ │ ├── ReadMe.md │ │ ├── docker-entrypoint.sh │ │ └── run_docker_test_in_chrome.sh │ ├── github/ │ │ ├── ReadMe.md │ │ └── workflows/ │ │ ├── ReadMe.md │ │ └── extras.md │ ├── google_cloud/ │ │ └── ReadMe.md │ ├── katalon/ │ │ └── ReadMe.md │ ├── linux/ │ │ ├── Linuxfile.sh │ │ ├── ReadMe.md │ │ ├── Xvfb_launcher.sh │ │ ├── jenkins_permissions.sh │ │ └── tomcat_permissions.sh │ ├── node_js/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── index.html │ │ ├── my_first_test.py │ │ ├── npm-shrinkwrap.json │ │ ├── package.json │ │ ├── server.js │ │ └── test_demo_site.py │ ├── selenium_grid/ │ │ └── ReadMe.md │ └── selenium_ide/ │ └── ReadMe.md ├── mkdocs.yml ├── mkdocs_build/ │ ├── ReadMe.txt │ ├── docs_instructions.txt │ ├── index.txt │ ├── prepare.py │ └── requirements.txt ├── pyproject.toml ├── pytest.ini ├── requirements.txt ├── sbase/ │ ├── ReadMe.txt │ ├── __init__.py │ ├── __main__.py │ └── steps.py ├── seleniumbase/ │ ├── ReadMe.md │ ├── __init__.py │ ├── __main__.py │ ├── __version__.py │ ├── behave/ │ │ ├── __init__.py │ │ ├── behave_helper.py │ │ ├── behave_sb.py │ │ └── steps.py │ ├── common/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── decorators.py │ │ ├── encryption.py │ │ ├── exceptions.py │ │ ├── obfuscate.py │ │ └── unobfuscate.py │ ├── config/ │ │ ├── __init__.py │ │ ├── ad_block_list.py │ │ ├── proxy_list.py │ │ └── settings.py │ ├── console_scripts/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── logo_helper.py │ │ ├── rich_helper.py │ │ ├── run.py │ │ ├── sb_behave_gui.py │ │ ├── sb_caseplans.py │ │ ├── sb_commander.py │ │ ├── sb_install.py │ │ ├── sb_mkchart.py │ │ ├── sb_mkdir.py │ │ ├── sb_mkfile.py │ │ ├── sb_mkpres.py │ │ ├── sb_mkrec.py │ │ ├── sb_objectify.py │ │ ├── sb_print.py │ │ └── sb_recorder.py │ ├── core/ │ │ ├── __init__.py │ │ ├── application_manager.py │ │ ├── browser_launcher.py │ │ ├── capabilities_parser.py │ │ ├── colored_traceback.py │ │ ├── create_db_tables.sql │ │ ├── detect_b_ver.py │ │ ├── download_helper.py │ │ ├── encoded_images.py │ │ ├── jqc_helper.py │ │ ├── log_helper.py │ │ ├── mysql.py │ │ ├── proxy_helper.py │ │ ├── recorder_helper.py │ │ ├── report_helper.py │ │ ├── s3_manager.py │ │ ├── sb_cdp.py │ │ ├── sb_driver.py │ │ ├── session_helper.py │ │ ├── settings_parser.py │ │ ├── style_sheet.py │ │ ├── testcase_manager.py │ │ ├── tour_helper.py │ │ └── visual_helper.py │ ├── drivers/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── atlas_drivers/ │ │ │ └── __init__.py │ │ ├── brave_drivers/ │ │ │ └── __init__.py │ │ ├── cft_drivers/ │ │ │ └── __init__.py │ │ ├── chromium_drivers/ │ │ │ └── __init__.py │ │ ├── chs_drivers/ │ │ │ └── __init__.py │ │ ├── comet_drivers/ │ │ │ └── __init__.py │ │ └── opera_drivers/ │ │ └── __init__.py │ ├── extensions/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ └── firefox_addon.xpi │ ├── fixtures/ │ │ ├── __init__.py │ │ ├── base_case.py │ │ ├── constants.py │ │ ├── css_to_xpath.py │ │ ├── errors.py │ │ ├── js_utils.py │ │ ├── page_actions.py │ │ ├── page_utils.py │ │ ├── shared_utils.py │ │ ├── unittest_helper.py │ │ ├── words.py │ │ └── xpath_to_css.py │ ├── js_code/ │ │ ├── __init__.py │ │ ├── active_css_js.py │ │ ├── live_js.py │ │ └── recorder_js.py │ ├── masterqa/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ └── master_qa.py │ ├── plugins/ │ │ ├── __init__.py │ │ ├── base_plugin.py │ │ ├── basic_test_info.py │ │ ├── db_reporting_plugin.py │ │ ├── driver_manager.py │ │ ├── page_source.py │ │ ├── pytest_plugin.py │ │ ├── s3_logging_plugin.py │ │ ├── sb_manager.py │ │ ├── screen_shots.py │ │ └── selenium_plugin.py │ ├── resources/ │ │ ├── ReadMe.md │ │ └── __init__.py │ ├── translate/ │ │ ├── __init__.py │ │ ├── chinese.py │ │ ├── dutch.py │ │ ├── french.py │ │ ├── italian.py │ │ ├── japanese.py │ │ ├── korean.py │ │ ├── master_dict.py │ │ ├── portuguese.py │ │ ├── russian.py │ │ ├── spanish.py │ │ └── translator.py │ ├── undetected/ │ │ ├── __init__.py │ │ ├── cdp.py │ │ ├── cdp_driver/ │ │ │ ├── __init__.py │ │ │ ├── _contradict.py │ │ │ ├── browser.py │ │ │ ├── cdp_util.py │ │ │ ├── config.py │ │ │ ├── connection.py │ │ │ ├── element.py │ │ │ └── tab.py │ │ ├── dprocess.py │ │ ├── options.py │ │ ├── patcher.py │ │ ├── reactor.py │ │ └── webelement.py │ └── utilities/ │ ├── __init__.py │ ├── selenium_grid/ │ │ ├── ReadMe.md │ │ ├── __init__.py │ │ ├── download_selenium_server.py │ │ ├── font_color │ │ ├── grid-hub │ │ ├── grid-node │ │ ├── grid_hub.py │ │ ├── grid_node.py │ │ ├── register-grid-node.bat │ │ ├── register-grid-node.sh │ │ ├── start-grid-hub.bat │ │ └── start-grid-hub.sh │ └── selenium_ide/ │ ├── ReadMe.md │ ├── __init__.py │ └── convert_ide.py ├── setup.cfg ├── setup.py ├── virtualenv_install.sh ├── win_install.bat └── win_virtualenv.bat ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ .git/ .gitignore .idea/ .mypy_cache/ .pytest_cache/ .tox/ .venv/ .vscode/ *.egg-info/ *.log *.py[cod] __pycache__/ build/ dist/ help_docs/ node_modules/ site/ ================================================ FILE: .github/FUNDING.yml ================================================ github: mdmintz ================================================ FILE: .github/Workflows.md ================================================ ### SeleniumBase Workflows > **Table of Contents / Navigation:** > - [**CI build**](workflows/python-package.yml) ================================================ FILE: .github/workflows/pages.yml ================================================ # Build and deploy a Jekyll site to GitHub Pages name: Deploy docs to GitHub Pages on: # Runs on pushes targeting the default branch push: branches: ["master"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write # Allow only one concurrent deployment concurrency: group: "pages" cancel-in-progress: false jobs: # Build job build: if: github.repository == 'seleniumbase/SeleniumBase' runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v6 - name: Setup Pages uses: actions/configure-pages@v5 - name: Build with Jekyll uses: actions/jekyll-build-pages@v1 with: source: ./ destination: ./_site - name: Upload artifact uses: actions/upload-pages-artifact@v4 # Deployment job deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} if: github.repository == 'seleniumbase/SeleniumBase' runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/python-nightly-mac.yml ================================================ name: Nightly Tests (macOS) on: schedule: - cron: "40 1 * * *" workflow_dispatch: branches: jobs: build: env: PY_COLORS: "1" runs-on: macos-latest strategy: fail-fast: false max-parallel: 6 matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade wheel pip install -r requirements.txt - name: Install SeleniumBase run: | pip install . - name: Check the console scripts interface run: | seleniumbase sbase - name: Install chromedriver run: | seleniumbase install chromedriver - name: Make sure pytest is working run: | echo "def test_1(): pass" > nothing.py pytest nothing.py - name: Make sure nosetests is working run: | echo "def test_2(): pass" > nothing2.py nosetests nothing2.py - name: Run pytest examples/unit_tests/verify_framework.py --browser=chrome --headless run: | pytest examples/unit_tests/verify_framework.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/offline_examples --browser=chrome --headless --rs run: | pytest examples/offline_examples --rs --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/boilerplate_test.py --browser=chrome --headless run: | pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_window_switching.py --browser=chrome --headless run: | pytest examples/test_window_switching.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Verify seleniumbase install from PyPI run: | pip install seleniumbase -U --no-deps --force-reinstall --no-cache-dir ================================================ FILE: .github/workflows/python-nightly-ubuntu.yml ================================================ name: Nightly Tests (Ubuntu) on: schedule: - cron: "30 1 * * *" workflow_dispatch: branches: jobs: build: env: PY_COLORS: "1" runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 6 matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade wheel pip install -r requirements.txt - name: Install SeleniumBase run: | pip install . - name: Check the console scripts interface run: | seleniumbase sbase - name: Install chromedriver run: | seleniumbase install chromedriver - name: Make sure pytest is working run: | echo "def test_1(): pass" > nothing.py pytest nothing.py - name: Make sure nosetests is working run: | echo "def test_2(): pass" > nothing2.py nosetests nothing2.py - name: Run pytest examples/unit_tests/verify_framework.py --browser=chrome --headless run: | pytest examples/unit_tests/verify_framework.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/offline_examples --browser=chrome --headless --rs run: | pytest examples/offline_examples --rs --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/boilerplate_test.py --browser=chrome --headless run: | pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_window_switching.py --browser=chrome --headless run: | pytest examples/test_window_switching.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Verify seleniumbase install from PyPI run: | pip install seleniumbase -U --no-deps --force-reinstall --no-cache-dir ================================================ FILE: .github/workflows/python-nightly-windows.yml ================================================ name: Nightly Tests (Windows) on: schedule: - cron: "50 1 * * *" workflow_dispatch: branches: jobs: build: env: PY_COLORS: "1" runs-on: windows-latest strategy: fail-fast: false max-parallel: 6 matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install --upgrade wheel pip install -r requirements.txt - name: Install SeleniumBase run: | pip install . - name: Check the console scripts interface run: | seleniumbase sbase - name: Install chromedriver run: | seleniumbase install chromedriver - name: Get chrome-headless-shell run: | sbase get chs - name: Make sure pytest is working run: | echo "def test_1(): pass" > nothing.py pytest nothing.py - name: Make sure nosetests is working run: | echo "def test_2(): pass" > nothing2.py nosetests nothing2.py - name: Run pytest examples/unit_tests/verify_framework.py --browser=chrome --headless run: | pytest examples/unit_tests/verify_framework.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/offline_examples --browser=chrome --headless --rs run: | pytest examples/offline_examples --rs --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/boilerplate_test.py --browser=chrome --headless run: | pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_window_switching.py --browser=chrome --headless run: | pytest examples/test_window_switching.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Verify seleniumbase install from PyPI run: | pip install seleniumbase -U --no-deps --force-reinstall --no-cache-dir ================================================ FILE: .github/workflows/python-package.yml ================================================ name: CI build on: pull_request: branches: push: branches: - master workflow_dispatch: branches: jobs: build: env: PY_COLORS: "1" runs-on: ubuntu-latest strategy: fail-fast: false max-parallel: 6 matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel setuptools pip install -r requirements.txt - name: Install SeleniumBase run: | pip install . - name: Lint with flake8 run: | pip install flake8 # Stop the build if there are flake8 issues flake8 . --count --show-source --statistics --exclude=temp - name: Install Chrome run: | sudo apt install google-chrome-stable - name: Check the console scripts interface run: | seleniumbase sbase - name: Install chromedriver run: | seleniumbase install chromedriver - name: Make sure pytest is working run: | pytest --help echo "def test_1(): pass" > nothing.py pytest nothing.py - name: Make sure nosetests is working run: | echo "def test_2(): pass" > nothing2.py nosetests nothing2.py - name: Run pytest examples/unit_tests/verify_framework.py --browser=chrome --headless run: | pytest examples/unit_tests/verify_framework.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/boilerplate_test.py --browser=chrome --headless run: | pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_demo_site.py --browser=chrome --xvfb run: | pytest examples/test_demo_site.py --browser=chrome --xvfb -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/iframe_tests.py --browser=chrome --xvfb --rs --crumbs run: | pytest examples/iframe_tests.py --browser=chrome --xvfb --rs --crumbs -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_mfa_login.py --browser=chrome --xvfb run: | pytest examples/test_mfa_login.py --browser=chrome --xvfb -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/iframe_tests.py --browser=chrome --xvfb --rs run: | pytest examples/iframe_tests.py --browser=chrome --xvfb --rs -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_window_switching.py --browser=chrome --headless run: | pytest examples/test_window_switching.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/my_first_test.py --browser=chrome --headless run: | pytest examples/my_first_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run pytest examples/test_inspect_html.py --browser=chrome --headless run: | pytest examples/test_inspect_html.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml - name: Run behave examples/behave_bdd/features/calculator.feature -D rs -D crumbs -D xvfb run: | behave examples/behave_bdd/features/calculator.feature -D rs -D crumbs -D xvfb -T -k - name: Run behave examples/behave_bdd/features/realworld.feature -D rs -D crumbs -D xvfb run: | behave examples/behave_bdd/features/realworld.feature -D rs -D crumbs -D xvfb -T -k ================================================ FILE: .gitignore ================================================ # Compiled Python Bytecode *.py[cod] # Packages *.egg *.egg-info .eggs eggs develop-eggs bin build dist lib lib64 parts sdist var .installed.cfg __pycache__ # Python3 pyvenv .env .venv env/ venv/ ENV/ VENV/ env.bak/ venv.bak/ .sbase .sbase* seleniumbase_env seleniumbase_venv sbase_env sbase_venv pyvenv.cfg .Python include pip-delete-this-directory.txt pip-selfcheck.json ipython.1.gz nosetests.1 .noseids # Installer logs pip-log.txt .swp # Unit test / coverage reports .coverage .tox coverage.xml nosetests.xml # py.test .cache/* .pytest_cache/* .pytest_config # Azure Pipelines junit test-results.xml # Developer .idea .project .pydevproject .vscode # Web Drivers chromedriver geckodriver msedgedriver operadriver uc_driver MicrosoftWebDriver.exe headless_ie_selenium.exe IEDriverServer.exe chromedriver.exe geckodriver.exe msedgedriver.exe operadriver.exe uc_driver.exe # Chromium Zip Files chrome-mac.zip chrome-linux.zip chrome-win.zip # Chromium folders chrome-mac chrome-linux chrome-win # Chrome for Testing Zip Files chrome-mac-arm64.zip chrome-mac-x64.zip chrome-linux64.zip chrome-win64.zip chrome-win32.zip # Chrome for Testing folders chrome-mac-arm64 chrome-mac-x64 chrome-linux64 chrome-win64 chrome-win32 # Chrome-Headless-Shell Zip Files chrome-headless-shell-mac-arm64.zip chrome-headless-shell-mac-x64.zip chrome-headless-shell-linux64.zip chrome-headless-shell-win64.zip chrome-headless-shell-win32.zip # Chrome-Headless-Shell folders chrome-headless-shell-mac-arm64 chrome-headless-shell-mac-x64 chrome-headless-shell-linux64 chrome-headless-shell-win64 chrome-headless-shell-win32 # msedgedriver requirements libc++.dylib # Logs logs latest_logs log_archives archived_logs geckodriver.log ghostdriver.log pytestdebug.log # Reports reports/*.xml latest_report report_archives archived_reports html_report.html last_report.html report.html report.xml # Dashboard dashboard.html dashboard.json dash_pie.json dashboard.lock # Allure Reports / Results allure_report allure-report allure_results allure-results # Charts saved_charts # Presentations saved_presentations # Tours tours_exported # Images images_exported # Cookies saved_cookies # Recordings recordings # Automated Visual Testing visual_baseline # MkDocs WebSite Generator site/* mkdocs_build/*.md mkdocs_build/*/*.md mkdocs_build/*/*/*.md mkdocs_build/*/*/*/*.md # macOS system files .DS_Store # Other selenium-server-standalone.jar proxy.zip proxy.lock verbose_hub_server.dat verbose_node_server.dat ip_of_grid_hub.dat downloaded_files archived_files assets temp temp_*/ node_modules ================================================ FILE: CHANGELOG.md ================================================

CHANGELOG

## See: [SeleniumBase/releases](https://github.com/seleniumbase/SeleniumBase/releases) 🗂️ 📋 ### (For CDP Mode, see the [CDP Mode docs](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md)) ### (For Stealthy Playwright, see the [Stealthy Playwright docs](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md)) ================================================ FILE: CNAME ================================================ seleniumbase.dev ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Code of conduct (SeleniumBase uses a modified version of [Flutter's Code of conduct](https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md)) The SeleniumBase project expects SeleniumBase's contributors to act professionally and respectfully. SeleniumBase contributors are expected to maintain the safety and dignity of SeleniumBase's social environments (such as GitHub and Gitter). Specifically: * Respect people, their identities, their culture, and their work. * Be kind. Be courteous. Be welcoming. * Listen. Consider and acknowledge people's points before responding. Should you experience anything that makes you feel unwelcome in SeleniumBase's community, please [contact us](https://gitter.im/seleniumbase/SeleniumBase). The SeleniumBase project will not tolerate harassment in SeleniumBase's community, even outside of SeleniumBase's public communication channels. ## Questions It's always OK to ask questions. Seleniumbase is a big project, and we don't expect everyone to know everything about everything. !["I try not to make fun of people for admitting they don't know things, because for each thing 'everyone knows' by the time they're adults, every day there are, on average, 10,000 people in the US hearing about it for the first time. If I make fun of people, I train them not to tell me when they have those moments. And I miss out on the fun." "Diet coke and mentos thing? What's that?" "Oh, man! We're going to the grocery store." "Why?" "You're one of today's lucky 10,000."](https://imgs.xkcd.com/comics/ten_thousand.png) Source: _[xkcd, May 2012](https://xkcd.com/1053/)_ ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to SeleniumBase The SeleniumBase project welcomes meaningful contributions. There are many ways to help: ## Bug Reports When opening a new issue or commenting on an existing issue, please make sure to provide concise, detailed instructions on how to reproduce the issue. If the issue can't be reproduced, it will be closed. Clearly describe the results you're seeing, and the results you're expecting. ## Feature Requests If you find that SeleniumBase is missing something, feel free to open an issue with details describing what feature(s) you'd like added or changed. ## Documentation SeleniumBase is a big software project, and documentation is key to understanding how it works and how to use it properly. If you feel that important documentation is missing, please let us know, or submit a pull request. ## Code Contributions The SeleniumBase project welcomes meaningful contributions. Given the complexity of the project, it may be easier to open an issue for a change you want made than to try implementing the change yourself. Recently, we've been moving toward the policy discussed in https://github.com/readme/featured/how-open-is-open-source, which is: "Open-Source, not Open-Contribution". As the article states, even small contributions (via pull requests) typically require hours of time to properly test and validate. We're still grateful for community involvement and when folks report bugs or suggest features. With some special exceptions, this project is mainly closed to contribution (via pull requests). ## (A Note on Style Guide Rules) [flake8](https://github.com/PyCQA/flake8) is the law of the land. The only flake8 rule ignored is [W503](https://github.com/grantmcconnaughey/Flake8Rules/blob/master/_rules/W503.md). For more details on why W503 should be ignored, see [this explanation](https://peps.python.org/pep-0008/#should-a-line-break-before-or-after-a-binary-operator), or [this shorter explanation](https://github.com/PyCQA/flake8/issues/494) by Python expert [Anthony Sottile](https://github.com/asottile). -------- For questions about this document, reach out to [Michael Mintz](https://github.com/mdmintz). ================================================ FILE: Dockerfile ================================================ # SeleniumBase Docker Image FROM ubuntu:24.04 SHELL ["/bin/bash", "-o", "pipefail", "-c"] ENV PYTHONUNBUFFERED=1 ENV PYTHONIOENCODING=UTF-8 ENV DEBIAN_FRONTEND=noninteractive #====================== # Locale Configuration #====================== RUN apt-get update RUN apt-get install -y --no-install-recommends tzdata locales RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen ENV TZ=America/New_York RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en ENV LC_ALL=en_US.UTF-8 RUN echo "LC_ALL=en_US.UTF-8" >> /etc/environment RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen RUN echo "LANG=en_US.UTF-8" > /etc/locale.conf RUN locale-gen en_US.UTF-8 #=========================== # Fingerprint Configuration #=========================== RUN apt-get update RUN apt install -y fonts-liberation fonts-noto-color-emoji libvulkan1 libnss3 libatk-bridge2.0-0 libcups2 libxcomposite1 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 RUN apt install -y fonts-freefont-ttf fonts-dejavu-core fonts-ubuntu fonts-roboto fonts-droid-fallback #====================== # Install Common Fonts #====================== RUN apt-get update RUN apt-get install -y \ fonts-liberation2 \ fonts-font-awesome \ fonts-terminus \ fonts-powerline \ fonts-open-sans \ fonts-mononoki \ fonts-lato #============================ # Install Linux Dependencies #============================ RUN apt-get update RUN apt-get install -y \ dbus-x11 \ libatk1.0-0 \ libatspi2.0-0 \ libdbus-1-3 \ libdrm2 \ libgtk-3-0 \ libnspr4 \ libasound2t64 \ libu2f-udev \ libwayland-client0 \ libx11-6 \ libx11-xcb1 \ libxdamage1 \ libxfixes3 \ libxkbcommon0 #========================== # Install useful utilities #========================== RUN apt-get update RUN apt-get install -y xdg-utils ca-certificates x11vnc #================================= # Install Bash Command Line Tools #================================= RUN apt-get update RUN apt-get -qy --no-install-recommends install \ curl \ sudo \ unzip \ vim \ wget \ xvfb #================ # Install Chrome #================ RUN apt-get update RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb RUN apt-get install -y ./google-chrome-stable_current_amd64.deb RUN rm ./google-chrome-stable_current_amd64.deb #================ # Install Python #================ RUN apt-get update && apt-get install -y software-properties-common RUN add-apt-repository ppa:deadsnakes/ppa -y RUN apt-get update RUN apt-get install -y python3.13 python3.13-venv python3.13-dev build-essential RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.13 1 RUN apt-get clean && rm -rf /var/lib/apt/lists/* RUN python3.13 -m ensurepip --upgrade RUN python3.13 -m pip install --upgrade pip RUN apt-get update RUN apt-get install -y python3.13-tk python3.13-dev RUN alias python=python3 RUN echo "alias python=python3" >> ~/.bashrc RUN rm /usr/bin/python3 RUN ln -s python3.13 /usr/bin/python3 #=============== # Cleanup Lists #=============== RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* #===================== # Set up SeleniumBase #===================== COPY sbase /SeleniumBase/sbase/ COPY seleniumbase /SeleniumBase/seleniumbase/ COPY examples /SeleniumBase/examples/ COPY integrations /SeleniumBase/integrations/ COPY requirements.txt /SeleniumBase/requirements.txt COPY setup.py /SeleniumBase/setup.py COPY MANIFEST.in /SeleniumBase/MANIFEST.in COPY pytest.ini /SeleniumBase/pytest.ini COPY setup.cfg /SeleniumBase/setup.cfg COPY virtualenv_install.sh /SeleniumBase/virtualenv_install.sh RUN find . -name '*.pyc' -delete RUN pip install --upgrade pip setuptools wheel RUN cd /SeleniumBase && ls && pip install -r requirements.txt --upgrade RUN cd /SeleniumBase && pip install . RUN pip install pyautogui RUN pip install playwright RUN seleniumbase get cft RUN seleniumbase get chromium #======================= # Download chromedriver #======================= RUN seleniumbase get chromedriver --path #============== # Extra config #============== ENV DISPLAY=":99" RUN Xvfb :99 -screen 1 1920x1080x16 -nolisten tcp & #========================================== # Create entrypoint and grab example tests #========================================== COPY integrations/docker/docker-entrypoint.sh / COPY integrations/docker/run_docker_test_in_chrome.sh / RUN chmod +x *.sh ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/bin/bash"] ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014-2026 Michael Mintz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ include README.md include pytest.ini include setup.cfg include .gitignore include requirements.txt include install.sh include install.bat include virtualenv_install.sh include virtualenv_install.bat include seleniumbase/core/create_db_tables.sql include seleniumbase/extensions/*.zip include seleniumbase/utilities/selenium_grid/grid-hub include seleniumbase/utilities/selenium_grid/grid-node include seleniumbase/utilities/selenium_grid/font_color include seleniumbase/utilities/selenium_grid/start-grid-hub.bat include seleniumbase/utilities/selenium_grid/register-grid-node.bat include seleniumbase/utilities/selenium_grid/start-grid-hub.sh include seleniumbase/utilities/selenium_grid/register-grid-node.sh ================================================ FILE: README.md ================================================

SeleniumBase

SeleniumBase

All-in-one Browser Automation Framework:
Web Crawling / Testing / Scraping / Stealth

PyPI version SeleniumBase GitHub Actions SeleniumBase Docs SeleniumBase PyPI downloads

🚀 Start | 🏰 Features | 🎛️ Options | 📚 Examples | 🪄 Scripts | 📱 Mobile
📘 The API | 🔠 SyntaxFormats | 🔴 Recorder | 📊 Dashboard | 🗾 Locale
🎖️ GUI | 📰 TestPage | 👤 UC Mode | 🐙 CDP Mode | 📶 Charts | 🖥️ Farm
👁️ How | 🚝 Migration | 🎭 Stealthy Playwright | 🛂 MasterQA | 🚎 Tours
🤖 CI/CD | ❇️ JSMgr | 🌏 Translator | 🎞️ Presenter | 🖼️ Visual | 🗂️ CPlans

📊 SeleniumBase is a complete framework for browser automation activities. It supports pytest, which makes it easy to scale testing. Includes advanced tools for reporting, script-generation, JavaScript manipulation, and stealthy automation. 🐙 Stealth modes: UC Mode and CDP Mode can bypass bot-detection, handle CAPTCHAs, and call methods from the Chrome Devtools Protocol. Includes Stealthy Playwright Mode, which makes Playwright stealthy via CDP Mode. 📚 Example scripts and tests are located in [**SeleniumBase/examples/**](https://github.com/seleniumbase/SeleniumBase/tree/master/examples). 🥷 Stealthy example scripts are located in [**SeleniumBase/examples/cdp_mode/**](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode). --------

📗 This script performs a Google Search using SeleniumBase UC Mode + CDP Mode:
SeleniumBase/examples/raw_google.py (Results are saved as PDF, HTML, and PNG)

```python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://google.com/ncr" sb.activate_cdp_mode(url) sb.type('[name="q"]', "SeleniumBase GitHub page") sb.click('[value="Google Search"]') sb.sleep(4) # The "AI Overview" sometimes loads print(sb.get_page_title()) sb.save_as_pdf_to_logs() sb.save_page_source_to_logs() sb.save_screenshot_to_logs() print("Logs have been saved to: ./latest_logs/") ``` > `python raw_google.py` SeleniumBase on Google --------

📗 Here's a script that bypasses Cloudflare's challenge page with UC Mode + CDP Mode: SeleniumBase/examples/cdp_mode/raw_gitlab.py

```python from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) ```

📙 There's also SeleniumBase's "Pure CDP Mode", which doesn't use WebDriver or Selenium at all: SeleniumBase/examples/cdp_mode/raw_cdp_gitlab.py

```python from seleniumbase import sb_cdp url = "https://gitlab.com/users/sign_in" sb = sb_cdp.Chrome(url, incognito=True) sb.sleep(2) sb.solve_captcha() sb.highlight('h1:contains("GitLab")') sb.highlight('button:contains("Sign in")') sb.driver.stop() ``` --------

📗 Here's SeleniumBase/examples/test_get_swag.py, which tests an e-commerce site:

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) # Call pytest class MyTestClass(BaseCase): def test_swag_labs(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("input#first-name", "SeleniumBase") self.type("input#last-name", "Automation") self.type("input#postal-code", "77123") self.click("input#continue") self.click("button#finish") self.assert_text("Thank you for your order!") ``` > `pytest test_get_swag.py` SeleniumBase Test > (The default browser is `--chrome` if not set.) --------

📗 Here's SeleniumBase/examples/test_coffee_cart.py, which verifies an e-commerce site:

```zsh pytest test_coffee_cart.py --demo ```

SeleniumBase Coffee Cart Test

>

(--demo mode slows down tests and highlights actions)

--------

📗 Here's SeleniumBase/examples/test_demo_site.py, which covers several actions:

```zsh pytest test_demo_site.py ```

SeleniumBase Example

> Easy to type, click, select, toggle, drag & drop, and more. (For more examples, see the SeleniumBase/examples/ folder.) --------

📓 Here's a high-level stealthy architecture overview of SeleniumBase:

High-Level Stealthy Architecture Overview (For maximum stealth, use CDP Mode, which is used by Stealthy Playwright Mode) --------

SeleniumBase

Explore the README:

--------
▶️ How is SeleniumBase different from raw Selenium? (click to expand)

💡 SeleniumBase is a Python framework for browser automation and testing. SeleniumBase uses Selenium/WebDriver APIs and incorporates test-runners such as pytest, pynose, and behave to provide organized structure, test discovery, test execution, test state (eg. passed, failed, or skipped), and command-line options for changing default settings (eg. browser selection). With raw Selenium, you would need to set up your own options-parser for configuring tests from the command-line.

💡 SeleniumBase's driver manager gives you more control over automatic driver downloads. (Use --driver-version=VER with your pytest run command to specify the version.) By default, SeleniumBase will download a driver version that matches your major browser version if not set.

💡 SeleniumBase automatically detects between CSS Selectors and XPath, which means you don't need to specify the type of selector in your commands (but optionally you could).

💡 SeleniumBase methods often perform multiple actions in a single method call. For example, self.type(selector, text) does the following:
1. Waits for the element to be visible.
2. Waits for the element to be interactive.
3. Clears the text field.
4. Types in the new text.
5. Presses Enter/Submit if the text ends in "\n".
With raw Selenium, those actions require multiple method calls.

💡 SeleniumBase uses default timeout values when not set:
self.click("button")
With raw Selenium, methods would fail instantly (by default) if an element needed more time to load:
self.driver.find_element(by="css selector", value="button").click()
(Reliable code is better than unreliable code.)

💡 SeleniumBase lets you change the explicit timeout values of methods:
self.click("button", timeout=10)
With raw Selenium, that requires more code:
WebDriverWait(driver, 10).until(EC.element_to_be_clickable("css selector", "button")).click()
(Simple code is better than complex code.)

💡 SeleniumBase gives you clean error output when a test fails. With raw Selenium, error messages can get very messy.

💡 SeleniumBase gives you the option to generate a dashboard and reports for tests. It also saves screenshots from failing tests to the ./latest_logs/ folder. Raw Selenium does not have these options out-of-the-box.

💡 SeleniumBase includes desktop GUI apps for running tests, such as SeleniumBase Commander for pytest and SeleniumBase Behave GUI for behave.

💡 SeleniumBase has its own Recorder / Test Generator for creating tests from manual browser actions.

💡 SeleniumBase comes with test case management software, ("CasePlans"), for organizing tests and step descriptions.

💡 SeleniumBase includes tools for building data apps, ("ChartMaker"), which can generate JavaScript from Python.

--------

📚 Learn about different ways of writing tests:

📗📝 Here's test_simple_login.py, which uses BaseCase class inheritance, and runs with pytest or pynose. (Use self.driver to access Selenium's raw driver.)

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TestSimpleLogin(BaseCase): def test_simple_login(self): self.open("seleniumbase.io/simple/login") self.type("#username", "demo_user") self.type("#password", "secret_pass") self.click('a:contains("Sign in")') self.assert_exact_text("Welcome!", "h1") self.assert_element("img#image1") self.highlight("#image1") self.click_link("Sign out") self.assert_text("signed out", "#top_message") ```

📘📝 Here's raw_login_sb.py, which uses the SB Context Manager. Runs with pure python. (Use sb.driver to access Selenium's raw driver.)

```python from seleniumbase import SB with SB() as sb: sb.open("seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") ```

📙📝 Here's raw_login_driver.py, which uses the Driver Manager. Runs with pure python. (The driver is an improved version of Selenium's raw driver, with more methods.)

```python from seleniumbase import Driver driver = Driver() try: driver.open("seleniumbase.io/simple/login") driver.type("#username", "demo_user") driver.type("#password", "secret_pass") driver.click('a:contains("Sign in")') driver.assert_exact_text("Welcome!", "h1") driver.assert_element("img#image1") driver.highlight("#image1") driver.click_link("Sign out") driver.assert_text("signed out", "#top_message") finally: driver.quit() ``` --------

Set up Python & Git:

🔵 Add Python and Git to your System PATH. 🔵 Using a Python virtual env is recommended.

Install SeleniumBase:

**You can install `seleniumbase` from [PyPI](https://pypi.org/project/seleniumbase/) or [GitHub](https://github.com/seleniumbase/SeleniumBase):** 🔵 **How to install `seleniumbase` from PyPI:** ```zsh pip install seleniumbase ``` * (Add `--upgrade` OR `-U` to upgrade SeleniumBase.) * (Add `--force-reinstall` to upgrade indirect packages.) 🔵 **How to install `seleniumbase` from a GitHub clone:** ```zsh git clone https://github.com/seleniumbase/SeleniumBase.git cd SeleniumBase/ pip install -e . ``` 🔵 **How to upgrade an existing install from a GitHub clone:** ```zsh git pull pip install -e . ``` 🔵 **Type `seleniumbase` or `sbase` to verify that SeleniumBase was installed successfully:** ```zsh ___ _ _ ___ / __| ___| |___ _ _ (_)_ _ _ __ | _ ) __ _ ______ \__ \/ -_) / -_) ' \| | \| | ' \ | _ \/ _` (_-< -_) |___/\___|_\___|_||_|_|\_,_|_|_|_\|___/\__,_/__|___| ---------------------------------------------------- ╭──────────────────────────────────────────────────╮ │ * USAGE: "seleniumbase [COMMAND] [PARAMETERS]" │ │ * OR: "sbase [COMMAND] [PARAMETERS]" │ │ │ │ COMMANDS: PARAMETERS / DESCRIPTIONS: │ │ get / install [DRIVER_NAME] [OPTIONS] │ │ methods (List common Python methods) │ │ options (List common pytest options) │ │ behave-options (List common behave options) │ │ gui / commander [OPTIONAL PATH or TEST FILE] │ │ behave-gui (SBase Commander for Behave) │ │ caseplans [OPTIONAL PATH or TEST FILE] │ │ mkdir [DIRECTORY] [OPTIONS] │ │ mkfile [FILE.py] [OPTIONS] │ │ mkrec / codegen [FILE.py] [OPTIONS] │ │ recorder (Open Recorder Desktop App.) │ │ record (If args: mkrec. Else: App.) │ │ mkpres [FILE.py] [LANG] │ │ mkchart [FILE.py] [LANG] │ │ print [FILE] [OPTIONS] │ │ translate [SB_FILE.py] [LANG] [ACTION] │ │ convert [WEBDRIVER_UNITTEST_FILE.py] │ │ extract-objects [SB_FILE.py] │ │ inject-objects [SB_FILE.py] [OPTIONS] │ │ objectify [SB_FILE.py] [OPTIONS] │ │ revert-objects [SB_FILE.py] [OPTIONS] │ │ encrypt / obfuscate │ │ decrypt / unobfuscate │ │ proxy (Start a basic proxy server) │ │ download server (Get Selenium Grid JAR file) │ │ grid-hub [start|stop] [OPTIONS] │ │ grid-node [start|stop] --hub=[HOST/IP] │ │ │ │ * EXAMPLE => "sbase get chromedriver stable" │ │ * For command info => "sbase help [COMMAND]" │ │ * For info on all commands => "sbase --help" │ ╰──────────────────────────────────────────────────╯ ```

🔵 Downloading webdrivers:

✅ SeleniumBase automatically downloads webdrivers as needed, such as `chromedriver`.
▶️ Here's sample output from a chromedriver download. (click to expand) ```zsh *** chromedriver to download = 141.0.7390.78 (Latest Stable) Downloading chromedriver-mac-arm64.zip from: https://storage.googleapis.com/chrome-for-testing-public/141.0.7390.78/mac-arm64/chromedriver-mac-arm64.zip ... Download Complete! Extracting ['chromedriver'] from chromedriver-mac-arm64.zip ... Unzip Complete! The file [chromedriver] was saved to: ~/github/SeleniumBase/seleniumbase/drivers/ chromedriver Making [chromedriver 141.0.7390.78] executable ... [chromedriver 141.0.7390.78] is now ready for use! ```

Basic Example / Usage:

🔵 If you've cloned SeleniumBase, you can run tests from the [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder.

Here's my_first_test.py:

```zsh cd examples/ pytest my_first_test.py ``` SeleniumBase Test

Here's the full code for my_first_test.py:

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_swag_labs(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Automation") self.type("#postal-code", "77123") self.click("input#continue") self.assert_text("Checkout: Overview") self.assert_text("Backpack", "div.cart_item") self.assert_text("29.99", "div.inventory_item_price") self.click("button#finish") self.assert_exact_text("Thank you for your order!", "h2") self.assert_element('img[alt="Pony Express"]') self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") ``` * By default, **[CSS Selectors](https://www.w3schools.com/cssref/css_selectors.asp)** are used for finding page elements. * If you're new to CSS Selectors, games like [CSS Diner](http://flukeout.github.io/) can help you learn. * For more reading, [here's an advanced guide on CSS attribute selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).

Here are some common SeleniumBase methods:

```python self.open(url) # Navigate the browser window to the URL. self.type(selector, text) # Update the field with the text. self.click(selector) # Click the element with the selector. self.click_link(link_text) # Click the link containing text. self.go_back() # Navigate back to the previous URL. self.select_option_by_text(dropdown_selector, option) self.hover_and_click(hover_selector, click_selector) self.drag_and_drop(drag_selector, drop_selector) self.get_text(selector) # Get the text from the element. self.get_current_url() # Get the URL of the current page. self.get_page_source() # Get the HTML of the current page. self.get_attribute(selector, attribute) # Get element attribute. self.get_title() # Get the title of the current page. self.switch_to_frame(frame) # Switch into the iframe container. self.switch_to_default_content() # Leave the iframe container. self.open_new_window() # Open a new window in the same browser. self.switch_to_window(window) # Switch to the browser window. self.switch_to_default_window() # Switch to the original window. self.get_new_driver(OPTIONS) # Open a new driver with OPTIONS. self.switch_to_driver(driver) # Switch to the browser driver. self.switch_to_default_driver() # Switch to the original driver. self.wait_for_element(selector) # Wait until element is visible. self.is_element_visible(selector) # Return element visibility. self.is_text_visible(text, selector) # Return text visibility. self.sleep(seconds) # Do nothing for the given amount of time. self.save_screenshot(name) # Save a screenshot in .png format. self.assert_element(selector) # Verify the element is visible. self.assert_text(text, selector) # Verify text in the element. self.assert_exact_text(text, selector) # Verify text is exact. self.assert_title(title) # Verify the title of the web page. self.assert_downloaded_file(file) # Verify file was downloaded. self.assert_no_404_errors() # Verify there are no broken links. self.assert_no_js_errors() # Verify there are no JS errors. ``` 🔵 For the complete list of SeleniumBase methods, see: Method Summary

Fun Facts / Learn More:

✅ SeleniumBase automatically handles common WebDriver actions such as launching web browsers before tests, saving screenshots during failures, and closing web browsers after tests.

✅ SeleniumBase lets you customize tests via command-line options.

✅ SeleniumBase uses simple syntax for commands. Example:

```python self.type("input", "dogs\n") # (The "\n" presses ENTER) ``` Most SeleniumBase scripts can be run with pytest, pynose, or pure python. Not all test runners can run all test formats. For example, tests that use the `sb` pytest fixture can only be run with `pytest`. (See Syntax Formats) There's also a Gherkin test format that runs with behave. ```zsh pytest coffee_cart_tests.py --rs pytest test_sb_fixture.py --demo pytest test_suite.py --rs --html=report.html --dashboard pynose basic_test.py --mobile pynose test_suite.py --headless --report --show-report python raw_sb.py python raw_test_scripts.py behave realworld.feature behave calculator.feature -D rs -D dashboard ```

pytest includes automatic test discovery. If you don't specify a specific file or folder to run, pytest will automatically search through all subdirectories for tests to run based on the following criteria:

* Python files that start with `test_` or end with `_test.py`. * Python methods that start with `test_`. With a SeleniumBase [pytest.ini](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/pytest.ini) file present, you can modify default discovery settings. The Python class name can be anything because `seleniumbase.BaseCase` inherits `unittest.TestCase` to trigger autodiscovery.

✅ You can do a pre-flight check to see which tests would get discovered by pytest before the actual run:

```zsh pytest --co -q ```

✅ You can be more specific when calling pytest or pynose on a file:

```zsh pytest [FILE_NAME.py]::[CLASS_NAME]::[METHOD_NAME] pynose [FILE_NAME.py]:[CLASS_NAME].[METHOD_NAME] ```

✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (up to a timeout limit). This means you no longer need random time.sleep() statements in your scripts.

NO MORE FLAKY TESTS! ✅ SeleniumBase supports all major browsers and operating systems:

Browsers: Chrome, Edge, Firefox, and Safari.

Systems: Linux/Ubuntu, macOS, and Windows.

✅ SeleniumBase works on all popular CI/CD platforms:

GitHub Actions integration Jenkins integration Azure integration Google Cloud integration AWS integration Your Computer

✅ SeleniumBase includes an automated/manual hybrid solution called MasterQA to speed up manual testing with automation while manual testers handle validation.

✅ SeleniumBase supports running tests while offline (assuming webdrivers have previously been downloaded when online).

✅ For a full list of SeleniumBase features, Click Here.

Demo Mode / Debugging:

🔵 Demo Mode helps you see what a test is doing. If a test is moving too fast for your eyes, run it in Demo Mode to pause the browser briefly between actions, highlight page elements being acted on, and display assertions: ```zsh pytest my_first_test.py --demo ``` 🔵 `time.sleep(seconds)` can be used to make a test wait at a specific spot: ```python import time; time.sleep(3) # Do nothing for 3 seconds. ``` 🔵 **Debug Mode** with Python's built-in **[pdb](https://docs.python.org/3/library/pdb.html)** library helps you debug tests: ```python import pdb; pdb.set_trace() import pytest; pytest.set_trace() breakpoint() # Shortcut for "import pdb; pdb.set_trace()" ``` > (**`pdb`** commands: `n`, `c`, `s`, `u`, `d` => `next`, `continue`, `step`, `up`, `down`) 🔵 To pause an active test that throws an exception or error, (*and keep the browser window open while **Debug Mode** begins in the console*), add **`--pdb`** as a `pytest` option: ```zsh pytest test_fail.py --pdb ``` 🔵 To start tests in Debug Mode, add **`--trace`** as a `pytest` option: ```zsh pytest test_coffee_cart.py --trace ``` SeleniumBase test with the pdbp (Pdb+) debugger

🔵 Command-line Options:

✅ Here are some useful command-line options that come with pytest: ```zsh -v # Verbose mode. Prints the full name of each test and shows more details. -q # Quiet mode. Print fewer details in the console output when running tests. -x # Stop running the tests after the first failure is reached. --html=report.html # Creates a detailed pytest-html report after tests finish. --co | --collect-only # Show what tests would get run. (Without running them) --co -q # (Both options together!) - Do a dry run with full test names shown. -n=NUM # Multithread the tests using that many threads. (Speed up test runs!) -s # See print statements. (Should be on by default with pytest.ini present.) --junit-xml=report.xml # Creates a junit-xml report after tests finish. --pdb # If a test fails, enter Post Mortem Debug Mode. (Don't use with CI!) --trace # Enter Debug Mode at the beginning of each test. (Don't use with CI!) -m=MARKER # Run tests with the specified pytest marker. ``` ✅ SeleniumBase provides additional pytest command-line options for tests: ```zsh --browser=BROWSER # (The web browser to use. Default: "chrome".) --chrome # (Shortcut for "--browser=chrome". On by default.) --edge # (Shortcut for "--browser=edge".) --firefox # (Shortcut for "--browser=firefox".) --safari # (Shortcut for "--browser=safari".) --opera # (Shortcut for "--browser=opera".) --brave # (Shortcut for "--browser=brave".) --comet # (Shortcut for "--browser=comet".) --atlas # (Shortcut for "--browser=atlas".) --settings-file=FILE # (Override default SeleniumBase settings.) --env=ENV # (Set the test env. Access with "self.env" in tests.) --account=STR # (Set account. Access with "self.account" in tests.) --data=STRING # (Extra test data. Access with "self.data" in tests.) --var1=STRING # (Extra test data. Access with "self.var1" in tests.) --var2=STRING # (Extra test data. Access with "self.var2" in tests.) --var3=STRING # (Extra test data. Access with "self.var3" in tests.) --variables=DICT # (Extra test data. Access with "self.variables".) --user-data-dir=DIR # (Set the Chrome user data directory to use.) --protocol=PROTOCOL # (The Selenium Grid protocol: http|https.) --server=SERVER # (The Selenium Grid server/IP used for tests.) --port=PORT # (The Selenium Grid port used by the test server.) --cap-file=FILE # (The web browser's desired capabilities to use.) --cap-string=STRING # (The web browser's desired capabilities to use.) --proxy=SERVER:PORT # (Connect to a proxy server:port as tests are running) --proxy=USERNAME:PASSWORD@SERVER:PORT # (Use an authenticated proxy server) --proxy-bypass-list=STRING # (";"-separated hosts to bypass, Eg "*.foo.com") --proxy-pac-url=URL # (Connect to a proxy server using a PAC_URL.pac file.) --proxy-pac-url=USERNAME:PASSWORD@URL # (Authenticated proxy with PAC URL.) --proxy-driver # (If a driver download is needed, will use: --proxy=PROXY.) --multi-proxy # (Allow multiple authenticated proxies when multi-threaded.) --agent=STRING # (Modify the web browser's User-Agent string.) --mobile # (Use the mobile device emulator while running tests.) --metrics=STRING # (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".) --chromium-arg="ARG=N,ARG2" # (Set Chromium args, ","-separated, no spaces.) --firefox-arg="ARG=N,ARG2" # (Set Firefox args, comma-separated, no spaces.) --firefox-pref=SET # (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP # (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR # (Load a Chrome Extension directory, comma-separated.) --disable-features="F1,F2" # (Disable features, comma-separated, no spaces.) --binary-location=PATH # (Set path of the Chromium browser binary to use.) --driver-version=VER # (Set the chromedriver or uc_driver version to use.) --sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.) --wfa # (Wait for AngularJS to be done loading after specific web actions.) --pls=PLS # (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) --headless # (The default headless mode. Linux uses this mode by default.) --headless1 # (Use Chrome's old headless mode. Fast, but has limitations.) --headless2 # (Use Chrome's new headless mode, which supports extensions.) --headed # (Run tests in headed/GUI mode on Linux OS, where not default.) --xvfb # (Run tests using the Xvfb virtual display server on Linux OS.) --xvfb-metrics=STRING # (Set Xvfb display size on Linux: "Width,Height".) --locale=LOCALE_CODE # (Set the Language Locale Code for the web browser.) --interval=SECONDS # (The autoplay interval for presentations & tour steps) --start-page=URL # (The starting URL for the web browser when tests begin.) --archive-logs # (Archive existing log files instead of deleting them.) --archive-downloads # (Archive old downloads instead of deleting them.) --time-limit=SECONDS # (Safely fail any test that exceeds the time limit.) --slow # (Slow down the automation. Faster than using Demo Mode.) --demo # (Slow down and visually see test actions as they occur.) --demo-sleep=SECONDS # (Set the wait time after Slow & Demo Mode actions.) --highlights=NUM # (Number of highlight animations for Demo Mode actions.) --message-duration=SECONDS # (The time length for Messenger alerts.) --check-js # (Check for JavaScript errors after page loads.) --ad-block # (Block some types of display ads from loading.) --host-resolver-rules=RULES # (Set host-resolver-rules, comma-separated.) --block-images # (Block images from loading during tests.) --do-not-track # (Indicate to websites that you don't want to be tracked.) --verify-delay=SECONDS # (The delay before MasterQA verification checks.) --ee | --esc-end # (Lets the user end the current test via the ESC key.) --recorder # (Enables the Recorder for turning browser actions into code.) --rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.) --rec-sleep # (If the Recorder is enabled, also records self.sleep calls.) --rec-print # (If the Recorder is enabled, prints output after tests end.) --disable-cookies # (Disable Cookies on websites. Pages might break!) --disable-js # (Disable JavaScript on websites. Pages might break!) --disable-csp # (Disable the Content Security Policy of websites.) --disable-ws # (Disable Web Security on Chromium-based browsers.) --enable-ws # (Enable Web Security on Chromium-based browsers.) --enable-sync # (Enable "Chrome Sync" on websites.) --uc | --undetected # (Use undetected-chromedriver to evade bot-detection.) --uc-cdp-events # (Capture CDP events when running in "--undetected" mode.) --log-cdp # ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) --remote-debug # (Sync to Chrome Remote Debugger chrome://inspect/#devices) --ftrace | --final-trace # (Debug Mode after each test. Don't use with CI!) --dashboard # (Enable the SeleniumBase Dashboard. Saved at: dashboard.html) --dash-title=STRING # (Set the title shown for the generated dashboard.) --enable-3d-apis # (Enables WebGL and 3D APIs.) --swiftshader # (Chrome "--use-gl=angle" / "--use-angle=swiftshader-webgl") --incognito # (Enable Chrome's Incognito mode.) --guest # (Enable Chrome's Guest mode.) --dark # (Enable Chrome's Dark mode.) --devtools # (Open Chrome's DevTools when the browser opens.) --rs | --reuse-session # (Reuse browser session for all tests.) --rcs | --reuse-class-session # (Reuse session for tests in class.) --crumbs # (Delete all cookies between tests reusing a session.) --disable-beforeunload # (Disable the "beforeunload" event on Chrome.) --window-position=X,Y # (Set the browser's starting window position.) --window-size=WIDTH,HEIGHT # (Set the browser's starting window size.) --maximize # (Start tests with the browser window maximized.) --screenshot # (Save a screenshot at the end of each test.) --no-screenshot # (No screenshots saved unless tests directly ask it.) --visual-baseline # (Set the visual baseline for Visual/Layout tests.) --wire # (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf # (Set Chromium "plugins.always_open_pdf_externally":True.) --timeout-multiplier=MULTIPLIER # (Multiplies the default timeout values.) --list-fail-page # (After each failing test, list the URL of the failure.) ``` (See the full list of command-line option definitions **[here](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/pytest_plugin.py)**. For detailed examples of command-line options, see **[customizing_test_runs.md](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md)**) -------- 🔵 During test failures, logs and screenshots from the most recent test run will get saved to the `latest_logs/` folder. Those logs will get moved to `archived_logs/` if you add --archive_logs to command-line options, or have `ARCHIVE_EXISTING_LOGS` set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py), otherwise log files with be cleaned up at the start of the next test run. The `test_suite.py` collection contains tests that fail on purpose so that you can see how logging works. ```zsh cd examples/ pytest test_suite.py --chrome pytest test_suite.py --firefox ``` An easy way to override seleniumbase/config/settings.py is by using a custom settings file. Here's the command-line option to add to tests: (See [examples/custom_settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/custom_settings.py)) `--settings_file=custom_settings.py` (Settings include default timeout values, a two-factor auth key, DB credentials, S3 credentials, and other important settings used by tests.) 🔵 To pass additional data from the command-line to tests, add `--data="ANY STRING"`. Inside your tests, you can use `self.data` to access that.

Directory Configuration:

🔵 When running tests with **`pytest`**, you'll want a copy of **[pytest.ini](https://github.com/seleniumbase/SeleniumBase/blob/master/pytest.ini)** in your root folders. When running tests with **`pynose`**, you'll want a copy of **[setup.cfg](https://github.com/seleniumbase/SeleniumBase/blob/master/setup.cfg)** in your root folders. These files specify default configuration details for tests. Test folders should also include a blank **[__init__.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/offline_examples/__init__.py)** file to allow your test files to import other files from that folder. 🔵 `sbase mkdir DIR` creates a folder with config files and sample tests: ```zsh sbase mkdir ui_tests ``` > That new folder will have these files: ```zsh ui_tests/ ├── __init__.py ├── my_first_test.py ├── parameterized_test.py ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── test_demo_site.py └── boilerplates/ ├── __init__.py ├── base_test_case.py ├── boilerplate_test.py ├── classic_obj_test.py ├── page_objects.py ├── sb_fixture_test.py └── samples/ ├── __init__.py ├── google_objects.py ├── google_test.py ├── sb_swag_test.py └── swag_labs_test.py ``` ProTip™: You can also create a boilerplate folder without any sample tests in it by adding `-b` or `--basic` to the `sbase mkdir` command: ```zsh sbase mkdir ui_tests --basic ``` > That new folder will have these files: ```zsh ui_tests/ ├── __init__.py ├── pytest.ini ├── requirements.txt └── setup.cfg ``` Of those files, the `pytest.ini` config file is the most important, followed by a blank `__init__.py` file. There's also a `setup.cfg` file (for pynose). Finally, the `requirements.txt` file can be used to help you install seleniumbase into your environments (if it's not already installed). ProTip™: Add `--gha` to include a GitHub Actions `.yml` file with default settings: ```zsh ui_tests/ └── .github └── workflows/ └── python-package.yml ``` --------

Log files from failed tests:

Let's try an example of a test that fails: ```python """ test_fail.py """ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_find_army_of_robots_on_xkcd_desert_island(self): self.open("https://xkcd.com/731/") self.assert_element("div#ARMY_OF_ROBOTS", timeout=1) # This should fail ``` You can run it from the `examples/` folder like this: ```zsh pytest test_fail.py ``` 🔵 You'll notice that a logs folder, `./latest_logs/`, was created to hold information (and screenshots) about the failing test. During test runs, past results get moved to the archived_logs folder if you have ARCHIVE_EXISTING_LOGS set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py), or if your run tests with `--archive-logs`. If you choose not to archive existing logs, they will be deleted and replaced by the logs of the latest test run. --------

SeleniumBase Dashboard:

🔵 The `--dashboard` option for pytest generates a SeleniumBase Dashboard located at `dashboard.html`, which updates automatically as tests run and produce results. Example: ```zsh pytest --dashboard --rs --headless ``` The SeleniumBase Dashboard 🔵 Additionally, you can host your own SeleniumBase Dashboard Server on a port of your choice. Here's an example of that using Python's `http.server`: ```zsh python -m http.server 1948 ``` 🔵 Now you can navigate to `http://localhost:1948/dashboard.html` in order to view the dashboard as a web app. This requires two different terminal windows: one for running the server, and another for running the tests, which should be run from the same directory. (Use Ctrl+C to stop the http server.) 🔵 Here's a full example of what the SeleniumBase Dashboard may look like: ```zsh pytest test_suite.py test_image_saving.py --dashboard --rs --headless ``` The SeleniumBase Dashboard --------

Generating Test Reports:

🔵 pytest HTML Reports:

✅ Using `--html=report.html` gives you a fancy report of the name specified after your test suite completes. ```zsh pytest test_suite.py --html=report.html ``` Example Pytest Report ✅ When combining pytest html reports with SeleniumBase Dashboard usage, the pie chart from the Dashboard will get added to the html report. Additionally, if you set the html report URL to be the same as the Dashboard URL when also using the dashboard, (example: `--dashboard --html=dashboard.html`), then the Dashboard will become an advanced html report when all the tests complete. ✅ Here's an example of an upgraded html report: ```zsh pytest test_suite.py --dashboard --html=report.html ``` Dashboard Pytest HTML Report If viewing pytest html reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356/7058266) for the html to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/system-administration/security/configuring-content-security-policy/). You can also use `--junit-xml=report.xml` to get an xml report instead. Jenkins can use this file to display better reporting for your tests. ```zsh pytest test_suite.py --junit-xml=report.xml ```

🔵 pynose Reports:

The `--report` option gives you a fancy report after your test suite completes. ```zsh pynose test_suite.py --report ``` Example pynose Report (NOTE: You can add `--show-report` to immediately display pynose reports after the test suite completes. Only use `--show-report` when running tests locally because it pauses the test run.)

🔵 behave Dashboard & Reports:

(The [behave_bdd/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/behave_bdd) folder can be found in the [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder.) ```zsh behave behave_bdd/features/ -D dashboard -D headless ``` You can also use `--junit` to get `.xml` reports for each behave feature. Jenkins can use these files to display better reporting for your tests. ```zsh behave behave_bdd/features/ --junit -D rs -D headless ```

🔵 Allure Reports:

See: [https://allurereport.org/docs/pytest/](https://allurereport.org/docs/pytest/) SeleniumBase no longer includes `allure-pytest` as part of installed dependencies. If you want to use it, install it first: ```zsh pip install allure-pytest ``` Now your tests can create Allure results files, which can be processed by Allure Reports. ```zsh pytest test_suite.py --alluredir=allure_results ``` --------

Using a Proxy Server:

If you wish to use a proxy server for your browser tests (Chromium or Firefox), you can add `--proxy=IP_ADDRESS:PORT` as an argument on the command line. ```zsh pytest proxy_test.py --proxy=IP_ADDRESS:PORT ``` If the proxy server that you wish to use requires authentication, you can do the following (Chromium only): ```zsh pytest proxy_test.py --proxy=USERNAME:PASSWORD@IP_ADDRESS:PORT ``` SeleniumBase also supports SOCKS4 and SOCKS5 proxies: ```zsh pytest proxy_test.py --proxy="socks4://IP_ADDRESS:PORT" pytest proxy_test.py --proxy="socks5://IP_ADDRESS:PORT" ``` To make things easier, you can add your frequently-used proxies to PROXY_LIST in [proxy_list.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/proxy_list.py), and then use `--proxy=KEY_FROM_PROXY_LIST` to use the IP_ADDRESS:PORT of that key. ```zsh pytest proxy_test.py --proxy=proxy1 ```

Changing the User-Agent:

🔵 If you wish to change the User-Agent for your browser tests (Chromium and Firefox only), you can add `--agent="USER AGENT STRING"` as an argument on the command-line. ```zsh pytest user_agent_test.py --agent="Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7412.EU" ```

Handling Pop-Up Alerts:

🔵 self.accept_alert() automatically waits for and accepts alert pop-ups. self.dismiss_alert() automatically waits for and dismisses alert pop-ups. On occasion, some methods like self.click(SELECTOR) might dismiss a pop-up on its own because they call JavaScript to make sure that the readyState of the page is complete before advancing. If you're trying to accept a pop-up that got dismissed this way, use this workaround: Call self.find_element(SELECTOR).click() instead, (which will let the pop-up remain on the screen), and then use self.accept_alert() to accept the pop-up (more on that here). If pop-ups are intermittent, wrap code in a try/except block.

Building Guided Tours for Websites:

🔵 Learn about SeleniumBase Interactive Walkthroughs (in the `examples/tour_examples/` folder). It's great for prototyping a website onboarding experience. --------

Production Environments & Integrations:

▶️ Here are some things you can do to set up a production environment for your testing. (click to expand) Here's an example of running tests with some additional features enabled: ```zsh pytest [YOUR_TEST_FILE.py] --with-db-reporting --with-s3-logging ```

Detailed Method Specifications and Examples:

🔵 **Navigating to a web page: (and related commands)** ```python self.open("https://xkcd.com/378/") # This method opens the specified page. self.go_back() # This method navigates the browser to the previous page. self.go_forward() # This method navigates the browser forward in history. self.refresh_page() # This method reloads the current page. self.get_current_url() # This method returns the current page URL. self.get_page_source() # This method returns the current page source. ``` ProTip™: You can use the self.get_page_source() method with Python's find() command to parse through HTML to find something specific. (For more advanced parsing, see the BeautifulSoup example.) ```python source = self.get_page_source() head_open_tag = source.find('') head_close_tag = source.find('', head_open_tag) everything_inside_head = source[head_open_tag+len(''):head_close_tag] ``` 🔵 **Clicking:** To click an element on the page: ```python self.click("div#my_id") ``` **ProTip™:** In most web browsers, you can right-click on a page and select `Inspect Element` to see the CSS selector details that you'll need to create your own scripts. 🔵 **Typing Text:** self.type(selector, text) # updates the text from the specified element with the specified value. An exception is raised if the element is missing or if the text field is not editable. Example: ```python self.type("input#id_value", "2012") ``` You can also use self.add_text() or the WebDriver .send_keys() command, but those won't clear the text box first if there's already text inside. 🔵 **Getting the text from an element on a page:** ```python text = self.get_text("header h2") ``` 🔵 **Getting the attribute value from an element on a page:** ```python attribute = self.get_attribute("#comic img", "title") ``` 🔵 **Asserting existence of an element on a page within some number of seconds:** ```python self.wait_for_element_present("div.my_class", timeout=10) ``` (NOTE: You can also use: `self.assert_element_present(ELEMENT)`) 🔵 **Asserting visibility of an element on a page within some number of seconds:** ```python self.wait_for_element_visible("a.my_class", timeout=5) ``` (NOTE: The short versions of that are `self.find_element(ELEMENT)` and `self.assert_element(ELEMENT)`. The `find_element()` version returns the element.) Since the line above returns the element, you can combine that with `.click()` as shown below: ```python self.find_element("a.my_class", timeout=5).click() # But you're better off using the following statement, which does the same thing: self.click("a.my_class") # DO IT THIS WAY! ``` **ProTip™:** You can use dots to signify class names (Ex: `div.class_name`) as a simplified version of `div[class="class_name"]` within a CSS selector. You can also use `*=` to search for any partial value in a CSS selector as shown below: ```python self.click('a[name*="partial_name"]') ``` 🔵 **Asserting visibility of text inside an element on a page within some number of seconds:** ```python self.assert_text("Make it so!", "div#trek div.picard div.quotes") self.assert_text("Tea. Earl Grey. Hot.", "div#trek div.picard div.quotes", timeout=3) ``` (NOTE: `self.find_text(TEXT, ELEMENT)` and `self.wait_for_text(TEXT, ELEMENT)` also do this. For backwards compatibility, older method names were kept, but the default timeout may be different.) 🔵 **Asserting Anything:** ```python self.assert_true(var1 == var2) self.assert_false(var1 == var2) self.assert_equal(var1, var2) ``` 🔵 **Useful Conditional Statements: (with creative examples)** ❓ `is_element_visible(selector):` (visible on the page) ```python if self.is_element_visible('div#warning'): print("Red Alert: Something bad might be happening!") ``` ❓ `is_element_present(selector):` (present in the HTML) ```python if self.is_element_present('div#top_secret img.tracking_cookie'): self.contact_cookie_monster() # Not a real SeleniumBase method else: current_url = self.get_current_url() self.contact_the_nsa(url=current_url, message="Dark Zone Found") # Not a real SeleniumBase method ``` ```python def is_there_a_cloaked_klingon_ship_on_this_page(): if self.is_element_present("div.ships div.klingon"): return not self.is_element_visible("div.ships div.klingon") return False ``` ❓ `is_text_visible(text, selector):` (text visible on element) ```python if self.is_text_visible("You Shall Not Pass!", "h1"): self.open("https://www.youtube.com/watch?v=3xYXUeSmb-Y") ```
▶️ Click for a longer example of is_text_visible(): ```python def get_mirror_universe_captain_picard_superbowl_ad(superbowl_year): selector = "div.superbowl_%s div.commercials div.transcript div.picard" % superbowl_year if self.is_text_visible("Yes, it was I who summoned you all here.", selector): return "Picard Paramount+ Superbowl Ad 2020" elif self.is_text_visible("Commander, signal the following: Our Network is Secure!"): return "Picard Mirror Universe iboss Superbowl Ad 2018" elif self.is_text_visible("For the Love of Marketing and Earl Grey Tea!", selector): return "Picard Mirror Universe HubSpot Superbowl Ad 2015" elif self.is_text_visible("Delivery Drones... Engage", selector): return "Picard Mirror Universe Amazon Superbowl Ad 2015" elif self.is_text_visible("Bing it on Screen!", selector): return "Picard Mirror Universe Microsoft Superbowl Ad 2015" elif self.is_text_visible("OK Glass, Make it So!", selector): return "Picard Mirror Universe Google Superbowl Ad 2015" elif self.is_text_visible("Number One, I've Never Seen Anything Like It.", selector): return "Picard Mirror Universe Tesla Superbowl Ad 2015" elif self.is_text_visible("Let us make sure history never forgets the name ... Facebook", selector): return "Picard Mirror Universe Facebook Superbowl Ad 2015" elif self.is_text_visible("""With the first link, the chain is forged. The first speech censored, the first thought forbidden, the first freedom denied, chains us all irrevocably.""", selector): return "Picard Mirror Universe Wikimedia Superbowl Ad 2015" else: raise Exception("Reports of my assimilation are greatly exaggerated.") ```
❓ `is_link_text_visible(link_text):` ```python if self.is_link_text_visible("Stop! Hammer time!"): self.click_link("Stop! Hammer time!") ```

🔵 Switching Tabs:

If your test opens up a new tab/window, you can switch to it. (SeleniumBase automatically switches to new tabs that don't open to about:blank URLs.)

```python self.switch_to_window(1) # This switches to the new tab (0 is the first one) ```

🔵 How to handle iframes:

🔵 iframes follow the same principle as new windows: You must first switch to the iframe if you want to perform actions in there: ```python self.switch_to_frame("iframe") # ... Now perform actions inside the iframe self.switch_to_parent_frame() # Exit the current iframe ``` To exit from multiple iframes, use `self.switch_to_default_content()`. (If inside a single iframe, this has the same effect as `self.switch_to_parent_frame()`.) ```python self.switch_to_frame('iframe[name="frame1"]') self.switch_to_frame('iframe[name="frame2"]') # ... Now perform actions inside the inner iframe self.switch_to_default_content() # Back to the main page ``` 🔵 You can also use a context manager to act inside iframes: ```python with self.frame_switch("iframe"): # ... Now perform actions while inside the code block # You have left the iframe ``` This also works with nested iframes: ```python with self.frame_switch('iframe[name="frame1"]'): with self.frame_switch('iframe[name="frame2"]'): # ... Now perform actions while inside the code block # You are now back inside the first iframe # You have left all the iframes ```

🔵 How to execute custom jQuery scripts:

jQuery is a powerful JavaScript library that allows you to perform advanced actions in a web browser. If the web page you're on already has jQuery loaded, you can start executing jQuery scripts immediately. You'd know this because the web page would contain something like the following in the HTML:

```html ``` 🔵 It's OK if you want to use jQuery on a page that doesn't have it loaded yet. To do so, run the following command first: ```python self.activate_jquery() ```
▶️ Here are some examples of using jQuery in your scripts. (click to expand) ```python self.execute_script("jQuery, window.scrollTo(0, 600)") # Scrolling the page self.execute_script("jQuery('#annoying-widget').hide()") # Hiding elements on a page self.execute_script("jQuery('#hidden-widget').show(0)") # Showing hidden elements on a page self.execute_script("jQuery('#annoying-button a').remove()") # Removing elements on a page self.execute_script("jQuery('%s').mouseover()" % (mouse_over_item)) # Mouse-over elements on a page self.execute_script("jQuery('input#the_id').val('my_text')") # Fast text input on a page self.execute_script("jQuery('div#dropdown a.link').click()") # Click elements on a page self.execute_script("return jQuery('div#amazing')[0].text") # Returns the css "text" of the element given self.execute_script("return jQuery('textarea')[2].value") # Returns the css "value" of the 3rd textarea element on the page ``` (Most of the above commands can be done directly with built-in SeleniumBase methods.)

🔵 How to handle a restrictive CSP:

❗ Some websites have a restrictive [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to prevent users from loading jQuery and other external libraries onto their websites. If you need to use jQuery or another JS library on those websites, add `--disable-csp` as a `pytest` command-line option to load a Chromium extension that bypasses the CSP.

🔵 More JavaScript fun:

▶️ In this example, JavaScript creates a referral button on a page, which is then clicked. (click to expand) ```python start_page = "https://xkcd.com/465/" destination_page = "https://github.com/seleniumbase/SeleniumBase" self.open(start_page) referral_link = '''Free-Referral Button!''' % destination_page self.execute_script('''document.body.innerHTML = \"%s\"''' % referral_link) self.click("a.analytics") # Clicks the generated button ``` (Due to popular demand, this traffic generation example has been included in SeleniumBase with the self.generate_referral(start_page, end_page) and the self.generate_traffic(start_page, end_page, loops) methods.)

🔵 How to use deferred asserts:

Let's say you want to verify multiple different elements on a web page in a single test, but you don't want the test to fail until you verified several elements at once so that you don't have to rerun the test to find more missing elements on the same page. That's where deferred asserts come in. Here's an example:

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DeferredAssertTests(BaseCase): def test_deferred_asserts(self): self.open("https://xkcd.com/993/") self.wait_for_element("#comic") self.deferred_assert_element('img[alt="Brand Identity"]') self.deferred_assert_element('img[alt="Rocket Ship"]') # Will Fail self.deferred_assert_element("#comicmap") self.deferred_assert_text("Fake Item", "ul.comicNav") # Will Fail self.deferred_assert_text("Random", "ul.comicNav") self.deferred_assert_element('a[name="Super Fake !!!"]') # Will Fail self.deferred_assert_exact_text("Brand Identity", "#ctitle") self.deferred_assert_exact_text("Fake Food", "#comic") # Will Fail self.process_deferred_asserts() ``` deferred_assert_element() and deferred_assert_text() will save any exceptions that would be raised. To flush out all the failed deferred asserts into a single exception, make sure to call self.process_deferred_asserts() at the end of your test method. If your test hits multiple pages, you can call self.process_deferred_asserts() before navigating to a new page so that the screenshot from your log files matches the URL where the deferred asserts were made.

🔵 How to access raw WebDriver:

If you need access to any commands that come with standard WebDriver, you can call them directly like this:

```python self.driver.delete_all_cookies() capabilities = self.driver.capabilities self.driver.find_elements("partial link text", "GitHub") ``` (In general, you'll want to use the SeleniumBase versions of methods when available.)

🔵 How to retry failing tests automatically:

You can use pytest --reruns=NUM to retry failing tests that many times. Add --reruns-delay=SECONDS to wait that many seconds between retries. Example:

```zsh pytest --reruns=1 --reruns-delay=1 ```

You can use the @retry_on_exception() decorator to retry failing methods. (First import: from seleniumbase import decorators). To learn more about SeleniumBase decorators, click here.

-------- > "Catch bugs in QA before deploying code to Production!"

Catch bugs in QA before deploying code to Production!

--------

Wrap-Up

If you see something, say something!

SeleniumBase

SeleniumBase Playlist on YouTube SeleniumBase on GitHub SeleniumBase on Discord SeleniumBase on Facebook

https://github.com/mdmintz

SeleniumBase Docs
Tested with SeleniumBase Views
SeleniumBase PyPI downloads
================================================ FILE: SECURITY.md ================================================ # Security Policy ## Reporting a Vulnerability If you've found a security vulnerability in SeleniumBase, (or a dependency we use), please open an issue. [github.com/seleniumbase/SeleniumBase/issues](https://github.com/seleniumbase/SeleniumBase/issues) Please describe the results you're seeing, and the results you're expecting. ================================================ FILE: _config.yml ================================================ theme: jekyll-theme-cayman title: SeleniumBase description: Reliable Browser Automation & Testing ================================================ FILE: azure-pipelines.yml ================================================ # Test the SeleniumBase Python package with Azure Pipelines. # https://docs.microsoft.com/azure/devops/pipelines/languages/python trigger: - master jobs: - job: 'Test' pool: vmImage: 'Ubuntu-22.04' strategy: matrix: Python3_7: python.version: '3.7' Python3_8: python.version: '3.8' Python3_9: python.version: '3.9' Python3_10: python.version: '3.10' Python3_11: python.version: '3.11' maxParallel: 5 steps: - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' architecture: 'x64' - script: python -m pip install --upgrade pip && pip --version displayName: 'Install/upgrade pip' - script: python -m pip install seleniumbase displayName: 'Verify install from PyPI' #- script: python -m pip install -r requirements.txt --upgrade # displayName: 'Install dependencies' - script: python -m pip install -e . displayName: 'Install SeleniumBase' - script: | sudo apt install google-chrome-stable displayName: 'Install Chrome' #- script: | # sudo apt-get install firefox # displayName: 'Install Firefox' - script: | seleniumbase sbase displayName: 'Check the console scripts interface' - script: | seleniumbase install chromedriver displayName: 'Install chromedriver' - script: | echo "def test_1(): pass" > nothing.py pytest nothing.py displayName: 'Make sure pytest is working' #- script: python -m pytest examples/unit_tests/verify_framework.py # displayName: 'Run pytest verify_framework.py' - script: python -m pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml displayName: 'Run pytest boilerplate_test.py --browser=chrome --headless' #- script: python -m pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml # displayName: 'Run pytest boilerplate_test.py --browser=firefox --headless' #- script: python -m pytest examples/test_demo_site.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml # displayName: 'Run pytest test_demo_site.py --browser=chrome --headless' #- script: python -m pytest examples/my_first_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml # displayName: 'Run pytest my_first_test.py --browser=chrome --headless' #- script: python -m pytest examples/test_inspect_html.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml # displayName: 'Run pytest test_inspect_html.py --browser=chrome --headless' - task: PublishTestResults@2 inputs: testResultsFiles: '**/test-results.xml' testRunTitle: 'Python $(python.version)' condition: succeededOrFailed() #- job: 'Publish' # dependsOn: 'Test' # pool: # vmImage: 'Ubuntu-22.04' # steps: # - task: UsePythonVersion@0 # inputs: # versionSpec: '3.x' # architecture: 'x64' # - script: python setup.py sdist # displayName: 'Build sdist' ================================================ FILE: examples/ReadMe.md ================================================

Example Tests:

SeleniumBase Demo Page

* SeleniumBase "tests" are run with pytest. * Chrome is the default browser if not specified. * Tests are structured using [25 unique syntax formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md). * Logs from test failures are saved to `./latest_logs/`. * Tests can be run with [multiple command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md). * Examples can be found in [SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples). * For stealthy examples, see [SeleniumBase/examples/cdp_mode/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode). (NOTE: Some example tests fail on purpose to demonstrate [logging features](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md).) --------

Example tests with run commands to help you get started:

-------- Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py): (Default option: `--chrome`) ```zsh pytest my_first_test.py ```
-------- Here's one way of changing the browser to Firefox: ```zsh pytest my_first_test.py --firefox ``` -------- Another [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py) for a web page that has lots of different HTML items: ```zsh pytest test_demo_site.py ```
-------- Run an example test in `--demo` mode: (highlight assertions) ```zsh pytest test_swag_labs.py --demo ```
-------- Run [test_coffee_cart.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py) to test the [Coffee Cart](https://seleniumbase.io/coffee/) app: ```zsh pytest test_coffee_cart.py --demo ``` SeleniumBase Example You can debug tests easily with the included `pdbp` (Pdb+) debugger: ```zsh pytest test_coffee_cart.py --trace ```

SeleniumBase test with the pdbp debugger

-------- Run a [Wordle-solver example](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/wordle_test.py): ```zsh pytest wordle_test.py ```
-------- Run an example test in `--headless` mode: (invisible browser) ```zsh pytest my_first_test.py --headless ``` -------- Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_swag_labs.py) using Chrome's mobile device emulator: (default settings) ```zsh pytest test_swag_labs.py --mobile ```
-------- Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_xkcd.py) in `--demo` mode: (highlight assertions) ```zsh pytest test_xkcd.py --demo ```
-------- Run a [test suite](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_suite.py) with verbose output: (see more details) ```zsh pytest test_suite.py -v ``` -------- Run a test suite using multiple parallel processes (`-n=NUM`): ```zsh pytest test_suite.py -n=8 ``` -------- Run a [parameterized test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/parameterized_test.py): (Generates multiple tests from one) ```zsh pytest parameterized_test.py -v ``` -------- Run a test suite and generate a SeleniumBase Dashboard: ```zsh pytest test_suite.py --dashboard ``` -------- Run a test suite and generate a `pytest` report: ```zsh pytest test_suite.py --html=report.html ``` -------- Run a [failing test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_fail.py): (See the `latest_logs/` folder for logs and screenshots) ```zsh pytest test_fail.py ``` -------- Run a failing test that activates `pdb` debug mode on failure: ```zsh pytest test_fail.py --pdb -s ``` > (**`pdb`** commands: `n`, `c`, `s`, `u`, `d` => `next`, `continue`, `step`, `up`, `down`) -------- Run a test suite that demonstrates the use of `pytest` markers: ```zsh pytest -m marker_test_suite -v ``` -------- Run a test suite that reuses the browser session between tests: ```zsh pytest test_suite.py --rs ``` -------- Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/rate_limiting_test.py) demonstrating the `rate_limited` Python decorator: ```zsh pytest rate_limiting_test.py ``` -------- Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/upload_file_test.py) that demonstrates how to upload a file to a website: ```zsh pytest upload_file_test.py ``` -------- 🎖️ **SeleniumBase Commander** is a GUI for `pytest`: ```zsh sbase gui ```
-------- SeleniumBase tests can also be run with `pynose`: ```zsh pynose my_first_test.py ``` -------- Run an example test suite and generate a `pynose` test report: ```zsh pynose test_suite.py --report --show-report ``` -------- Run an example test using a `pynose` configuration file: ```zsh pynose my_first_test.py --config=example_config.cfg ``` -------- For more advanced **run commands**, such as using a proxy server, see [../help_docs/customizing_test_runs.md](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) -------- If you just need to perform some quick website verification on various devices, you can use the SeleniumBase Device Farm. Just plug in a website URL, and it will display how the website looks on four different devices:
-------- To make things easier, here's a **simple GUI program** that allows you to run a few example tests by pressing a button: ```zsh python gui_test_runner.py ``` (The newer **[SeleniumBase Commander](https://seleniumbase.io/help_docs/commander/)** improves on that.) --------

Join the SeleniumBase chat on Discord Join the SeleniumBase chat on Discord!

Ask questions. Find answers. Learn how to automate! -------- Tested with SeleniumBase ================================================ FILE: examples/__init__.py ================================================ ================================================ FILE: examples/basic_test.py ================================================ """Add an item to a shopping cart. Verify. Remove item. Verify.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_basics(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click('button:contains("Remove")') # HTML innerText self.assert_text_not_visible("Backpack", "div.cart_item") self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") ================================================ FILE: examples/behave_bdd/ReadMe.md ================================================ ## [](https://github.com/seleniumbase/SeleniumBase/) 🐝 Behave test runner for SeleniumBase 🐝 🐝 (Utilizes the [Behave BDD Python library](https://github.com/behave/behave). For more info, see the [Behave tutorial](https://behave.readthedocs.io/en/stable/tutorial/) and read about [Behave's Gherkin model](https://behave.readthedocs.io/en/stable/gherkin/).) 🐝 Behave examples with SeleniumBase: [SeleniumBase/examples/behave_bdd](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd) ```zsh > cd examples/behave_bdd/ > behave features/realworld.feature -T -D dashboard -k Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html ******************************************************************************** Feature: SeleniumBase scenarios for the RealWorld App # features/realworld.feature:1 Scenario: Verify RealWorld App (log in / sign out) # features/realworld.feature:3 Given Open "seleniumbase.io/realworld/login" # ../../sbase/steps.py:10 And Clear Session Storage # ../../sbase/steps.py:669 When Type "demo_user" into "#username" # ../../sbase/steps.py:40 And Type "secret_pass" into "#password" # ../../sbase/steps.py:40 And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" # ../../sbase/steps.py:322 Then Assert exact text "Welcome!" in "h1" # ../../sbase/steps.py:157 And Highlight "img#image1" # ../../sbase/steps.py:184 And Click 'a:contains("This Page")' # ../../sbase/steps.py:27 And Save screenshot to logs # ../../sbase/steps.py:239 When Click link "Sign out" # ../../sbase/steps.py:195 Then Assert element 'a:contains("Sign in")' # ../../sbase/steps.py:120 And Assert text "You have been signed out!" # ../../sbase/steps.py:145 ✅ Scenario Passed! - Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html --- LogPath: /Users/michael/github/SeleniumBase/examples/behave_bdd/latest_logs/ ================================================================================== 1 feature passed, 0 failed, 0 skipped 1 scenario passed, 0 failed, 0 skipped 12 steps passed, 0 failed, 0 skipped, 0 undefined Took 0m4.682s ``` 🐝 Another example, which uses higher-level Behave steps to simplify the ``.feature`` file: ```zsh > cd examples/behave_bdd/ > behave features/calculator.feature:61 -T -D dashboard -k Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html ******************************************************************************** Feature: SeleniumBase scenarios for the Calculator App # features/calculator.feature:1 Background: # features/calculator.feature:3 Scenario: 7.0 × (3 + 3) = 42 # features/calculator.feature:49 Given Open the Calculator App # features/steps/calculator.py:4 When Press C # features/steps/calculator.py:9 And Press 7 # features/steps/calculator.py:79 And Press . # features/steps/calculator.py:104 And Press 0 # features/steps/calculator.py:94 And Press × # features/steps/calculator.py:29 And Press ( # features/steps/calculator.py:14 And Press 3 # features/steps/calculator.py:59 And Press + # features/steps/calculator.py:39 And Press 3 # features/steps/calculator.py:59 And Press ) # features/steps/calculator.py:19 Then Verify output is "7.0×(3+3)" # features/steps/calculator.py:135 When Press = # features/steps/calculator.py:44 Then Verify output is "42" # features/steps/calculator.py:135 ✅ Scenario Passed! - Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html --- LogPath: /Users/michael/github/SeleniumBase/examples/behave_bdd/latest_logs/ ================================================================================== 1 feature passed, 0 failed, 0 skipped 1 scenario passed, 0 failed, 8 skipped 14 steps passed, 0 failed, 60 skipped, 0 undefined Took 0m1.672s ``` 🐝⚪ With the Dashboard enabled, you'll get one of these: ### 🐝 Behave-Gherkin files: 🐝 The ``*.feature`` files can use any step seen from: ```zsh behave --steps-catalog ``` 🐝 SeleniumBase includes several pre-made Behave steps, which you can use by creating a Python file with the following line in your ``features/steps/`` directory: ```python from seleniumbase.behave import steps # noqa ``` 🐝 Inside your ``features/environment.py`` file, you should have the following: ```python from seleniumbase import BaseCase from seleniumbase.behave import behave_sb behave_sb.set_base_class(BaseCase) # Accepts a BaseCase subclass from seleniumbase.behave.behave_sb import before_all # noqa from seleniumbase.behave.behave_sb import before_feature # noqa from seleniumbase.behave.behave_sb import before_scenario # noqa from seleniumbase.behave.behave_sb import before_step # noqa from seleniumbase.behave.behave_sb import after_step # noqa from seleniumbase.behave.behave_sb import after_scenario # noqa from seleniumbase.behave.behave_sb import after_feature # noqa from seleniumbase.behave.behave_sb import after_all # noqa ``` 🐝 If you've already created a subclass of ``BaseCase`` with custom methods, you can swap ``BaseCase`` in with your own subclass, which will allow you to easily use your own custom methods in your Behave step definitions. 🐝 Here's an example Python file in the ``features/steps/`` folder: ```python from behave import step @step("Open the Swag Labs Login Page") def go_to_swag_labs(context): sb = context.sb sb.open("https://www.saucedemo.com") sb.clear_local_storage() @step("Login to Swag Labs with {user}") def login_to_swag_labs(context, user): sb = context.sb sb.type("#user-name", user) sb.type("#password", "secret_sauce\n") @step("Verify that the current user is logged in") def verify_logged_in(context): sb = context.sb sb.assert_element("#header_container") sb.assert_element("#react-burger-menu-btn") sb.assert_element("#shopping_cart_container") @step('Add "{item}" to cart') def add_item_to_cart(context, item): sb = context.sb sb.click('div.inventory_item:contains("%s") button[name*="add"]' % item) ``` 🐝 A ``*.feature`` file could look like this: ```gherkin Feature: SeleniumBase scenarios for the Swag Labs App Background: Given Open the Swag Labs Login Page Scenario: User can order a backpack from the store When Login to Swag Labs with standard_user Then Verify that the current user is logged in And Save price of "Backpack" to When Add "Backpack" to Cart Then Verify shopping cart badge shows 1 item(s) When Click on shopping cart icon And Click Checkout And Enter checkout info: First, Last, 12345 And Click Continue Then Verify 1 "Backpack"(s) in cart And Verify cost of "Backpack" is And Verify item total is $29.99 And Verify tax amount is $2.40 And Verify total cost is $32.39 When Click Finish Then Verify order complete When Logout from Swag Labs Then Verify on Login page ``` 🐝 Here's another example of a ``*.feature`` file: ```gherkin Feature: SeleniumBase scenarios for the RealWorld App Scenario: Verify RealWorld App (log in / sign out) Given Open "seleniumbase.io/realworld/login" And Clear Session Storage When Type "demo_user" into "#username" And Type "secret_pass" into "#password" And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" Then Assert text "Welcome!" in "h1" And Highlight element "img#image1" And Click 'a:contains("This Page")' And Save screenshot to logs When Click link "Sign out" Then Assert element 'a:contains("Sign in")' And Assert text "You have been signed out!" ``` 🐝 If there's a test failure, that's easy to spot: ```zsh Feature: SeleniumBase scenarios for the Fail Page # features/fail_page.feature:1 Scenario: Fail test on purpose to see what happens # features/fail_page.feature:3 When Open the Fail Page # features/steps/fail_page.py:4 Then Fail test on purpose # features/steps/fail_page.py:9 Assertion Failed: This test fails on purpose! Captured stdout: >>> STEP FAILED: (#2) Fail test on purpose Class / Feature: SeleniumBase scenarios for the Fail Page Test / Scenario: Fail test on purpose to see what happens ❌ Scenario Failed! ``` 🐝🎖️ For convenience, the [SeleniumBase Behave GUI](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/behave_gui.md) lets you run ``behave`` scripts from a Desktop app. 🐝🎖️ To launch it, call ``sbase behave-gui`` or ``sbase gui-behave``: ```zsh sbase behave-gui * Starting the SeleniumBase Behave Commander GUI App... ``` 🐝🎖️ You can customize the tests that show up there: ```zsh sbase behave-gui # all tests sbase behave-gui -i=calculator # tests with "calculator" in the name sbase behave-gui features/ # tests located in the "features/" folder sbase behave-gui features/calculator.feature # tests in that feature ``` --------
To learn more about SeleniumBase, check out the Docs Site:
SeleniumBase.io Docs
All the code is on GitHub:
SeleniumBase on GitHub ================================================ FILE: examples/behave_bdd/__init__.py ================================================ ================================================ FILE: examples/behave_bdd/behave.ini ================================================ [behave] show_skipped=false show_timings=false ================================================ FILE: examples/behave_bdd/features/__init__.py ================================================ ================================================ FILE: examples/behave_bdd/features/behave.ini ================================================ [behave] show_skipped=false show_timings=false ================================================ FILE: examples/behave_bdd/features/calculator.feature ================================================ Feature: SeleniumBase scenarios for the Calculator App Background: Given Open the Calculator App Scenario: Pressing "C" outputs "0" When Press C Then Verify output is "0" Scenario: 1 + 2 + 3 + 4 + 5 = 15 When Press C And Press 1 And Press + And Press 2 And Press + And Press 3 And Press + And Press 4 And Press + And Press 5 Then Verify output is "1+2+3+4+5" When Press = Then Verify output is "15" Scenario: 6 × 7 × 8 × 9 = 3024 When Press C And Press 6 And Press × And Press 7 And Press × And Press 8 And Press × And Press 9 Then Verify output is "6×7×8×9" When Press = Then Verify output is "3024" Scenario: 44 - 11 = 33 When Press C And Press 4 And Press 4 And Press - And Press 1 And Press 1 Then Verify output is "44-11" When Press = Then Verify output is "33" Scenario: 7.0 × (3 + 3) = 42 When Press C And Press 7 And Press . And Press 0 And Press × And Press ( And Press 3 And Press + And Press 3 And Press ) Then Verify output is "7.0×(3+3)" When Press = Then Verify output is "42" Scenario: 4.5 × 68 = 306 When Press C And Evaluate [4.5 × 68] Then Verify output is "306" Scenario Outline: ÷ = When Press C And Press [] And Press ÷ And Press [] And Press = Then Verify output is "" Examples: | First | Second | Result | | 1948 | 4 | 487 | | 21 | 0 | Error | Scenario: Save calculator screenshot to logs Given Press [1337] Given Save calculator screenshot to logs ================================================ FILE: examples/behave_bdd/features/environment.py ================================================ from seleniumbase import BaseCase from seleniumbase.behave import behave_sb behave_sb.set_base_class(BaseCase) # Accepts a BaseCase subclass from seleniumbase.behave.behave_sb import before_all # noqa from seleniumbase.behave.behave_sb import before_feature # noqa from seleniumbase.behave.behave_sb import before_scenario # noqa from seleniumbase.behave.behave_sb import before_step # noqa from seleniumbase.behave.behave_sb import after_step # noqa from seleniumbase.behave.behave_sb import after_scenario # noqa from seleniumbase.behave.behave_sb import after_feature # noqa from seleniumbase.behave.behave_sb import after_all # noqa ================================================ FILE: examples/behave_bdd/features/fail_page.feature ================================================ Feature: SeleniumBase scenarios for the Fail Page Scenario: Fail test on purpose to see what happens When Open the Fail Page Then Fail test on purpose ================================================ FILE: examples/behave_bdd/features/login_app.feature ================================================ Feature: SeleniumBase scenarios for the Simple App Scenario: Verify the Simple App (Login / Logout) Given Open "seleniumbase.io/simple/login" And Type "demo_user" into "#username" And Type "secret_pass" into "#password" And Click 'a:contains("Sign in")' And Assert exact text "Welcome!" in "h1" And Assert element "img#image1" And Highlight "#image1" And Click link "Sign out" And Assert text "signed out" in "#top_message" ================================================ FILE: examples/behave_bdd/features/realworld.feature ================================================ Feature: SeleniumBase scenarios for the RealWorld App Scenario: Verify RealWorld App (log in / sign out) Given Open "seleniumbase.io/realworld/login" And Clear Session Storage When Type "demo_user" into "#username" And Type "secret_pass" into "#password" And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" Then Assert exact text "Welcome!" in "h1" And Highlight "img#image1" And Click 'a:contains("This Page")' And Save screenshot to logs When Click link "Sign out" Then Assert element 'a:contains("Sign in")' And Assert text "You have been signed out!" ================================================ FILE: examples/behave_bdd/features/steps/__init__.py ================================================ ================================================ FILE: examples/behave_bdd/features/steps/calculator.py ================================================ from behave import step @step("Open the Calculator App") def go_to_calculator(context): context.sb.open("https://seleniumbase.io/apps/calculator") @step("Press C") def press_c(context): context.sb.click("button#clear") @step("Press (") def press_open_paren(context): context.sb.click('button[id="("]') @step("Press )") def press_close_paren(context): context.sb.click('button[id=")"]') @step("Press ÷") def press_divide(context): context.sb.click("button#divide") @step("Press ×") def press_multiply(context): context.sb.click("button#multiply") @step("Press -") def press_subtract(context): context.sb.click("button#subtract") @step("Press +") def press_add(context): context.sb.click("button#add") @step("Press =") def press_equal(context): context.sb.click("button#equal") @step("Press 1") def press_1(context): context.sb.click('button[id="1"]') @step("Press 2") def press_2(context): context.sb.click('button[id="2"]') @step("Press 3") def press_3(context): context.sb.click('button[id="3"]') @step("Press 4") def press_4(context): context.sb.click('button[id="4"]') @step("Press 5") def press_5(context): context.sb.click('button[id="5"]') @step("Press 6") def press_6(context): context.sb.click('button[id="6"]') @step("Press 7") def press_7(context): context.sb.click('button[id="7"]') @step("Press 8") def press_8(context): context.sb.click('button[id="8"]') @step("Press 9") def press_9(context): context.sb.click('button[id="9"]') @step("Press 0") def press_0(context): context.sb.click('button[id="0"]') @step("Press ←") def press_delete(context): context.sb.click("button#delete") @step("Press .") def press_dot(context): context.sb.click('button[id="."]') @step("Press [{number}]") def enter_number_into_calc(context, number): sb = context.sb for digit in number: sb.click('button[id="%s"]' % digit) @step("Evaluate [{equation}]") def evaluate_equation(context, equation): sb = context.sb for key in equation: if key == " ": continue elif key == "÷": sb.click("button#divide") elif key == "×": sb.click("button#multiply") elif key == "-": sb.click("button#subtract") elif key == "+": sb.click("button#add") else: sb.click('button[id="%s"]' % key) sb.click("button#equal") @step('Verify output is "{output}"') def verify_output(context, output): sb = context.sb sb.assert_exact_text(output, "#output") @step("Save calculator screenshot to logs") def save_calculator_screenshot_to_logs(context): sb = context.sb sb.save_screenshot_to_logs() ================================================ FILE: examples/behave_bdd/features/steps/fail_page.py ================================================ from behave import step @step("Open the Fail Page") def go_to_error_page(context): context.sb.open("https://seleniumbase.io/error_page/") @step("Fail test on purpose") def fail_on_purpose(context): context.sb.fail("This test fails on purpose!") ================================================ FILE: examples/behave_bdd/features/steps/imported.py ================================================ from seleniumbase.behave import steps # noqa ================================================ FILE: examples/behave_bdd/features/steps/swag_labs.py ================================================ from behave import step @step("Open the Swag Labs Login Page") def go_to_swag_labs(context): sb = context.sb sb.open("https://www.saucedemo.com") sb.clear_local_storage() @step("Login to Swag Labs with {user}") def login_to_swag_labs(context, user): sb = context.sb sb.type("#user-name", user) sb.type("#password", "secret_sauce\n") @step("Verify that the current user is logged in") def verify_logged_in(context): sb = context.sb sb.assert_element("#header_container") sb.assert_element("#react-burger-menu-btn") sb.assert_element("#shopping_cart_container") @step('Add "{item}" to cart') def add_item_to_cart(context, item): sb = context.sb sb.click('div.inventory_item:contains("%s") button[name*="add"]' % item) @step('Save price of "{item}" to <{var}>') def save_price_of_item(context, item, var): sb = context.sb price = sb.get_text( 'div.inventory_item:contains("%s") .inventory_item_price' % item ) sb.variables[var] = price @step('Remove "{item}" from cart') def remove_item_to_cart(context, item): sb = context.sb sb.click('div.inventory_item:contains("%s") button[name*="remove"]' % item) @step("Verify shopping cart badge shows {number} item(s)") def verify_badge_number(context, number): sb = context.sb sb.assert_exact_text(number, "span.shopping_cart_badge") @step("Verify shopping cart badge is missing") def verify_badge_missing(context): sb = context.sb sb.assert_element_not_visible("span.shopping_cart_badge") @step("Click on shopping cart icon") def click_shopping_cart(context): sb = context.sb sb.click("#shopping_cart_container a") @step("Click Checkout") def click_checkout(context): sb = context.sb sb.click("#checkout") @step("Enter checkout info: {first_name}, {last_name}, {zip_code}") def enter_checkout_info(context, first_name, last_name, zip_code): sb = context.sb sb.type("#first-name", first_name) sb.type("#last-name", last_name) sb.type("#postal-code", zip_code) @step("Click Continue") def click_continue(context): sb = context.sb sb.click("input#continue") @step('Verify {quantity} "{item}"(s) in cart') def verify_item_in_cart(context, quantity, item): sb = context.sb sb.assert_exact_text( quantity, 'div.cart_item:contains("%s") div.cart_quantity' % item ) @step('Verify cost of "{item}" is <{var}>') def verify_cost_of_item(context, item, var): sb = context.sb earlier_price = sb.variables[var] sb.assert_exact_text( earlier_price, 'div.cart_item_label:contains("%s") .inventory_item_price' % item, ) @step("Verify item total is {item_total}") def verify_item_total(context, item_total): sb = context.sb sb.assert_exact_text( "Item total: %s" % item_total, "div.summary_subtotal_label", timeout=1 ) @step("Verify tax amount is {tax_amount}") def verify_tax_amount(context, tax_amount): sb = context.sb sb.assert_exact_text( "Tax: %s" % tax_amount, "div.summary_tax_label", timeout=1 ) @step("Verify total cost is {total_cost}") def verify_total_cost(context, total_cost): sb = context.sb sb.assert_exact_text( "Total: %s" % total_cost, "div.summary_total_label", timeout=1 ) @step("Click Finish") def click_finish(context): sb = context.sb sb.click("button#finish") @step("Verify order complete") def verify_order_complete(context): sb = context.sb sb.assert_exact_text("Thank you for your order!", "h2") sb.assert_element('img[alt="Pony Express"]') @step("Logout from Swag Labs") def logout_from_swag_labs(context): sb = context.sb sb.js_click("a#logout_sidebar_link") @step("Verify on Login page") def verify_on_login_page(context): sb = context.sb sb.assert_element("#login-button") @step("Sort items from Z to A") def sort_items_from_z_to_a(context): sb = context.sb sb.select_option_by_text("select.product_sort_container", "Name (Z to A)") @step('Verify "{item}" on top') def verify_item_on_top(context, item): sb = context.sb sb.assert_text(item, "div.inventory_item_name") ================================================ FILE: examples/behave_bdd/features/swag_labs.feature ================================================ Feature: SeleniumBase scenarios for the Swag Labs App Background: Given Open the Swag Labs Login Page Scenario: User can log in and log out successfully When Login to Swag Labs with standard_user Then Verify that the current user is logged in When Logout from Swag Labs Then Verify on Login page Scenario: User can order a backpack from the store When Login to Swag Labs with standard_user Then Verify that the current user is logged in And Save price of "Backpack" to When Add "Backpack" to Cart Then Verify shopping cart badge shows 1 item(s) When Click on shopping cart icon And Click Checkout And Enter checkout info: First, Last, 12345 And Click Continue Then Verify 1 "Backpack"(s) in cart And Verify cost of "Backpack" is And Verify item total is $29.99 And Verify tax amount is $2.40 And Verify total cost is $32.39 When Click Finish Then Verify order complete When Logout from Swag Labs Then Verify on Login page Scenario: User can order two items from the store When Login to Swag Labs with standard_user And Add "Bike Light" to Cart And Add "Fleece Jacket" to Cart Then Verify shopping cart badge shows 2 item(s) When Click on shopping cart icon And Click Checkout And Enter checkout info: First, Last, 54321 And Click Continue Then Verify 1 "Bike Light"(s) in cart Then Verify 1 "Fleece Jacket"(s) in cart And Verify item total is $59.98 And Verify tax amount is $4.80 And Verify total cost is $64.78 When Click Finish Then Verify order complete When Logout from Swag Labs Then Verify on Login page Scenario: User can sort items by name from Z to A When Login to Swag Labs with standard_user And Sort items from Z to A Then Verify "Test.allTheThings() T-Shirt" on top When Logout from Swag Labs Then Verify on Login page Scenario: User can add & remove 6 items to/from cart When Login to Swag Labs with standard_user And Add "Backpack" to Cart And Add "Bike Light" to Cart And Add "Bolt T-Shirt" to Cart And Add "Fleece Jacket" to Cart And Add "Onesie" to Cart And Add "Test.allTheThings() T-Shirt" to Cart Then Verify shopping cart badge shows 6 item(s) When Remove "Backpack" from Cart And Remove "Bike Light" from Cart And Remove "Bolt T-Shirt" from Cart And Remove "Fleece Jacket" from Cart And Remove "Onesie" from Cart And Remove "Test.allTheThings() T-Shirt" from Cart Then Verify shopping cart badge is missing When Logout from Swag Labs Then Verify on Login page ================================================ FILE: examples/boilerplates/ReadMe.md ================================================

Example Boilerplates:

* Boilerplate files are located in the [SeleniumBase => examples/boilerplates/](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates) folder. * Boilerplates can help you structure tests using common design patterns such as the Page Object Model. * For all 20 SeleniumBase design patterns, see: [Syntax Formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md)

Boilerplate Files:

* [base_test_case.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/base_test_case.py): This example demonstrates a test class inheriting [BaseCase](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py) for adding new methods. * [page_objects.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/page_objects.py): This example demonstrates Page Objects for reusing commonly-used selectors in tests. * [boilerplate_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/boilerplate_test.py): This example demonstrates inheritance of the above files for making a complete test. * [classic_obj_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/classic_obj_test.py): This example demonstrates the classic Page Object Model for structuring tests. --------
SeleniumBase
================================================ FILE: examples/boilerplates/__init__.py ================================================ ================================================ FILE: examples/boilerplates/base_test_case.py ================================================ """Use this as a boilerplate for your test framework. Define customized library methods in a class like this. Then have your test classes inherit it. BaseTestCase inherits SeleniumBase methods from BaseCase.""" from seleniumbase import BaseCase class BaseTestCase(BaseCase): def setUp(self): super().setUp() # <<< Run custom setUp() code for tests AFTER the super().setUp() >>> def tearDown(self): self.save_teardown_screenshot() # If test fails, or if "--screenshot" if self.has_exception(): # <<< Run custom code if the test failed. >>> pass else: # <<< Run custom code if the test passed. >>> pass # (Wrap unreliable tearDown() code in a try/except block.) # <<< Run custom tearDown() code BEFORE the super().tearDown() >>> super().tearDown() def login(self): # <<< Placeholder. Add your code here. >>> # Reduce duplicate code in tests by having reusable methods like this. # If the UI changes, the fix can be applied in one place. pass def example_method(self): # <<< Placeholder. Add your code here. >>> pass """ # Now you can do something like this in your test files: from base_test_case import BaseTestCase class MyTests(BaseTestCase): def test_example(self): self.login() self.example_method() self.type("input", "Name") self.click("form button") ... """ ================================================ FILE: examples/boilerplates/boilerplate_test.py ================================================ try: # Run with "pytest" (relative imports are valid) from .base_test_case import BaseTestCase from .page_objects import Page except (ImportError, ValueError): # Run with "python" from base_test_case import BaseTestCase from page_objects import Page BaseTestCase.main(__name__, __file__) class MyTestClass(BaseTestCase): def test_boilerplate(self): self.login() self.example_method() self.assert_element(Page.html) ================================================ FILE: examples/boilerplates/classic_obj_test.py ================================================ """Classic Page Object Model with BaseCase inheritance.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DataPage: def go_to_data_url(self, sb): sb.open("data:text/html,

Hello!

") def add_input_text(self, sb, text): sb.type("input", text) class ObjTests(BaseCase): def test_data_url_page(self): DataPage().go_to_data_url(self) self.assert_text("Hello!", "p") DataPage().add_input_text(self, "Goodbye!") ================================================ FILE: examples/boilerplates/page_objects.py ================================================ """Example of using the Page Object Pattern in tests. Makes code more Readable, Maintainable, and Reusable. Import files like this at the top of your test files.""" class Page(object): html = "html" ok_button = "#ok" cancel_button = "#cancel" see_items_button = "button.items" class HomePage(object): see_items_button = "button.items" class ShoppingPage(object): buyable_item = 'img[alt="Item"]' add_to_cart = "button.add" go_to_checkout = "#checkout" class CheckoutPage(object): remove_from_cart = "button.remove" buy_now = "#buy-now" shop_more = "#shop-more" """ # Now you can do something like this in your test files: from .base_test_case import BaseTestCase from .page_objects import HomePage, ShoppingPage, CheckoutPage class MyTests(BaseTestCase): def test_example(self): self.login() self.click(HomePage.see_items_button) self.click(ShoppingPage.buyable_item) self.click(ShoppingPage.add_to_cart) self.click(CheckoutPage.buy_now) self.assert_element("#success") self.assert_text("Order Received!", "#h2") """ ================================================ FILE: examples/boilerplates/samples/__init__.py ================================================ ================================================ FILE: examples/boilerplates/samples/file_parsing/__init__.py ================================================ ================================================ FILE: examples/boilerplates/samples/file_parsing/parse_files.py ================================================ """Example of parsing data from files.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ParseTestCase(BaseCase): def get_login_credentials(self, user_type): # Example of parsing data from a file (Method 1) with open("qa_login_example.txt") as f: file_lines = [line.rstrip() for line in f] for line in file_lines: line_items = line.split(",") if line_items[0] == user_type: return line_items[1], line_items[2] def get_all_login_credentials(self): # Example of parsing data from a file (Method 2) keys = {} with open("staging_login_example.txt") as f: file_lines = [line.rstrip() for line in f] for line in file_lines: line_items = line.split(",") if line_items[0] == "admin": keys["admin"] = { "username": line_items[1], "password": line_items[2], } if line_items[0] == "employee": keys["employee"] = { "username": line_items[1], "password": line_items[2], } if line_items[0] == "customer": keys["customer"] = { "username": line_items[1], "password": line_items[2], } return keys class ParseTests(ParseTestCase): def test_get_login_credentials(self): print("\nExample 1 of getting login info from parsing a config file:") print("") username, password = self.get_login_credentials("admin") print("Getting Admin User login data:") print("Username: %s" % username) print("Password: %s" % password) print("\nExample 2 of getting login info from parsing a config file:") print("") keys = self.get_all_login_credentials() print("Getting Customer login data:") print("Username: %s" % keys["customer"]["username"]) print("Password: %s" % keys["customer"]["password"]) ================================================ FILE: examples/boilerplates/samples/file_parsing/qa_login_example.txt ================================================ admin,admin_username_qa,admin_password_qa employee,employee_username_qa,employee_password_qa customer,customer_username_qa,customer_password_qa ================================================ FILE: examples/boilerplates/samples/file_parsing/staging_login_example.txt ================================================ admin,admin_username_staging,admin_password_staging employee,employee_username_staging,employee_password_staging customer,customer_username_staging,customer_password_staging ================================================ FILE: examples/boilerplates/samples/google_objects.py ================================================ """google.com page objects""" class HomePage(object): dialog_box = '[role="dialog"] div' search_box = '[title="Search"]' search_button = 'input[value="Google Search"]' feeling_lucky_button = """input[value="I'm Feeling Lucky"]""" class ResultsPage(object): search_results = "div#center_col" ================================================ FILE: examples/boilerplates/samples/google_test.py ================================================ """google.com example test that uses page objects""" from seleniumbase import BaseCase try: from .google_objects import HomePage, ResultsPage except Exception: from google_objects import HomePage, ResultsPage BaseCase.main(__name__, __file__, "--uc") class GoogleTests(BaseCase): def test_google_dot_com(self): if self.headless: self.open_if_not_url("about:blank") print("\n Skipping test in headless mode.") self.skip("Skipping test in headless mode.") if not self.undetectable: self.get_new_driver(undetectable=True) self.driver.get("https://google.com/ncr") self.assert_title_contains("Google") self.sleep(0.05) self.save_screenshot_to_logs() # ("./latest_logs" folder) self.type(HomePage.search_box, "github.com") self.assert_element(HomePage.search_button) self.assert_element(HomePage.feeling_lucky_button) self.click(HomePage.search_button) self.assert_text("github.com", ResultsPage.search_results) ================================================ FILE: examples/boilerplates/samples/sb_swag_test.py ================================================ """Classic Page Object Model with the "sb" fixture.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class LoginPage: def login_to_swag_labs(self, sb: BaseCase, username): sb.open("https://www.saucedemo.com") sb.type("#user-name", username) sb.type("#password", "secret_sauce") sb.click('input[type="submit"]') class MyTests: def test_swag_labs_login(self, sb: BaseCase): LoginPage().login_to_swag_labs(sb, "standard_user") sb.assert_element("div.inventory_list") sb.assert_element('div:contains("Sauce Labs Backpack")') sb.js_click("a#logout_sidebar_link") sb.assert_element("div#login_button_container") ================================================ FILE: examples/boilerplates/samples/swag_labs_test.py ================================================ """Classic Page Object Model with BaseCase inheritance.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class LoginPage: def login_to_swag_labs(self, sb: BaseCase, username): sb.open("https://www.saucedemo.com") sb.type("#user-name", username) sb.type("#password", "secret_sauce") sb.click('input[type="submit"]') class MyTests(BaseCase): def test_swag_labs_login(self): LoginPage().login_to_swag_labs(self, "standard_user") self.assert_element("div.inventory_list") self.assert_element('div:contains("Sauce Labs Backpack")') self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") ================================================ FILE: examples/boilerplates/samples/test_page_objects.py ================================================ """An example using the Classic Page Object Model.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class GooglePage: def go_to_google(self, sb): sb.driver.get("https://google.com/ncr") def assert_google_title(self, sb): sb.assert_title_contains("Google") def hide_sign_in_pop_up(self, sb): if not sb.is_element_visible("iframe"): sb.sleep(1.5) # A slow pop-up might appear sb.hide_elements('iframe') sb.sleep(0.05) def do_search(self, sb, search_term): sb.sleep(0.05) sb.click('[title="Search"]') sb.type('[title="Search"]', search_term + "\n") def click_search_result(self, sb, content): sb.click('a:contains("%s")' % content) class SeleniumBaseIOPage: def do_search_and_click(self, sb, search_term): sb.sleep(0.05) sb.type('form[name="search"] input', search_term) sb.click("li.md-search-result__item h1:contains(%s)" % search_term) class MyTests(BaseCase): def test_page_objects(self): if self.headless: self.open_if_not_url("about:blank") print("\n Skipping test in headless mode.") self.skip("Skipping test in headless mode.") if not self.undetectable: self.get_new_driver(undetectable=True) search_term = "SeleniumBase.io Docs" expected_text = "SeleniumBase" GooglePage().go_to_google(self) GooglePage().assert_google_title(self) GooglePage().hide_sign_in_pop_up(self) GooglePage().do_search(self, search_term) self.assert_text(expected_text, "#search") GooglePage().click_search_result(self, expected_text) SeleniumBaseIOPage().do_search_and_click(self, "Dashboard") self.assert_text("Dashboard", "main h1") ================================================ FILE: examples/boilerplates/sb_fixture_test.py ================================================ """Classic Page Object Model with the "sb" fixture.""" class DataPage: def go_to_data_url(self, sb): sb.open("data:text/html,

Hello!

") def add_input_text(self, sb, text): sb.type("input", text) class ObjTests: def test_data_url_page(self, sb): DataPage().go_to_data_url(sb) sb.assert_text("Hello!", "p") DataPage().add_input_text(sb, "Goodbye!") ================================================ FILE: examples/capabilities/ReadMe.md ================================================ ## [](https://github.com/seleniumbase/SeleniumBase/) Using Desired Capabilities You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as BrowserStack or Sauce Labs). Sample run commands may look like this when run from the [SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder: (The browser is now specified in the capabilities file.) ```zsh pytest test_demo_site.py --browser=remote --server=USERNAME:KEY@hub.browserstack.com --port=80 --cap_file=capabilities/sample_cap_file_BS.py ``` ```zsh pytest test_demo_site.py --browser=remote --server=USERNAME:KEY@ondemand.us-east-1.saucelabs.com --port=443 --protocol=https --cap_file=capabilities/sample_cap_file_SL.py ``` (Parameters: ``--browser=remote``, ``--server=SERVER``, ``--port=PORT``, ``--protocol=PROTOCOL``, and ``--cap_file=CAP_FILE.py``) Here's an example desired capabilities file for BrowserStack using the newer SDK format in a `.yml` / `.yaml` file: ```yml platforms: - browserName: safari osVersion: 17 deviceName: iPhone 15 Pro Max buildIdentifier: ${BUILD_NUMBER} parallelsPerPlatform: 1 projectName: My Project browserstackLocal: true debug: true networkLogs: true ``` Here's an example desired capabilities file for BrowserStack using the legacy JSONWP format in a `.py` file: ```python desired_cap = { "browser": "Chrome", "os": "Windows", "os_version": "11", "browser_version": "latest", "browserstack.console": "info", "browserstack.debug": "true", "browserstack.networkLogs": "true", "browserstack.local": "true", } ``` Here's an example desired capabilities file for Sauce Labs: ```python capabilities = { "browserName": "chrome", "browserVersion": "latest", "platformName": "macOS 10.14", "sauce:options": {}, } ``` (Note that the browser is now being specified in the capabilities file, rather than with ``--BROWSER`` when using a **remote** Selenium Grid. If using a **local** Selenium Grid, specify the browser, eg: ``--firefox``.)
You can generate specific desired capabilities using:
Parsing desired capabilities:
SeleniumBase has a desired capabilities parser that can capture all lines from the specified file in the following formats: ```python 'KEY': 'VALUE' 'KEY': True 'KEY': False caps['KEY'] = "VALUE" caps['KEY'] = True caps['KEY'] = False ``` (Each pair must be on a separate line. You can interchange single and double quotes.) You can also swap ``--browser=remote`` with an actual browser, eg ``--browser=chrome``, which will combine the default SeleniumBase desired capabilities with those that were specified in the capabilities file when using ``--cap_file=FILE.py``. Capabilities will override other parameters, so if you set the browser to one thing and the capabilities browser to another, SeleniumBase will use the capabilities browser. You'll need default SeleniumBase capabilities for: * Using a proxy server (not the same as a Selenium Grid server) * Downloading files to a desired folder * Disabling some warnings on Chrome * Overriding a website's Content Security Policy on Chrome * Other possible reasons You can also set browser desired capabilities from a command-line string. Eg: ```zsh pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}' --server="127.0.0.1" --browser=remote ``` (Enclose cap-string in single quotes. Enclose parameter keys in double quotes.) If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Eg: ```zsh pytest my_first_test.py --cap-string='{"browserName":"chrome","name":"*"}' --server="127.0.0.1" --browser=chrome ``` Example name: ``"my_first_test.MyTestClass.test_basics"``

Using a local Selenium Grid

If using a local Selenium Grid with SeleniumBase, start up the Grid Hub and nodes first: ```zsh sbase grid-hub start sbase grid-node start ``` (The Selenium Server JAR file will be automatically downloaded for first-time Grid users. You'll also need Java installed to start up the Grid.) ================================================ FILE: examples/capabilities/mac_cap_file.py ================================================ # Desired capabilities example file for generic macOS Grid nodes capabilities = { "platformName": "MAC", "browserVersion": "latest", } ================================================ FILE: examples/capabilities/sample_cap_file_BS.py ================================================ # Desired capabilities example .py file for BrowserStack: # https://www.browserstack.com/docs/automate/capabilities desired_cap = { "browser": "Chrome", "os": "Windows", "os_version": "11", "browser_version": "latest", "browserstack.console": "info", "browserstack.debug": "true", "browserstack.networkLogs": "true", "browserstack.local": "true", } ================================================ FILE: examples/capabilities/sample_cap_file_BS.yml ================================================ # Desired capabilities example YML file for BrowserStack: # https://www.browserstack.com/docs/automate/capabilities platforms: - browserName: safari osVersion: 17 deviceName: iPhone 15 Pro Max buildIdentifier: ${BUILD_NUMBER} parallelsPerPlatform: 1 projectName: My Project browserstackLocal: true debug: true networkLogs: true ================================================ FILE: examples/capabilities/sample_cap_file_SL.py ================================================ # Desired capabilities example file for Sauce Labs # Generate from https://saucelabs.com/products/platform-configurator capabilities = { "browserName": "chrome", "browserVersion": "latest", "platformName": "macOS 10.14", "sauce:options": {}, } ================================================ FILE: examples/capabilities/selenoid_cap_file.py ================================================ # Desired capabilities example file for Selenoid Grid # # The same result can be achieved on the command-line. Eg: # --cap-string='{"selenoid:options": {"enableVNC": true}}' capabilities = { "acceptSslCerts": True, "acceptInsecureCerts": True, "screenResolution": "1920x1080x24", "selenoid:options": { "enableVNC": True, "enableVideo": False, }, } ================================================ FILE: examples/capabilities/win10_cap_file.py ================================================ # Desired capabilities example file for Windows 10 Grid nodes capabilities = { "platformName": "WIN10", "browserVersion": "latest", } ================================================ FILE: examples/case_plans/basic_test.MyTestClass.test_basics.md ================================================ ``basic_test.py::MyTestClass::test_basics`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Remove the ``Backpack`` from the cart. | The ``Backpack`` is no longer in the cart. | | 5 | Log out from the website. | Logout was successful. | ================================================ FILE: examples/case_plans/my_first_test.MyTestClass.test_swag_labs.md ================================================ ``my_first_test.py::MyTestClass::test_swag_labs`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Click on the ``CHECKOUT`` button.
Enter user details and click ``CONTINUE``. | The ``Backpack`` is seen in the cart on the ``CHECKOUT: OVERVIEW`` page. | | 5 | Click on the ``FINISH`` button. | There is a ``Thank you`` message. | | 6 | Log out from the website. | Logout was successful. | ================================================ FILE: examples/case_plans/shadow_root_test.ShadowRootTest.test_shadow_root.md ================================================ ``shadow_root_test.py::ShadowRootTest::test_shadow_root`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/other/shadow_dom.
Click each tab and verify the text contained within the Shadow Root sections. | Tab 1 text: ``Content Panel 1``
Tab 2 text: ``Content Panel 2``
Tab 3 text: ``Content Panel 3`` | ================================================ FILE: examples/case_plans/test_assert_elements.ListAssertTests.test_assert_list_of_elements.md ================================================ ``test_assert_elements.py::ListAssertTests::test_assert_list_of_elements`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/demo_page. | | | 2 | Use ``self.assert_elements_present("head", "style", "script")`` to verify that multiple elements are present in the HTML. | The assertion is successful. | | 3 | Use ``self.assert_elements("h1", "h2", "h3")`` to verify that multiple elements are visible. | The assertion is successful. | | 4 | Use ``self.assert_elements(["#myDropdown", "#myButton", "#svgRect"])`` to verify that multiple elements are visible. | The assertion is successful. | ================================================ FILE: examples/case_plans/test_calculator.CalculatorTests.test_6_times_7_plus_12_equals_54.md ================================================ ``test_calculator.py::CalculatorTests::test_6_times_7_plus_12_equals_54`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/apps/calculator.
Perform the following calculation: ``6 × 7 + 12`` | The output is ``54`` after pressing ``=`` | ================================================ FILE: examples/case_plans/test_demo_site.DemoSiteTests.test_demo_site.md ================================================ ``test_demo_site.py::DemoSiteTests::test_demo_site`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/demo_page | | | 2 | Assert the title of the current web page.
Assert that a given element is visible on the page.
Assert that a text substring appears in an element's text. | The assertions were successful. | | 3 | Type text into various text fields and then verify. | The assertions were successful. | | 4 | Verify that a hover dropdown link changes page text. | The assertion was successful. | | 5 | Verify that a button click changes text on the page. | The assertion was successful. | | 6 | Verify that an SVG element is located on the page. | The assertion was successful. | | 7 | Verify that a slider control updates a progress bar. | The assertion was successful. | | 8 | Verify that a "select" option updates a meter bar. | The assertion was successful. | | 9 | Assert an element located inside an iFrame. | The assertion was successful. | | 10 | Assert text located inside an iFrame. | The assertion was successful. | | 11 | Verify that clicking a radio button selects it. | The assertion was successful. | | 12 | Verify that clicking an empty checkbox makes it selected. | The assertion was successful. | | 13 | Verify clicking on multiple elements with one call. | The assertions were successful. | | 14 | Verify that clicking an iFrame checkbox selects it. | The assertions were successful. | | 15 | Verify that Drag and Drop works. | The assertion was successful. | | 16 | Assert link text. | The assertion was successful. | | 17 | Verify clicking on link text. | The action was successful. | | 18 | Assert exact text in an element. | The assertion was successful. | | 19 | Highlight a page element. | The action was successful. | | 20 | Verify that Demo Mode works. | The assertion was successful. | ================================================ FILE: examples/case_plans/test_login.SwagLabsLoginTests.test_swag_labs_login.md ================================================ ``test_login.py::SwagLabsLoginTests::test_swag_labs_login`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Log out from the website. | Logout was successful. | ================================================ FILE: examples/case_plans/test_mfa_login.TestMFALogin.test_mfa_login.md ================================================ ``test_mfa_login.py::TestMFALogin::test_mfa_login`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/realworld/login
Enter credentials and Sign In. | Sign In was successful. | | 2 | Click the ``This Page`` button.
Save a screenshot to the logs. | | | 3 | Click to Sign Out | Sign Out was successful. | ================================================ FILE: examples/case_summary.md ================================================

Summary of existing Case Plans

| | | | | - | -: | - | | 🔵 | 14 | Case Plans with customized tables | | ⭕ | 0 | Case Plans using boilerplate code | | 🚧 | 0 | Case Plans that are missing tables | --------

🔎 (Click rows to expand) 🔍

🔵 basic_test.py::MyTestClass::test_basics | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Remove the ``Backpack`` from the cart. | The ``Backpack`` is no longer in the cart. | | 5 | Log out from the website. | Logout was successful. |
🔵 my_first_test.py::MyTestClass::test_swag_labs | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Click on the ``CHECKOUT`` button.
Enter user details and click ``CONTINUE``. | The ``Backpack`` is seen in the cart on the ``CHECKOUT: OVERVIEW`` page. | | 5 | Click on the ``FINISH`` button. | There is a ``Thank you`` message. | | 6 | Log out from the website. | Logout was successful. |
🔵 shadow_root_test.py::ShadowRootTest::test_shadow_root | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/other/shadow_dom.
Click each tab and verify the text contained within the Shadow Root sections. | Tab 1 text: ``Content Panel 1``
Tab 2 text: ``Content Panel 2``
Tab 3 text: ``Content Panel 3`` |
🔵 test_assert_elements.py::ListAssertTests::test_assert_list_of_elements | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/demo_page. | | | 2 | Use ``self.assert_elements_present("head", "style", "script")`` to verify that multiple elements are present in the HTML. | The assertion is successful. | | 3 | Use ``self.assert_elements("h1", "h2", "h3")`` to verify that multiple elements are visible. | The assertion is successful. | | 4 | Use ``self.assert_elements(["#myDropdown", "#myButton", "#svgRect"])`` to verify that multiple elements are visible. | The assertion is successful. |
🔵 test_calculator.py::CalculatorTests::test_6_times_7_plus_12_equals_54 | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/apps/calculator.
Perform the following calculation: ``6 × 7 + 12`` | The output is ``54`` after pressing ``=`` |
🔵 test_demo_site.py::DemoSiteTests::test_demo_site | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/demo_page | | | 2 | Assert the title of the current web page.
Assert that a given element is visible on the page.
Assert that a text substring appears in an element's text. | The assertions were successful. | | 3 | Type text into various text fields and then verify. | The assertions were successful. | | 4 | Verify that a hover dropdown link changes page text. | The assertion was successful. | | 5 | Verify that a button click changes text on the page. | The assertion was successful. | | 6 | Verify that an SVG element is located on the page. | The assertion was successful. | | 7 | Verify that a slider control updates a progress bar. | The assertion was successful. | | 8 | Verify that a "select" option updates a meter bar. | The assertion was successful. | | 9 | Assert an element located inside an iFrame. | The assertion was successful. | | 10 | Assert text located inside an iFrame. | The assertion was successful. | | 11 | Verify that clicking a radio button selects it. | The assertion was successful. | | 12 | Verify that clicking an empty checkbox makes it selected. | The assertion was successful. | | 13 | Verify clicking on multiple elements with one call. | The assertions were successful. | | 14 | Verify that clicking an iFrame checkbox selects it. | The assertions were successful. | | 15 | Verify that Drag and Drop works. | The assertion was successful. | | 16 | Assert link text. | The assertion was successful. | | 17 | Verify clicking on link text. | The action was successful. | | 18 | Assert exact text in an element. | The assertion was successful. | | 19 | Highlight a page element. | The action was successful. | | 20 | Verify that Demo Mode works. | The assertion was successful. |
🔵 test_login.py::SwagLabsLoginTests::test_swag_labs_login | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Log out from the website. | Logout was successful. |
🔵 test_mfa_login.py::TestMFALogin::test_mfa_login | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/realworld/login
Enter credentials and Sign In. | Sign In was successful. | | 2 | Click the ``This Page`` button.
Save a screenshot to the logs. | | | 3 | Click to Sign Out | Sign Out was successful. |
🔵 visual_testing/layout_test.py::VisualLayoutTests::test_applitools_layout_change | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://applitools.com/helloworld?diff1.
Call ``check_window()`` with ``baseline=True``.
Click the button that changes the text of an element.
Call ``check_window()`` three times for ``level=1``, ``level=2``, and ``level=3``. | No issues are detected because a text change should not affect ``check_window()`` | | 2 | Click the button that makes a hidden element visible.
Call ``check_window()`` three times for ``level=1``, ``level=2``, and ``level=3``, but wrap the third call with ``self.assert_raises(Exception):``. | No exceptions are raised because the first two calls should pass and the third one was wrapped with ``self.assert_raises(Exception):``. |
🔵 visual_testing/python_home_test.py::VisualLayoutTests::test_python_home_layout_change | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://python.org/.
Call ``check_window()`` with ``baseline=True``. | | | 2 | Remove the ``Donate`` button using ``remove_element(SELECTOR)``.
Call ``check_window()`` with ``level=0``. | The test detects that the ``Donate`` button was removed. The test does not fail because the check was set to ``level=0`` (print-only).
A ``side_by_side_NAME.html`` file appears in the specific ``latest_logs/`` folder of the test. |
🔵 visual_testing/test_layout_fail.py::VisualLayout_FixtureTests::test_python_home_change | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://python.org/.
Call ``check_window()`` with ``baseline=True``. | | | 2 | Remove the ``Donate`` button using ``remove_element(SELECTOR)``.
Call ``check_window()`` with ``level=3``. | The test fails because the ``Donate`` button was removed.
A ``side_by_side.html`` file appears in the specific ``latest_logs/`` folder of the test. |
🔵 visual_testing/test_layout_fail.py::VisualLayoutFailureTests::test_applitools_change | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://applitools.com/helloworld?diff1.
Call ``check_window()`` with ``baseline=True``. | | | 2 | Click the button that makes a hidden element visible.
Call ``check_window()`` with ``level=3``. | The test fails because the element attribute has changed.
A ``side_by_side.html`` file appears in the specific ``latest_logs/`` folder of the test. |
🔵 visual_testing/test_layout_fail.py::VisualLayoutFailureTests::test_xkcd_logo_change | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://xkcd.com/554/.
Call ``check_window()`` with ``baseline=True``. | | | 2 | Resize the logo using ``set_attribute()``.
Call ``check_window()`` with ``level=3``. | The test fails because the logo has changed.
A ``side_by_side.html`` file appears in the specific ``latest_logs/`` folder of the test. |
🔵 visual_testing/xkcd_visual_test.py::VisualLayoutTests::test_xkcd_layout_change | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://xkcd.com/554/.
Call ``check_window()`` with ``baseline=True``. | | | 2 | Resize the logo using ``set_attribute()``.
Call ``check_window()`` with ``level=0``. | The test detects that the logo has changed. The test does not fail because the check was set to ``level=0`` (print-only).
A ``side_by_side_NAME.html`` file appears in the specific ``latest_logs/`` folder of the test. |
================================================ FILE: examples/cdp_mode/ReadMe.md ================================================

CDP Mode 🐙

🐙 SeleniumBase CDP Mode is a stealth mode that uses the Chrome Devtools Protocol (via MyCDP) to control the web browser. CDP Mode can be used as a subset of UC Mode, or via Pure CDP Mode, which has sync and async formats. From CDP Mode, you can make Playwright stealthy (Stealthy Playwright Mode). ---- ⚙️ This diagram shows the stealthy architecture with CDP Mode: High-Level Stealthy Architecture Overview ---- ### 🎞️ YouTube tutorials that cover CDP Mode:

(Watch "Undetectable Automation 4" on YouTube! ▶️)

(See `examples/cdp_mode/` for up-to-date examples.) ----

(Watch "Hacking websites with CDP" on YouTube! ▶️)

----

(Watch "Web-Scraping with GitHub Actions" on YouTube! ▶️)

---- ℹ️ Note the differences between UC Mode and CDP Mode: 👤 UC Mode's stealth is based on a modified chromedriver (uc_driver) that avoids bot-detection by disconnecting and reconnecting WebDriver from the browser at strategic times. Due to advancements in anti-bot technology, more stealth was needed to bypass advanced bot-detection. (That's where CDP Mode comes in.) 🐙 CDP Mode includes multiple updates to the above, such as: * Using CDP directly, which is stealthier than WebDriver. * Backwards compatibility for existing UC Mode scripts. * More configuration options when launching browsers. * The ability to use WebDriver and CDP calls together. * Full access to call any advanced CDP library method. * Can be used to make the Playwright library stealthy. ---- ### 🐙 CDP Mode Usage (when used as a subset of UC Mode): * **`sb.activate_cdp_mode(url)`** That disconnects WebDriver from Chrome (which prevents detection), and gives you access to `sb.cdp` methods (which don't trigger anti-bot checks). > (**New:** Calling **`sb.open(url)`** from UC Mode also activates CDP Mode now.) Simple example from [SeleniumBase/examples/cdp_mode/raw_gitlab.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py): ```python from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() sb.sleep(2) ``` (If the CAPTCHA wasn't bypassed automatically when going to the URL, then `sb.solve_captcha()` gets the job done.) ---- Here's another example that calls `sb.solve_captcha()`: ([SeleniumBase/examples/cdp_mode/raw_planetmc.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_planetmc.py)) ```python from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: url = "www.planetminecraft.com/account/sign_in/" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() sb.wait_for_element_absent("input[disabled]") sb.sleep(2) ``` In many cases, the CAPTCHA will be solved automatically without needing to call `solve_captcha()`. ---- You can also use `PyAutoGUI` to click on elements with the mouse by calling `sb.cdp.gui_click_element(selector)`. ℹ️ Note that `PyAutoGUI` is an optional dependency. If calling a method that uses it when not already installed, then `SeleniumBase` installs `PyAutoGUI` at runtime. ---- ### 🐙 Here are a few common `sb.cdp` methods: * `sb.cdp.click(selector)` (Uses the CDP API to click) * `sb.cdp.click_if_visible(selector)` (Click if visible) * `sb.cdp.solve_captcha()` (Uses CDP to click a CAPTCHA) * `sb.cdp.gui_click_element(selector)` (Uses `PyAutoGUI`) * `sb.cdp.type(selector, text)` (Type text into a selector) * `sb.cdp.press_keys(selector, text)` (Human-speed `type`) * `sb.cdp.select_all(selector)` (Returns matching elements) * `sb.cdp.get_text(selector)` (Returns the element's text) Methods that start with `sb.cdp.gui` use `PyAutoGUI` for interaction. To use WebDriver methods again, call: * **`sb.reconnect()`** or **`sb.connect()`** (Note that reconnecting allows anti-bots to detect you, so only reconnect if it is safe to do so.) To disconnect again, call: * **`sb.disconnect()`** While disconnected, if you call a WebDriver method, then SeleniumBase will attempt to use the CDP Mode version of that method (if available). For example, if you call `sb.click(selector)` instead of `sb.cdp.click(selector)`, then your WebDriver call will automatically be redirected to the CDP Mode version. Not all WebDriver methods have a matching CDP Mode method. In that scenario, calling a WebDriver method while disconnected could raise an error, or make WebDriver automatically reconnect first. To find out if WebDriver is connected or disconnected, call: * **`sb.is_connected()`** Note: When CDP Mode is initialized from UC Mode, the WebDriver is disconnected from the browser. (The stealthy CDP-Driver takes over.) ---- ### 🐙 CDP Mode examples ([SeleniumBase/examples/cdp_mode](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode))

▶️ 🔖 Example 1: (Pokemon site using Incapsula/Imperva protection with invisible reCAPTCHA) ```python from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.pokemon.com/us" sb.activate_cdp_mode(url) sb.sleep(1.5) sb.click_if_visible("button#onetrust-accept-btn-handler") sb.sleep(1.2) sb.click("a span.icon_pokeball") sb.sleep(2.5) sb.click('b:contains("Show Advanced Search")') sb.sleep(2.5) sb.click('span[data-type="type"][data-value="electric"]') sb.sleep(0.7) sb.scroll_into_view("a#advSearch") sb.sleep(0.7) sb.click("a#advSearch") sb.sleep(1.2) sb.click('img[src*="img/pokedex/detail/025.png"]') sb.assert_text("Pikachu", 'div[class*="title"]') sb.assert_element('img[alt="Pikachu"]') sb.scroll_into_view("div.pokemon-ability-info") sb.sleep(1.2) sb.cdp.flash('div[class*="title"]') sb.cdp.flash('img[alt="Pikachu"]') sb.cdp.flash("div.pokemon-ability-info") name = sb.get_text("label.styled-select") info = sb.get_text("div.version-descriptions p.active") print("*** %s: ***\n* %s" % (name, info)) sb.sleep(2) sb.cdp.highlight_overlay("div.pokemon-ability-info") sb.sleep(2) sb.open("https://events.pokemon.com/EventLocator/") sb.sleep(2) sb.click('span:contains("Championship")') sb.sleep(2) events = sb.select_all("div.event-info__title") print("*** Pokémon Championship Events: ***") for event in events: print("* " + event.text) sb.sleep(2) ```
> [SeleniumBase/examples/cdp_mode/raw_pokemon.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_pokemon.py)
▶️ 🔖 Example 2: (Hyatt site using Kasada protection) ```python from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.hyatt.com/" sb.activate_cdp_mode(url) sb.sleep(3.2) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(0.1) sb.click_if_visible("#onetrust-reject-all-handler") sb.sleep(1.2) location = "Anaheim, CA, USA" sb.type('input[id="search-term"]', location) sb.sleep(1.2) sb.click('li[data-js="suggestion"]') sb.sleep(0.6) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(0.6) sb.click("button.be-button-shop") sb.sleep(1) sb.click_if_visible('[label="Find Hotels"]') sb.sleep(5) card_info = 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]' hotels = sb.select_all(card_info) print("Hyatt Hotels in %s:" % location) print("(" + sb.get_text('span[class*="summary_destination"]') + ")") if len(hotels) == 0: print("No availability over the selected dates!") for hotel in hotels: info = hotel.text.strip() if "Avg/Night" in info and not info.startswith("Rates from"): name = info.split(" (")[0].split(" + ")[0].split(" Award Cat")[0] name = name.split(" Rates from :")[0] price = "?" if "Rates from : " in info: price = info.split("Rates from : ")[1].split(" Avg/Night")[0] print("* %s => %s" % (name, price)) ```
> [SeleniumBase/examples/cdp_mode/raw_hyatt.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_hyatt.py)
▶️ 🔖 Example 3: (BestWestern site using DataDome protection) ```python from seleniumbase import SB with SB(uc=True, test=True, locale="en", guest=True) as sb: url = "https://www.bestwestern.com/en_US.html" sb.activate_cdp_mode(url) sb.sleep(3) sb.click_if_visible(".onetrust-close-btn-handler") sb.sleep(1) sb.click("input#destination-input") sb.sleep(2) location = "Palm Springs, CA, USA" sb.press_keys("input#destination-input", location) sb.sleep(1) sb.click("ul#google-suggestions li") sb.sleep(1) sb.click("button#btn-modify-stay-update") sb.sleep(4) sb.click("label#available-label") sb.sleep(2.5) print("Best Western Hotels in %s:" % location) summary_details = sb.get_text("#summary-details-column") dates = summary_details.split("DESTINATION")[-1] dates = dates.split(" CHECK-OUT")[0].strip() + " CHECK-OUT" dates = dates.replace(" ", " ") print("(Dates: %s)" % dates) flip_cards = sb.select_all(".flipCard") for i, flip_card in enumerate(flip_cards): hotel = flip_card.query_selector(".hotelName") price = flip_card.query_selector(".priceSection") if hotel and price: print("* %s: %s => %s" % ( i + 1, hotel.text.strip(), price.text.strip()) ) ```
> [SeleniumBase/examples/cdp_mode/raw_bestwestern.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_bestwestern.py)
▶️ 🔖 Example 4: (Walmart site using Akamai protection with PerimeterX) ```python from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.walmart.com/" sb.activate_cdp_mode(url) sb.sleep(1.8) continue_button = 'button:contains("Continue shopping")' if sb.is_element_visible(continue_button): sb.cdp.gui_click_element(continue_button) sb.sleep(0.6) sb.click('input[aria-label="Search"]') sb.sleep(1.2) search = "Settlers of Catan Board Game" required_text = "Catan" sb.press_keys('input[aria-label="Search"]', search + "\n") sb.sleep(3.8) if sb.is_element_visible("#px-captcha"): sb.cdp.gui_click_and_hold("#px-captcha", 7.2) sb.sleep(4.2) if sb.is_element_visible("#px-captcha"): sb.cdp.gui_click_and_hold("#px-captcha", 4.2) sb.sleep(3.2) sb.remove_elements('[data-testid="skyline-ad"]') sb.remove_elements('[data-testid="sba-container"]') print('*** Walmart Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] sb.click_if_visible('[data-automation-id="sb-btn-close-mark"]') items = sb.find_elements('[data-item-id]') for item in items: if required_text in item.text: description = item.querySelector( '[data-automation-id="product-title"]' ) if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector( '[data-automation-id="product-price"]' ) if price: price_text = price.text price_text = price_text.split("current price Now ")[-1] price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") ```
> [SeleniumBase/examples/cdp_mode/raw_walmart.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_walmart.py)
▶️ 🔖 Example 5: (Nike site using Shape Security) ```python from seleniumbase import SB with SB(uc=True, test=True, locale="en", pls="none") as sb: url = "https://www.nike.com/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.click('[data-testid="user-tools-container"] search') sb.sleep(1.5) search = "Nike Air Force 1" sb.press_keys('input[type="search"]', search) sb.sleep(4) details = 'ul[data-testid*="products"] figure .details' elements = sb.select_all(details) if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("* " + element.text) sb.sleep(2) ```
> [SeleniumBase/examples/cdp_mode/raw_nike.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/raw_nike.py)

(Note: Extra sb.sleep() calls have been added to prevent bot-detection because some sites will flag you as a bot if you perform actions too quickly.) (Note: Some sites may IP-block you for 36 hours or more if they catch you using regular Selenium WebDriver. Be extra careful when creating and/or modifying automation scripts that run on them.) ---- ### 🐙 CDP Mode API / Methods ```python sb.cdp.get(url, **kwargs) sb.cdp.open(url, **kwargs) # Same as sb.cdp.get(url, **kwargs) sb.cdp.reload(ignore_cache=True, script_to_evaluate_on_load=None) sb.cdp.refresh(*args, **kwargs) sb.cdp.get_event_loop() sb.cdp.get_rd_host() # Returns the remote-debugging host sb.cdp.get_rd_port() # Returns the remote-debugging port sb.cdp.get_rd_url() # Returns the remote-debugging URL sb.cdp.get_endpoint_url() # Same as sb.cdp.get_rd_url() sb.cdp.get_port() # Same as sb.cdp.get_rd_port() sb.cdp.get_websocket_url() # Returns the websocket URL sb.cdp.add_handler(event, handler) sb.cdp.find_element(selector, best_match=False, timeout=None) sb.cdp.find(selector, best_match=False, timeout=None) sb.cdp.locator(selector, best_match=False, timeout=None) sb.cdp.find_element_by_text(text, tag_name=None, timeout=None) sb.cdp.find_all(selector, timeout=None) sb.cdp.find_elements_by_text(text, tag_name=None) sb.cdp.select(selector, timeout=None) sb.cdp.select_all(selector, timeout=None) sb.cdp.find_elements(selector, timeout=None) sb.cdp.find_visible_elements(selector, timeout=None) sb.cdp.click(selector, timeout=None) sb.cdp.click_if_visible(selector, timeout=0) sb.cdp.click_visible_elements(selector, limit=0) sb.cdp.click_nth_element(selector, number) sb.cdp.click_nth_visible_element(selector, number) sb.cdp.click_with_offset(selector, x, y, center=False) sb.cdp.click_link(link_text) sb.cdp.go_back() sb.cdp.go_forward() sb.cdp.get_navigation_history() sb.cdp.tile_windows(windows=None, max_columns=0) sb.cdp.grant_permissions(permissions, origin=None) sb.cdp.grant_all_permissions() sb.cdp.reset_permissions() sb.cdp.get_all_cookies(*args, **kwargs) sb.cdp.set_all_cookies(*args, **kwargs) sb.cdp.save_cookies(*args, **kwargs) sb.cdp.load_cookies(*args, **kwargs) sb.cdp.clear_cookies() sb.cdp.sleep(seconds) sb.cdp.bring_active_window_to_front() sb.cdp.bring_to_front() sb.cdp.get_active_element() sb.cdp.get_active_element_css() sb.cdp.click_active_element() sb.cdp.mouse_click(selector, timeout=None) sb.cdp.nested_click(parent_selector, selector) sb.cdp.get_nested_element(parent_selector, selector) sb.cdp.select_option_by_text(dropdown_selector, option) sb.cdp.select_option_by_index(dropdown_selector, option) sb.cdp.select_option_by_value(dropdown_selector, option) sb.cdp.flash(selector, duration=1, color="44CC88", pause=0) sb.cdp.highlight(selector) sb.cdp.focus(selector) sb.cdp.highlight_overlay(selector) sb.cdp.get_parent(element) sb.cdp.remove_element(selector) sb.cdp.remove_from_dom(selector) sb.cdp.remove_elements(selector) sb.cdp.send_keys(selector, text, timeout=None) sb.cdp.press_keys(selector, text, timeout=None) sb.cdp.type(selector, text, timeout=None) sb.cdp.set_value(selector, text, timeout=None) sb.cdp.clear_input(selector, timeout=None) sb.cdp.clear(selector, timeout=None) sb.cdp.submit(selector) sb.cdp.evaluate(expression) sb.cdp.execute_script(expression) sb.cdp.js_dumps(obj_name) sb.cdp.maximize() sb.cdp.minimize() sb.cdp.medimize() sb.cdp.set_window_rect(x, y, width, height) sb.cdp.reset_window_size() sb.cdp.open_new_window(url=None, switch_to=True) sb.cdp.switch_to_window(window) sb.cdp.switch_to_newest_window() sb.cdp.open_new_tab(url=None, switch_to=True) sb.cdp.switch_to_tab(tab) sb.cdp.switch_to_newest_tab() sb.cdp.close_active_tab() sb.cdp.get_active_tab() sb.cdp.get_tabs() sb.cdp.get_window() sb.cdp.get_text(selector) sb.cdp.get_title() sb.cdp.get_current_url() sb.cdp.get_origin() sb.cdp.get_html(include_shadow_dom=True) sb.cdp.get_page_source(include_shadow_dom=True) sb.cdp.get_user_agent() sb.cdp.get_cookie_string() sb.cdp.get_locale_code() sb.cdp.get_local_storage_item(key) sb.cdp.get_session_storage_item(key) sb.cdp.get_screen_rect() sb.cdp.get_window_rect() sb.cdp.get_window_size() sb.cdp.get_window_position() sb.cdp.get_element_rect(selector, timeout=None) sb.cdp.get_element_size(selector, timeout=None) sb.cdp.get_element_position(selector, timeout=None) sb.cdp.get_gui_element_rect(selector, timeout=None) sb.cdp.get_gui_element_center(selector, timeout=None) sb.cdp.get_document() sb.cdp.get_flattened_document() sb.cdp.get_element_attributes(selector) sb.cdp.get_element_attribute(selector, attribute) sb.cdp.get_attribute(selector, attribute) sb.cdp.get_element_html(selector) sb.cdp.get_mfa_code(totp_key=None) sb.cdp.enter_mfa_code(selector, totp_key=None, timeout=None) sb.cdp.activate_messenger() sb.cdp.set_messenger_theme(theme="default", location="default") sb.cdp.post_message(message, duration=None, pause=True, style="info") sb.cdp.set_locale(locale) sb.cdp.set_local_storage_item(key, value) sb.cdp.set_session_storage_item(key, value) sb.cdp.set_attributes(selector, attribute, value) sb.cdp.is_attribute_present(selector, attribute, value=None) sb.cdp.is_online() sb.cdp.solve_captcha() sb.cdp.click_captcha() sb.cdp.gui_press_key(key) sb.cdp.gui_press_keys(keys) sb.cdp.gui_write(text) sb.cdp.gui_click_x_y(x, y, timeframe=0.25) sb.cdp.gui_click_element(selector, timeframe=0.25) sb.cdp.gui_click_with_offset(selector, x, y, timeframe=0.25, center=False) sb.cdp.gui_click_captcha() sb.cdp.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.35) sb.cdp.gui_drag_and_drop(drag_selector, drop_selector, timeframe=0.35) sb.cdp.gui_click_and_hold(selector, timeframe=0.35) sb.cdp.gui_hover_x_y(x, y) sb.cdp.gui_hover_element(selector) sb.cdp.gui_hover_and_click(hover_selector, click_selector) sb.cdp.hover_element(selector) sb.cdp.hover_and_click(hover_selector, click_selector) sb.cdp.internalize_links() sb.cdp.is_checked(selector) sb.cdp.is_selected(selector) sb.cdp.check_if_unchecked(selector) sb.cdp.select_if_unselected(selector) sb.cdp.uncheck_if_checked(selector) sb.cdp.unselect_if_selected(selector) sb.cdp.is_element_present(selector) sb.cdp.is_element_visible(selector) sb.cdp.is_text_visible(text, selector="body") sb.cdp.is_exact_text_visible(text, selector="body") sb.cdp.wait_for_text(text, selector="body", timeout=None) sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None) sb.cdp.wait_for_element_visible(selector, timeout=None) sb.cdp.wait_for_element(selector, timeout=None) sb.cdp.wait_for_element_not_visible(selector, timeout=None) sb.cdp.wait_for_element_absent(selector, timeout=None) sb.cdp.wait_for_any_of_elements_visible(*args, **kwargs) sb.cdp.wait_for_any_of_elements_present(*args, **kwargs) sb.cdp.assert_any_of_elements_visible(*args, **kwargs) sb.cdp.assert_any_of_elements_present(*args, **kwargs) sb.cdp.assert_element(selector, timeout=None) sb.cdp.assert_element_visible(selector, timeout=None) sb.cdp.assert_element_present(selector, timeout=None) sb.cdp.assert_element_absent(selector, timeout=None) sb.cdp.assert_element_not_visible(selector, timeout=None) sb.cdp.assert_element_attribute(selector, attribute, value=None) sb.cdp.assert_title(title) sb.cdp.assert_title_contains(substring) sb.cdp.assert_url(url) sb.cdp.assert_url_contains(substring) sb.cdp.assert_text(text, selector="html", timeout=None) sb.cdp.assert_exact_text(text, selector="html", timeout=None) sb.cdp.assert_text_not_visible(text, selector="body", timeout=None) sb.cdp.assert_true() sb.cdp.assert_false() sb.cdp.assert_equal(first, second) sb.cdp.assert_not_equal(first, second) sb.cdp.assert_in(first, second) sb.cdp.assert_not_in(first, second) sb.cdp.scroll_into_view(selector) sb.cdp.scroll_to_y(y) sb.cdp.scroll_by_y(y) sb.cdp.scroll_to_top() sb.cdp.scroll_to_bottom() sb.cdp.scroll_up(amount=25) sb.cdp.scroll_down(amount=25) sb.cdp.save_page_source(name, folder=None) sb.cdp.save_as_html(name, folder=None) sb.cdp.save_screenshot(name, folder=None, selector=None) sb.cdp.print_to_pdf(name, folder=None) sb.cdp.save_as_pdf(name, folder=None) ``` ℹ️ When available, calling `sb.METHOD()` redirects to `sb.cdp.METHOD()` when CDP Mode is active. From Pure CDP Mode, always call these methods with `sb.METHOD()` instead of `sb.cdp.METHOD()`. ---- ### 🐙 Pure CDP Mode (sb_cdp) Pure CDP Mode doesn't use WebDriver for anything. The browser is launched using CDP, and all browser actions are performed using CDP (or PyAutoGUI). Initialization: ```python from seleniumbase import sb_cdp sb = sb_cdp.Chrome(url=None, **kwargs) ``` Pure CDP Mode includes all methods from regular CDP Mode, except that they're called directly from sb instead of sb.cdp. Eg: sb.gui_click_captcha(). To quit a CDP-launched browser, use `sb.driver.stop()`. Basic example from [SeleniumBase/examples/cdp_mode/raw_cdp_turnstile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_turnstile.py): ```python from seleniumbase import sb_cdp url = "https://seleniumbase.io/apps/turnstile" sb = sb_cdp.Chrome(url) sb.solve_captcha() sb.assert_element("img#captcha-success") sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) sb.driver.stop() ``` Another example: ([SeleniumBase/examples/cdp_mode/raw_cdp_methods.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_methods.py)) ```python from seleniumbase import sb_cdp url = "https://seleniumbase.io/demo_page" sb = sb_cdp.Chrome(url) sb.press_keys("input", "Text") sb.highlight("button") sb.type("textarea", "Here are some words") sb.click("button") sb.set_value("input#mySlider", "100") sb.click_visible_elements("input.checkBoxClassB") sb.select_option_by_text("#mySelect", "Set to 75%") sb.gui_hover_and_click("#myDropdown", "#dropOption2") sb.gui_click_element("#checkBox1") sb.gui_drag_and_drop("img#logo", "div#drop2") sb.nested_click("iframe#myFrame3", ".fBox") sb.sleep(2) sb.driver.stop() ``` ℹ️ Even if you don't call `sb.driver.stop()`, the browser still quits after the script goes out-of-scope. ---- ### 🐙 CDP Mode Async API / Methods Initialization: ```python from seleniumbase import cdp_driver driver = await cdp_driver.start_async() tab = await driver.get(url, **kwargs) ``` Methods: (Sometimes `tab` is named `page` in examples) ```python await tab.get(url="about:blank") await tab.open(url="about:blank") await tab.find(text, best_match=False, timeout=10) # text can be selector await tab.find_all(text, timeout=10) # text can be selector await tab.select(selector, timeout=10) await tab.select_all(selector, timeout=10, include_frames=False) await tab.query_selector(selector) await tab.query_selector_all(selector) await tab.find_element_by_text(text, best_match=False) await tab.find_elements_by_text(text) await tab.reload(ignore_cache=True, script_to_evaluate_on_load=None) await tab.evaluate(expression) await tab.js_dumps(obj_name) await tab.back() await tab.forward() await tab.get_window() await tab.get_content() await tab.maximize() await tab.minimize() await tab.fullscreen() await tab.medimize() await tab.set_window_size(left=0, top=0, width=1280, height=1024) await tab.set_window_rect(left=0, top=0, width=1280, height=1024) await tab.activate() await tab.bring_to_front() await tab.set_window_state( left=0, top=0, width=1280, height=720, state="normal") await tab.get_navigation_history() await tab.get_user_agent() await tab.get_cookie_string() await tab.get_locale_code() await tab.is_online() await tab.open_external_inspector() # Open separate browser for debugging await tab.close() await tab.scroll_down(amount=25) await tab.scroll_up(amount=25) await tab.wait_for(selector="", text="", timeout=10) await tab.set_attributes(selector, attribute, value) await tab.internalize_links() await tab.download_file(url, filename=None) await tab.save_screenshot( filename="auto", format="png", full_page=False) await tab.print_to_pdf(filename="auto") await tab.set_download_path(path) await tab.get_all_linked_sources() await tab.get_all_urls(absolute=True) await tab.get_html() await tab.get_page_source() await tab.is_element_present(selector) await tab.is_element_visible(selector) await tab.get_element_rect(selector, timeout=5) # (window-based) await tab.get_window_rect() await tab.get_gui_element_rect(selector, timeout=5) # (screen-based) await tab.get_title() await tab.get_current_url() await tab.get_origin() await tab.send_keys(selector, text, timeout=5) await tab.type(selector, text, timeout=5) await tab.click(selector, timeout=5) await tab.click_if_visible(selector, timeout=0) await tab.click_with_offset(selector, x, y, center=False, timeout=5) await tab.solve_captcha() await tab.click_captcha() # Same as solve_captcha() await tab.get_document() await tab.get_flattened_document() await tab.get_local_storage() await tab.set_local_storage(items) ``` ---- ### 🐙 CDP Mode WebElement API / Methods After finding an element in CDP Mode, you can access `WebElement` methods: (Eg. After `element = sb.find_element(selector)`) ```python element.clear_input() element.click() element.click_with_offset(x, y, center=False) element.flash(duration=0.5, color="EE4488") element.focus() element.gui_click(timeframe=0.25) element.highlight_overlay() element.mouse_click() element.mouse_drag(destination) element.mouse_move() element.press_keys(text) element.query_selector(selector) element.querySelector(selector) element.query_selector_all(selector) element.querySelectorAll(selector) element.remove_from_dom() element.save_screenshot(*args, **kwargs) element.save_to_dom() element.scroll_into_view() element.select_option() element.send_file(*file_paths) element.send_keys(text) element.set_text(value) element.type(text) element.get_position() element.get_html() element.get_js_attributes() element.get_attribute(attribute) element.get_parent() ``` ---- SeleniumBase
SeleniumBase
================================================ FILE: examples/cdp_mode/__init__.py ================================================ ================================================ FILE: examples/cdp_mode/playwright/ReadMe.md ================================================

Stealthy Playwright Mode 🎭

🎭 Stealthy Playwright Mode is a subset of **[SeleniumBase CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md)** that launches **[Playwright](https://github.com/microsoft/playwright-python)** from an existing SeleniumBase browser to make Playwright stealthy (for bypassing bot-detection). Playwright uses connect_over_cdp() to attach itself onto an existing SeleniumBase session via the remote-debugging-port. From here, APIs of both frameworks can be used together. --------

(See Stealthy Playwright Mode on YouTube! ▶️)

-------- ### 🎭 Getting started with Stealthy Playwright Mode: If `playwright` isn't already installed, then install it first: ```zsh pip install playwright ``` Stealthy Playwright Mode comes in 3 formats: 1. `sb_cdp` sync format 2. `SB` nested sync format 3. `cdp_driver` async format #### `sb_cdp` sync format (minimal boilerplate): ```python from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://example.com") ``` #### `SB` nested sync format (minimal boilerplate): ```python from playwright.sync_api import sync_playwright from seleniumbase import SB with SB(uc=True) as sb: sb.activate_cdp_mode() endpoint_url = sb.cdp.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://example.com") ``` #### `cdp_driver` async format (minimal boilerplate): ```python import asyncio from seleniumbase import cdp_driver from playwright.async_api import async_playwright async def main(): driver = await cdp_driver.start_async() endpoint_url = driver.get_endpoint_url() async with async_playwright() as p: browser = await p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] await page.goto("https://example.com") if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ``` ### 🎭 Stealthy Playwright Mode details: The `sb_cdp` and `cdp_driver` formats don't use WebDriver at all, meaning that `chromedriver` isn't needed. From these two formats, Stealthy Playwright Mode can call [CDP Mode methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/cdp_mode_methods.md) and Playwright methods. The `SB()` format requires WebDriver, therefore `chromedriver` will be downloaded (as `uc_driver`) if the driver isn't already present on the local machine. The `SB()` format has access to Selenium WebDriver methods via [the SeleniumBase API](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md). Using Stealthy Playwright Mode from `SB()` grants access to all the APIs: Selenium, SeleniumBase, [UC Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md), [CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md), and Playwright. In the sync formats, `get_endpoint_url()` also applies `nest-asyncio` so that nested event loops are allowed. (Python doesn't allow nested event loops by default). Without this, you'd get the error: `"Cannot run the event loop while another loop is running"` when calling CDP Mode methods (such as `solve_captcha()`) from within the Playwright context manager. This `nest-asyncio` call is done behind-the-scenes so that users don't need to handle this on their own. ### 🎭 Stealthy Playwright Mode examples: Here's an example that queries Microsoft Copilot: ```python from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://copilot.microsoft.com") page.wait_for_selector("textarea#userInput") sb.sleep(1) query = "Playwright Python connect_over_cdp() sync example" page.fill("textarea#userInput", query) page.click('button[data-testid="submit-button"]') sb.sleep(3) sb.solve_captcha() page.wait_for_selector('button[data-testid*="-thumbs-up"]') sb.sleep(4) page.click('button[data-testid*="scroll-to-bottom"]') sb.sleep(3) chat_results = '[data-testid="highlighted-chats"]' result = page.locator(chat_results).inner_text() print(result.replace("\n\n", " \n")) ``` Here's an example that solves the Bing CAPTCHA: ```python from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.bing.com/turing/captcha/challenge") sb.sleep(3) sb.solve_captcha() sb.sleep(3) ``` For more examples, see [examples/cdp_mode/playwright](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/cdp_mode/playwright). -------- SeleniumBasePlaywright ================================================ FILE: examples/cdp_mode/playwright/__init__.py ================================================ ================================================ FILE: examples/cdp_mode/playwright/raw_basic_async.py ================================================ import asyncio from playwright.async_api import async_playwright from seleniumbase import cdp_driver async def main(): driver = await cdp_driver.start_async() endpoint_url = driver.get_endpoint_url() async with async_playwright() as p: browser = await p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] await page.goto("https://seleniumbase.io/simple/login") await page.fill("#username", "demo_user") await page.fill("#password", "secret_pass") await page.click("#log-in") await page.wait_for_selector("h1") await driver.sleep(1) if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/playwright/raw_basic_nested.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import SB with SB(uc=True) as sb: sb.activate_cdp_mode() endpoint_url = sb.cdp.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://seleniumbase.io/simple/login") page.fill("#username", "demo_user") page.fill("#password", "secret_pass") page.click("#log-in") page.wait_for_selector("h1") sb.sleep(1) ================================================ FILE: examples/cdp_mode/playwright/raw_basic_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://seleniumbase.io/simple/login") page.fill("#username", "demo_user") page.fill("#password", "secret_pass") page.click("#log-in") page.wait_for_selector("h1") sb.sleep(1) ================================================ FILE: examples/cdp_mode/playwright/raw_bing_cap_async.py ================================================ import asyncio from playwright.async_api import async_playwright from seleniumbase import cdp_driver async def main(): driver = await cdp_driver.start_async(locale="en") endpoint_url = driver.get_endpoint_url() async with async_playwright() as p: browser = await p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] await page.goto("https://www.bing.com/turing/captcha/challenge") await driver.sleep(3) await driver.solve_captcha() await driver.sleep(3) if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/playwright/raw_bing_cap_nested.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import SB with SB(uc=True, locale="en") as sb: sb.activate_cdp_mode() endpoint_url = sb.cdp.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.bing.com/turing/captcha/challenge") sb.sleep(3) sb.solve_captcha() sb.sleep(3) ================================================ FILE: examples/cdp_mode/playwright/raw_bing_cap_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.bing.com/turing/captcha/challenge") sb.sleep(3) sb.solve_captcha() sb.sleep(3) ================================================ FILE: examples/cdp_mode/playwright/raw_cf_cap_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.cloudflare.com/login") sb.sleep(3) sb.solve_captcha() sb.sleep(3) ================================================ FILE: examples/cdp_mode/playwright/raw_copilot_async.py ================================================ import asyncio from playwright.async_api import async_playwright from seleniumbase import cdp_driver async def main(): driver = await cdp_driver.start_async() endpoint_url = driver.get_endpoint_url() async with async_playwright() as p: browser = await p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] await page.goto("https://copilot.microsoft.com") await page.wait_for_selector("textarea#userInput") await driver.sleep(1) query = "Playwright Python connect_over_cdp() sync example" await page.fill("textarea#userInput", query) await page.click('button[data-testid="submit-button"]') await driver.sleep(4) await driver.solve_captcha() await page.wait_for_selector('button[data-testid*="-thumbs-up"]') await driver.sleep(4) await page.click('button[data-testid*="scroll-to-bottom"]') await driver.sleep(3) chat_results = '[data-testid="highlighted-chats"]' result = await page.locator(chat_results).inner_text() print(result.replace("\n\n", " \n")) if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/playwright/raw_copilot_nested.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import SB with SB(uc=True) as sb: sb.activate_cdp_mode() endpoint_url = sb.cdp.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://copilot.microsoft.com") page.wait_for_selector("textarea#userInput") sb.sleep(1) query = "Playwright Python connect_over_cdp() sync example" page.fill("textarea#userInput", query) page.click('button[data-testid="submit-button"]') sb.sleep(4) sb.solve_captcha() page.wait_for_selector('button[data-testid*="-thumbs-up"]') sb.sleep(4) page.click('button[data-testid*="scroll-to-bottom"]') sb.sleep(3) chat_results = '[data-testid="highlighted-chats"]' result = page.locator(chat_results).inner_text() print(result.replace("\n\n", " \n")) ================================================ FILE: examples/cdp_mode/playwright/raw_copilot_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://copilot.microsoft.com") page.wait_for_selector("textarea#userInput") sb.sleep(1) query = "Playwright Python connect_over_cdp() sync example" page.fill("textarea#userInput", query) page.click('button[data-testid="submit-button"]') sb.sleep(4) sb.solve_captcha() page.wait_for_selector('button[data-testid*="-thumbs-up"]') sb.sleep(4) page.click('button[data-testid*="scroll-to-bottom"]') sb.sleep(3) chat_results = '[data-testid="highlighted-chats"]' result = page.locator(chat_results).inner_text() print(result.replace("\n\n", " \n")) ================================================ FILE: examples/cdp_mode/playwright/raw_footlocker_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en", ad_block=True) endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.footlocker.com/") input_field = 'input[name="query"]' page.wait_for_selector(input_field) sb.sleep(1.5) sb.click_if_visible('button[id*="Agree"]') sb.sleep(1.2) page.click(input_field) sb.sleep(0.5) search = "Nike Shoes" sb.press_keys(input_field, search) sb.sleep(1.2) page.click('ul[id*="typeahead"] li div') sb.sleep(3.5) elements = sb.select_all("a.ProductCard-link") if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("------------------ >>>") print("* " + element.text) sb.sleep(2) ================================================ FILE: examples/cdp_mode/playwright/raw_gas_info_async.py ================================================ import asyncio from playwright.async_api import async_playwright from seleniumbase import cdp_driver async def main(): driver = await cdp_driver.start_async() endpoint_url = driver.get_endpoint_url() tab = await driver.get("about:blank") async with async_playwright() as p: browser = await p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] url = ( "https://www.gassaferegister.co.uk/gas-safety" "/gas-safety-certificates-records/building-regulations-certificate" "/order-replacement-building-regulations-certificate/" ) await page.goto(url) await tab.sleep(0.6) await tab.solve_captcha() await page.wait_for_selector("#SearchTerm") await tab.sleep(1.4) allow_cookies = 'button:contains("Allow all cookies")' await tab.click_if_visible(allow_cookies, timeout=2) await tab.sleep(1) await page.fill("#SearchTerm", "Hydrogen") await page.click("button.search-button") await tab.sleep(3) results = await tab.query_selector_all("div.search-result") for result in results: print(result.text.replace(" " * 12, " ").strip() + "\n") await tab.scroll_down(50) await tab.sleep(1) if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/playwright/raw_gas_info_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] url = ( "https://www.gassaferegister.co.uk/gas-safety" "/gas-safety-certificates-records/building-regulations-certificate" "/order-replacement-building-regulations-certificate/" ) page.goto(url) sb.sleep(0.6) sb.solve_captcha() page.wait_for_selector("#SearchTerm") sb.sleep(1.4) allow_cookies = 'button:contains("Allow all cookies")' sb.click_if_visible(allow_cookies, timeout=2) sb.sleep(1) page.fill("#SearchTerm", "Hydrogen") page.click("button.search-button") sb.sleep(3) items = page.locator("div.search-result") for i in range(items.count()): item_text = items.nth(i).inner_text() print(item_text.replace("\n\n", "\n") + "\n") sb.scroll_to_bottom() sb.sleep(1) ================================================ FILE: examples/cdp_mode/playwright/raw_gitlab_async.py ================================================ import asyncio from playwright.async_api import async_playwright from seleniumbase import cdp_driver async def main(): driver = await cdp_driver.start_async(locale="en", agent="headless") endpoint_url = driver.get_endpoint_url() async with async_playwright() as p: browser = await p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] await page.goto("https://gitlab.com/users/sign_in") await driver.sleep(3) await driver.solve_captcha() await driver.sleep(1) await page.locator('label[for="user_login"]').click() await page.wait_for_selector('[data-testid="sign-in-button"]') await page.locator("#user_login").fill("Username") await driver.sleep(2) if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/playwright/raw_gitlab_nested.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import SB with SB(uc=True, locale="en") as sb: sb.activate_cdp_mode() endpoint_url = sb.cdp.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://gitlab.com/users/sign_in") sb.sleep(3) sb.solve_captcha() sb.sleep(1) page.locator('label[for="user_login"]').click() page.wait_for_selector('[data-testid="sign-in-button"]') page.locator("#user_login").fill("Username") sb.sleep(2) ================================================ FILE: examples/cdp_mode/playwright/raw_gitlab_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://gitlab.com/users/sign_in") sb.sleep(3) sb.solve_captcha() sb.sleep(1) page.locator('label[for="user_login"]').click() page.wait_for_selector('[data-testid="sign-in-button"]') page.locator("#user_login").fill("Username") sb.sleep(2) ================================================ FILE: examples/cdp_mode/playwright/raw_idealista_nested.py ================================================ """(Bypasses the DataDome slider CAPTCHA)""" from playwright.sync_api import sync_playwright from seleniumbase import SB with SB(uc=True, locale="es") as sb: url = "https://www.idealista.com/venta-viviendas/barcelona-provincia/" sb.activate_cdp_mode(url) sb.sleep(1) sb.solve_captcha() sb.sleep(2) endpoint_url = sb.cdp.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.click("button#didomi-notice-agree-button") page.wait_for_timeout(1000) print("*** " + page.locator("h1").inner_text()) items = page.locator("div.item-info-container") for i in range(items.count()): item = items.nth(i) print(item.locator("a.item-link").text_content().strip()) print(item.locator("span.item-price").text_content().strip()) item_text = items.nth(i) ================================================ FILE: examples/cdp_mode/playwright/raw_nike_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.nike.com/") page.click('[data-testid="user-tools-container"] search') search = "Pegasus" page.fill('input[type="search"]', search) sb.sleep(4) details = 'ul[data-testid*="products"] figure .details' items = page.locator(details) if items: print('**** Found results for "%s": ****' % search) for i in range(items.count()): item = items.nth(i) print(item.inner_text()) ================================================ FILE: examples/cdp_mode/playwright/raw_nordstrom_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en") endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.nordstrom.com/") sb.sleep(2) page.click("input#keyword-search-input") sb.sleep(0.8) search = "cocktail dresses for women teal" sb.press_keys("input#keyword-search-input", search + "\n") sb.sleep(2.2) for i in range(17): sb.scroll_down(16) sb.sleep(0.14) print('*** Nordstrom Search for "%s":' % search) unique_item_text = [] items = sb.find_elements("article") for item in items: description = item.querySelector("article h3") if description and description.text not in unique_item_text: unique_item_text.append(description.text) price_text = "" price = item.querySelector('div div span[aria-hidden="true"]') if price: price_text = price.text print("* %s (%s)" % (description.text, price_text)) ================================================ FILE: examples/cdp_mode/playwright/raw_planetmc_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome() endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.planetminecraft.com/account/sign_in/") sb.sleep(2) sb.solve_captcha() sb.wait_for_element_absent("input[disabled]") sb.sleep(2) ================================================ FILE: examples/cdp_mode/playwright/raw_reddit_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(use_chromium=True) endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] search = "reddit+scraper" url = f"https://www.reddit.com/r/webscraping/search/?q={search}" page.goto(url) sb.solve_captcha() # Might not be needed sb.sleep(1) post_title = '[data-testid="post-title"]' page.wait_for_selector(post_title) for i in range(8): sb.scroll_down(25) sb.sleep(0.2) print('*** Reddit Posts for "%s":' % search) items = page.locator(post_title) for i in range(items.count()): item_text = items.nth(i).inner_text() print("* " + item_text) ================================================ FILE: examples/cdp_mode/playwright/raw_seatgeek_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en", ad_block=True) endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://seatgeek.com/") input_field = 'input[name="search"]' page.wait_for_selector(input_field) sb.sleep(1.6) query = "Jerry Seinfeld" sb.press_keys(input_field, query) sb.sleep(1.6) page.click("li#active-result-item") sb.sleep(4.2) print('*** SeatGeek Search for "%s":' % query) items = page.locator('[data-testid="listing-item"]') for i in range(items.count()): item_text = items.nth(i).inner_text() print(item_text.replace("\n\n", "\n")) ================================================ FILE: examples/cdp_mode/playwright/raw_walmart_sync.py ================================================ from playwright.sync_api import sync_playwright from seleniumbase import sb_cdp sb = sb_cdp.Chrome(locale="en", guest=True) endpoint_url = sb.get_endpoint_url() with sync_playwright() as p: browser = p.chromium.connect_over_cdp(endpoint_url) context = browser.contexts[0] page = context.pages[0] page.goto("https://www.walmart.com/") sb.sleep(2.6) page.click('input[aria-label="Search"]') sb.sleep(1.4) search = "Settlers of Catan Board Game" required_text = "Catan" sb.press_keys('input[aria-label="Search"]', search + "\n") sb.sleep(3.8) sb.remove_elements('[data-testid="skyline-ad"]') sb.remove_elements('[data-testid="sba-container"]') print('*** Walmart Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item = [] sb.click_if_visible('[data-automation-id="sb-btn-close-mark"]') items = page.locator('[data-item-id]') for i in range(items.count()): item = items.nth(i) if required_text in item.inner_text(): description = item.locator('[data-automation-id="product-title"]') if ( description and description.is_visible() and description.inner_text() not in unique_item ): unique_item.append(description.inner_text()) print("* " + description.inner_text()) price = item.locator('[data-automation-id="product-price"]') if price: price_text = price.inner_text() price_text = price_text.split("current price Now ")[-1] price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") ================================================ FILE: examples/cdp_mode/raw_ad_blocking.py ================================================ import mycdp from seleniumbase import decorators from seleniumbase import sb_cdp async def block_urls(tab): await tab.send(mycdp.network.enable()) await tab.send(mycdp.network.set_blocked_urls( urls=[ "*.googlesyndication.com*", "*.googletagmanager.com*", "*.google-analytics.com*", "*.amazon-adsystem.com*", "*.adsafeprotected.com*", "*.doubleclick.net*", "*.fastclick.net*", "*.snigelweb.com*", "*.2mdn.net*", ] )) with decorators.print_runtime("raw_ad_blocking.py"): sb = sb_cdp.Chrome() loop = sb.get_event_loop() loop.run_until_complete(block_urls(sb.get_active_tab())) sb.open("https://www.w3schools.com/jquery/default.asp") source = sb.get_page_source() sb.assert_false("doubleclick.net" in source) sb.assert_false("google-analytics.com" in source) sb.post_message("Blocking was successful!") sb.driver.quit() ================================================ FILE: examples/cdp_mode/raw_ahrefs.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True, locale="en") as sb: url = "https://ahrefs.com/website-authority-checker" input_field = 'input[placeholder="Enter domain"]' submit_button = 'span:contains("Check Authority")' sb.activate_cdp_mode(url) # The bot-check is later sb.type(input_field, "github.com/seleniumbase/SeleniumBase") sb.scroll_down(36) sb.click(submit_button) sb.sleep(2) sb.solve_captcha() sb.sleep(3) sb.wait_for_text_not_visible("Checking", timeout=15) sb.click_if_visible('button[data-cky-tag="close-button"]') sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")') sb.highlight('a:contains("Top 100 backlinks")') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") ================================================ FILE: examples/cdp_mode/raw_albertsons.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.albertsons.com/recipes/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.remove_element("div > div > article") sb.scroll_into_view('input[type="search"]') close_btn = ".notification-alert-wrapper__close-button" sb.click_if_visible(close_btn) sb.click("input#search-suggestion-input") sb.sleep(0.2) search = "Avocado Smoked Salmon" required_text = "Salmon" sb.press_keys("input#search-suggestion-input", search) sb.sleep(0.8) sb.click("#suggestion-0 a span") sb.sleep(0.8) sb.click_if_visible(close_btn) sb.sleep(3.2) print('*** Albertsons Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] item_selector = 'a[href*="/meal-plans-recipes/shop/"]' items = sb.find_elements(item_selector) for item in items: sb.sleep(0.06) if required_text in item.text: item.flash(color="44CC88") sb.sleep(0.025) if item.text not in unique_item_text: unique_item_text.append(item.text) print("* " + item.text) ================================================ FILE: examples/cdp_mode/raw_amazon.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.amazon.com" sb.activate_cdp_mode(url) sb.sleep(2) sb.click_if_visible('button[alt="Continue shopping"]') sb.sleep(2) sb.press_keys('input[role="searchbox"]', "TI-89\n") sb.sleep(3) for i in range(16): sb.cdp.scroll_down(16) print(sb.get_page_title()) sb.save_as_pdf_to_logs() sb.save_page_source_to_logs() sb.save_screenshot_to_logs() print("Logs have been saved to: ./latest_logs/") ================================================ FILE: examples/cdp_mode/raw_antibot.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/antibot/login" sb.activate_cdp_mode(url) sb.press_keys("input#username", "demo_user") sb.press_keys("input#password", "secret_pass") sb.click("button#myButton") sb.sleep(1.5) sb.click("a#log-in") sb.assert_text("Welcome!", "h1") sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") sb.sleep(1.5) ================================================ FILE: examples/cdp_mode/raw_async.py ================================================ import asyncio import time from contextlib import suppress from seleniumbase import sb_cdp from seleniumbase import cdp_driver async def main(): url = "https://seleniumbase.io/simple/login" driver = await cdp_driver.start_async() page = await driver.get(url, lang="en") print(await page.get_title()) await page.type("#username", "demo_user") await page.type("#password", "secret_pass") await page.click("#log-in") print(await page.get_title()) element = await page.select("h1") assert element.text == "Welcome!" top_nav = await page.select("div.topnav") links = await top_nav.query_selector_all_async("a") for nav_item in links: print(nav_item.text) driver.stop() if __name__ == "__main__": # Call an async function with awaited methods loop = asyncio.new_event_loop() loop.run_until_complete(main()) # An example of wrapping all async calls with event loops driver = cdp_driver.start_sync() page = loop.run_until_complete(driver.get("about:blank")) loop.run_until_complete(page.set_locale("en")) loop.run_until_complete(page.get("https://www.pokemon.com/us")) time.sleep(3) print(loop.run_until_complete(page.evaluate("document.title"))) with suppress(Exception): selector = "button#onetrust-reject-all-handler" element = loop.run_until_complete(page.select(selector, timeout=1)) loop.run_until_complete(element.click_async()) time.sleep(1) element = loop.run_until_complete(page.select("span.icon_pokeball")) loop.run_until_complete(element.click_async()) time.sleep(2) print(loop.run_until_complete(page.evaluate("document.title"))) time.sleep(1) driver.stop() # Call CDP methods via the simplified SB CDP API sb = sb_cdp.Chrome("https://www.priceline.com/") sb.sleep(2.5) sb.internalize_links() # Don't open links in a new tab sb.click("#link_header_nav_experiences") sb.sleep(3.5) sb.remove_elements("msm-cookie-banner") sb.sleep(1.5) location = "Amsterdam" where_to = 'div[data-automation*="experiences"] input' button = 'button[data-automation*="experiences-search"]' sb.wait_for_text("Where to?") sb.click(where_to) sb.press_keys(where_to, location) sb.sleep(1) sb.click(button) sb.sleep(3) print(sb.get_title()) print("************") cards = sb.select_all('span[data-automation*="product-list-card"]') for card in cards: print("* %s" % card.text) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_basic_async.py ================================================ import asyncio from seleniumbase import cdp_driver from seleniumbase import decorators async def main(): url = "https://seleniumbase.io/simple/login" driver = await cdp_driver.start_async() page = await driver.get(url, lang="en") print(await page.get_title()) await page.type("#username", "demo_user") await page.type("#password", "secret_pass") await page.click("#log-in") print(await page.get_title()) element = await page.select("h1") assert element.text == "Welcome!" top_nav = await page.select("div.topnav") links = await top_nav.query_selector_all_async("a") for nav_item in links: print(nav_item.text) driver.stop() if __name__ == "__main__": loop = asyncio.new_event_loop() with decorators.print_runtime("raw_basic_async.py"): loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/raw_basic_cdp.py ================================================ from seleniumbase import sb_cdp url = "https://seleniumbase.io/simple/login" sb = sb_cdp.Chrome(url) sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") top_nav = sb.find_element("div.topnav") links = top_nav.query_selector_all("a") for nav_item in links: print(nav_item.text) sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_basic_mobile.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, mobile=True) as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) ================================================ FILE: examples/cdp_mode/raw_bestwestern.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", guest=True) as sb: url = "https://www.bestwestern.com/en_US.html" sb.activate_cdp_mode(url) sb.sleep(3) sb.click_if_visible(".onetrust-close-btn-handler") sb.sleep(1) sb.click("input#destination-input") sb.sleep(2) location = "Palm Springs, CA, USA" sb.press_keys("input#destination-input", location) sb.sleep(1) sb.click("ul#google-suggestions li") sb.sleep(1) sb.click("button#btn-modify-stay-update") sb.sleep(4) sb.click("label#available-label") sb.sleep(2.5) print("Best Western Hotels in %s:" % location) summary_details = sb.get_text("#summary-details-column") dates = summary_details.split("DESTINATION")[-1] dates = dates.split(" CHECK-OUT")[0].strip() + " CHECK-OUT" dates = dates.replace(" ", " ") print("(Dates: %s)" % dates) flip_cards = sb.select_all(".flipCard") for i, flip_card in enumerate(flip_cards): hotel = flip_card.query_selector(".hotelName") price = flip_card.query_selector(".priceSection") if hotel and price: print("* %s: %s => %s" % ( i + 1, hotel.text.strip(), price.text.strip()) ) ================================================ FILE: examples/cdp_mode/raw_browserscan.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.browserscan.net/bot-detection" sb.activate_cdp_mode(url) sb.sleep(1) sb.cdp.flash("Test Results", duration=4) sb.sleep(1) sb.assert_element('strong:contains("Normal")') sb.cdp.flash('strong:contains("Normal")', duration=4, pause=4) ================================================ FILE: examples/cdp_mode/raw_canvas.py ================================================ """Use SeleniumBase to interact with "canvas" elements.""" from seleniumbase import SB def get_canvas_pixel_colors_at_top_left(sb): # Return the RGB colors of the canvas's top left pixel color = sb.evaluate( "document.querySelector('canvas').getContext('2d')" ".getImageData(%s,%s,1,1).data;" % (0, 0) ) return [color["0"], color["1"], color["2"]] with SB(uc=True, test=True) as sb: # Testing sb.cdp.click_with_offset() url = "https://seleniumbase.io/canvas/" sb.activate_cdp_mode(url) sb.assert_title_contains("Canvas") sb.highlight("canvas") rgb = get_canvas_pixel_colors_at_top_left(sb) sb.assert_equal(rgb, [221, 242, 231]) # Looks greenish sb.click_with_offset("canvas", 500, 350) sb.highlight("canvas", loops=5) rgb = get_canvas_pixel_colors_at_top_left(sb) sb.assert_equal(rgb, [39, 43, 56]) # Blue by hamburger with SB(uc=True, test=True) as sb: # Testing sb.cdp.gui_click_with_offset() url = "https://seleniumbase.io/other/canvas" sb.activate_cdp_mode(url) sb.assert_title_contains("Canvas") sb.click_with_offset("canvas", 0, 0, center=True) sb.sleep(1) sb.uc_gui_press_key("ENTER") sb.sleep(0.5) ================================================ FILE: examples/cdp_mode/raw_cdp.py ================================================ """Example of using CDP Mode without WebDriver""" from seleniumbase import decorators from seleniumbase import sb_cdp @decorators.print_runtime("CDP Priceline Example") def main(): url = "https://www.priceline.com/" sb = sb_cdp.Chrome(url, lang="en") sb.sleep(2) sb.internalize_links() # Don't open links in a new tab sb.click("#link_header_nav_experiences") sb.sleep(3) sb.remove_elements("msm-cookie-banner") sb.sleep(1) location = "Amsterdam" where_to = 'div[data-automation*="experiences"] input' button = 'button[data-automation*="experiences-search"]' sb.wait_for_text("Where to?") sb.click(where_to) sb.press_keys(where_to, location) sb.sleep(1) sb.click(button) sb.sleep(2) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(1) print(sb.get_title()) print("************") for i in range(8): sb.scroll_down(50) sb.sleep(0.2) cards = sb.select_all('span[data-automation*="product-list-card"]') for card in cards: print("* %s" % card.text) sb.driver.stop() if __name__ == "__main__": main() ================================================ FILE: examples/cdp_mode/raw_cdp_copilot.py ================================================ from seleniumbase import sb_cdp url = "https://copilot.microsoft.com/" sb = sb_cdp.Chrome(url, locale="en", guest=True) textarea = "textarea#userInput" sb.wait_for_element(textarea) sb.sleep(1.3) sb.click_if_visible('[aria-label="Dismiss"]') sb.sleep(0.5) sb.click('button[data-testid*="chat-mode-"]') sb.sleep(1.1) sb.click_if_visible('button[title^="Think"]') sb.sleep(1.1) query = "How to start automating with SeleniumBase?" sb.press_keys(textarea, query) sb.sleep(1.1) seen_text = sb.get_text(textarea) if seen_text != query and seen_text in query: # When CAPTCHA appears while typing text sb.sleep(1.1) sb.solve_captcha() sb.sleep(2.2) sb.type(textarea, "") sb.press_keys(textarea, query) sb.sleep(0.5) sb.click('button[data-testid="submit-button"]') sb.sleep(2.5) sb.solve_captcha() sb.sleep(3.5) sb.solve_captcha() sb.sleep(2.5) stop_button = '[data-testid="stop-button"]' thumbs_up = 'button[data-testid*="-thumbs-up-"]' sb.wait_for_element_absent(stop_button, timeout=50) sb.wait_for_element(thumbs_up, timeout=20) sb.sleep(0.6) scroll = 'button[data-testid*="scroll-to-bottom"]' sb.click_if_visible(scroll) sb.sleep(2.2) folder = "downloaded_files" file_name = "copilot_results.html" sb.save_as_html(file_name, folder) print('"./%s/%s" was saved!' % (folder, file_name)) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_drivers.py ================================================ # An example of switching between multiple drivers from seleniumbase import SB with SB(uc=True, test=True) as sb: url1 = "https://seleniumbase.io/antibot/login" sb.activate_cdp_mode(url1) url2 = "https://seleniumbase.io/hobbit/login" driver2 = sb.get_new_driver(undetectable=True) sb.activate_cdp_mode(url2) sb.sleep(1) sb.switch_to_default_driver() sb.assert_url_contains("antibot") print(sb.get_current_url()) sb.type("input#username", "demo_user") sb.type("input#password", "secret_pass") sb.click("button") sb.sleep(1) sb.click("a#log-in") sb.assert_text("Welcome!", "h1") sb.sleep(2) sb.switch_to_driver(driver2) sb.assert_url_contains("hobbit") print(sb.get_current_url()) sb.click("button") sb.assert_text("Welcome to Middle Earth!") sb.click("img") sb.sleep(3) ================================================ FILE: examples/cdp_mode/raw_cdp_extended.py ================================================ """The long way of using CDP Mode without WebDriver""" import asyncio from seleniumbase import sb_cdp from seleniumbase import cdp_driver url = "https://seleniumbase.io/demo_page" loop = asyncio.new_event_loop() driver = cdp_driver.start_sync() page = loop.run_until_complete(driver.get(url)) sb = sb_cdp.CDPMethods(loop, page, driver) sb.press_keys("input", "Text") sb.highlight("button") sb.type("textarea", "Here are some words") sb.click("button") sb.set_value("input#mySlider", "100") sb.click_visible_elements("input.checkBoxClassB") sb.select_option_by_text("#mySelect", "Set to 75%") sb.gui_hover_and_click("#myDropdown", "#dropOption2") sb.gui_click_element("#checkBox1") sb.gui_drag_and_drop("img#logo", "div#drop2") sb.nested_click("iframe#myFrame3", ".fBox") sb.sleep(2) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_gitlab.py ================================================ from seleniumbase import sb_cdp url = "https://gitlab.com/users/sign_in" sb = sb_cdp.Chrome(url, incognito=True) sb.sleep(2) sb.solve_captcha() sb.highlight('h1:contains("GitLab")') sb.highlight('button:contains("Sign in")') sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_hyatt.py ================================================ from seleniumbase import sb_cdp url = "https://www.hyatt.com/" sb = sb_cdp.Chrome(url, locale="en", guest=True) sb.sleep(3.6) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(0.1) sb.click_if_visible("#onetrust-reject-all-handler") sb.sleep(1.2) location = "Anaheim, CA, USA" sb.type('input[id="search-term"]', location) sb.sleep(1.2) sb.click('li[data-js="suggestion"]') sb.sleep(1.2) sb.click("button.be-button-shop") sb.sleep(6) card_info = 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]' hotels = sb.select_all(card_info) print("Hyatt Hotels in %s:" % location) print("(" + sb.get_text('span[class*="summary_destination"]') + ")") if len(hotels) == 0: print("No availability over the selected dates!") for hotel in hotels: info = hotel.text.strip() if "Avg/Night" in info and not info.startswith("Rates from"): name = info.split(" (")[0].split(" + ")[0].split(" Award Cat")[0] name = name.split(" Rates from :")[0] price = "?" if "Rates from : " in info: price = info.split("Rates from : ")[1].split(" Avg/Night")[0] print("* %s => %s" % (name, price)) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_login.py ================================================ from seleniumbase import decorators from seleniumbase import sb_cdp def main(): url = "https://seleniumbase.io/simple/login" sb = sb_cdp.Chrome(url) sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") top_nav = sb.find_element("div.topnav") links = top_nav.query_selector_all("a") for nav_item in links: print(nav_item.text) sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") sb.driver.stop() if __name__ == "__main__": with decorators.print_runtime("raw_cdp_login.py"): main() ================================================ FILE: examples/cdp_mode/raw_cdp_methods.py ================================================ from seleniumbase import sb_cdp url = "https://seleniumbase.io/demo_page" sb = sb_cdp.Chrome(url) sb.press_keys("input", "Text") sb.highlight("button") sb.type("textarea", "Here are some words") sb.click("button") sb.set_value("input#mySlider", "100") sb.click_visible_elements("input.checkBoxClassB") sb.select_option_by_text("#mySelect", "Set to 75%") sb.gui_hover_and_click("#myDropdown", "#dropOption2") sb.gui_click_element("#checkBox1") sb.gui_drag_and_drop("img#logo", "div#drop2") sb.nested_click("iframe#myFrame3", ".fBox") sb.sleep(2) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_mobile.py ================================================ import mycdp from seleniumbase import sb_cdp sb = sb_cdp.Chrome() tab = sb.get_active_tab() loop = sb.get_event_loop() loop.run_until_complete( tab.send( mycdp.emulation.set_device_metrics_override( width=412, height=732, device_scale_factor=3, mobile=True ) ) ) url = "https://gitlab.com/users/sign_in" sb.open(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') ================================================ FILE: examples/cdp_mode/raw_cdp_nike.py ================================================ from seleniumbase import sb_cdp url = "https://www.nike.com/" sb = sb_cdp.Chrome(url) sb.sleep(1.2) sb.click('[data-testid="user-tools-container"] search') sb.sleep(1) search = "Pegasus" sb.press_keys('input[type="search"]', search) sb.sleep(4) details = 'ul[data-testid*="products"] figure .details' elements = sb.select_all(details) if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("* " + element.text) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_nordstrom.py ================================================ from seleniumbase import sb_cdp url = "https://www.nordstrom.com/" sb = sb_cdp.Chrome(url, locale="en", guest=True) sb.sleep(2.2) sb.click("input#keyword-search-input") sb.sleep(0.8) search = "cocktail dresses for women teal" sb.press_keys("input#keyword-search-input", search + "\n") sb.sleep(2.2) for i in range(17): sb.scroll_down(16) sb.sleep(0.14) print('*** Nordstrom Search for "%s":' % search) unique_item_text = [] items = sb.find_elements("article") for item in items: description = item.querySelector("article h3") if description and description.text not in unique_item_text: unique_item_text.append(description.text) price_text = "" price = item.querySelector('div div span[aria-hidden="true"]') if price: price_text = price.text print("* %s (%s)" % (description.text, price_text)) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_pixelscan.py ================================================ from seleniumbase import sb_cdp url = "https://pixelscan.net/fingerprint-check" sb = sb_cdp.Chrome(url, incognito=True) sb.remove_element("#headerBanner") sb.wait_for_element("pxlscn-dynamic-ad") sb.sleep(0.5) sb.remove_elements("pxlscn-dynamic-ad") sb.sleep(2) sb.assert_text("No masking detected", "pxlscn-fingerprint-masking") sb.assert_text("No automated behavior", "pxlscn-bot-detection") sb.highlight('span:contains("is consistent")') sb.sleep(1) sb.highlight("pxlscn-fingerprint-masking p") sb.sleep(1) sb.highlight("pxlscn-bot-detection p") sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_cdp_recaptcha.py ================================================ from seleniumbase import sb_cdp url = "https://seleniumbase.io/apps/recaptcha" sb = sb_cdp.Chrome(url) sb.solve_captcha() sb.assert_element("img#captcha-success") sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_reddit.py ================================================ """Reddit Search / Bypasses reCAPTCHA.""" from seleniumbase import sb_cdp search = "reddit+scraper" url = f"https://www.reddit.com/r/webscraping/search/?q={search}" sb = sb_cdp.Chrome(url, use_chromium=True) sb.solve_captcha() # Might not be needed post_title = '[data-testid="post-title"]' sb.wait_for_element(post_title) for i in range(8): sb.scroll_down(25) sb.sleep(0.2) posts = sb.select_all(post_title) print('*** Reddit Posts for "%s":' % search) for post in posts: print("* " + post.text) ================================================ FILE: examples/cdp_mode/raw_cdp_shadow.py ================================================ """An example of displaying Shadow DOM inside HTML""" from seleniumbase import sb_cdp url = "https://seleniumbase.io/apps/turnstile" sb = sb_cdp.Chrome(url) element = sb.find_element("div.cf-turnstile div") html_with_shadow_dom = element.get_html() print(html_with_shadow_dom) text_to_find = "Widget containing a Cloudflare security challenge" sb.assert_true(text_to_find in html_with_shadow_dom) sb.solve_captcha() sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ================================================ FILE: examples/cdp_mode/raw_cdp_tabs.py ================================================ from seleniumbase import sb_cdp sb = sb_cdp.Chrome() sb.open("data:text/html,

Page A

") sb.assert_text("Page A") sb.open_new_tab() sb.open("data:text/html,

Page B

") sb.assert_text("Page B") sb.switch_to_tab(0) sb.assert_text("Page A") sb.assert_text_not_visible("Page B") sb.switch_to_tab(1) sb.assert_text("Page B") sb.assert_text_not_visible("Page A") sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_turnstile.py ================================================ from seleniumbase import sb_cdp url = "https://seleniumbase.io/apps/turnstile" sb = sb_cdp.Chrome(url) sb.solve_captcha() sb.assert_element("img#captcha-success") sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_walmart.py ================================================ from seleniumbase import sb_cdp url = "https://www.walmart.com/" sb = sb_cdp.Chrome(url, locale="en", guest=True) sb.sleep(3) sb.click('input[aria-label="Search"]') sb.sleep(1.4) search = "Settlers of Catan Board Game" required_text = "Catan" sb.press_keys('input[aria-label="Search"]', search + "\n") sb.sleep(3.8) if sb.is_element_visible("#px-captcha"): sb.gui_click_and_hold("#px-captcha", 7.2) sb.sleep(4.2) if sb.is_element_visible("#px-captcha"): sb.gui_click_and_hold("#px-captcha", 4.2) sb.sleep(3.2) sb.remove_elements('[data-testid="skyline-ad"]') sb.remove_elements('[data-testid="sba-container"]') print('*** Walmart Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] sb.click_if_visible('[data-automation-id="sb-btn-close-mark"]') items = sb.find_elements('[data-item-id]') for item in items: if required_text in item.text: description = item.querySelector( '[data-automation-id="product-title"]' ) if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector( '[data-automation-id="product-price"]' ) if price: price_text = price.text price_text = price_text.split("current price Now ")[-1] price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_cdp_with_sb.py ================================================ """Example of using CDP Mode with WebDriver""" from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.priceline.com/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.internalize_links() # Don't open links in a new tab sb.click("#link_header_nav_experiences") sb.sleep(3.5) sb.remove_elements("msm-cookie-banner") sb.sleep(1.5) location = "Amsterdam" where_to = 'div[data-automation*="experiences"] input' button = 'button[data-automation*="experiences-search"]' sb.wait_for_text("Where to?") sb.cdp.gui_click_element(where_to) sb.press_keys(where_to, location) sb.sleep(1) sb.cdp.gui_click_element(button) sb.sleep(3) print(sb.get_title()) print("************") for i in range(8): sb.scroll_down(50) sb.sleep(0.2) cards = sb.select_all('span[data-automation*="product-list-card"]') for card in cards: print("* %s" % card.text) ================================================ FILE: examples/cdp_mode/raw_cf.py ================================================ """Using CDP Mode to bypass CAPTCHAs in different ways.""" from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.sleep(3) sb.uc_gui_handle_captcha() # PyAutoGUI Tabs + Spacebar sb.sleep(3) with SB(uc=True, test=True, guest=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.sleep(4) sb.uc_gui_click_captcha() # PyAutoGUI mouse click sb.sleep(3) with SB(uc=True, test=True, guest=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.sleep(4) sb.solve_captcha() # CDP Input.dispatchMouseEvent sb.sleep(3) ================================================ FILE: examples/cdp_mode/raw_cf_captcha.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.wait_for_element('div[data-testid*="challenge-widget"]') sb.sleep(2) sb.solve_captcha() sb.sleep(3) ================================================ FILE: examples/cdp_mode/raw_cf_clearance.py ================================================ from seleniumbase import sb_cdp def get_cf_clearance_cookie(sb): all_cookies = sb.get_all_cookies() for cookie in all_cookies: if cookie.name == "cf_clearance": return cookie return None url = "https://gitlab.com/users/sign_in" sb = sb_cdp.Chrome(url) sb.sleep(3) # Wait for CAPTCHA to load sb.solve_captcha() # (Only if found) sb.sleep(2) # Wait for CAPTCHA success cf_cookie = get_cf_clearance_cookie(sb) if cf_cookie: print("cf_clearance cookie: %s" % cf_cookie.value) else: print("Didn't find the cf_clearance cookie!") sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_chatgpt.py ================================================ from contextlib import suppress from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://chatgpt.com/" sb.activate_cdp_mode(url) sb.sleep(1) sb.click_if_visible('button[aria-label="Close dialog"]') sb.click_if_visible('button[data-testid="close-button"]') query = "Compare Playwright to SeleniumBase in under 178 words" sb.press_keys("#prompt-textarea", query) sb.click('button[data-testid="send-button"]') print('*** Input for ChatGPT: ***\n"%s"' % query) sb.sleep(3) with suppress(Exception): # The "Stop" button disappears when ChatGPT is done typing a response sb.wait_for_element_not_visible( 'button[data-testid="stop-button"]', timeout=20 ) chat = sb.find_element('[data-message-author-role="assistant"] .markdown') soup = sb.get_beautiful_soup(chat.get_html()).text.strip() soup = soup.replace("\n\n\n", "\n\n") print("*** Response from ChatGPT: ***\n%s" % soup) sb.sleep(3) ================================================ FILE: examples/cdp_mode/raw_consecutive_c.py ================================================ # An example of bypassing 2 consecutive CF CAPTCHAs""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://sms-man.com/login" sb.activate_cdp_mode(url) sb.sleep(2.2) if not sb.is_element_present('input[name="email"]'): sb.solve_captcha() sb.sleep(1) sb.wait_for_element('[name="email"]', timeout=3) sb.sleep(2) sb.solve_captcha() sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_cookies_async.py ================================================ """A script that loads cookies to bypass login.""" import asyncio import time from seleniumbase import cdp_driver # Log in to Swag Labs and save cookies async def get_login_cookies(): url = "https://www.saucedemo.com" driver = await cdp_driver.start_async(incognito=True) page = await driver.get(url) await page.type("#user-name", "standard_user") await page.type("#password", "secret_sauce") await page.click('input[type="submit"]') cookies = await driver.cookies.get_all() driver.stop() return cookies # Load previously saved cookies to bypass login async def login_with_cookies(cookies): url_1 = "https://www.saucedemo.com" url_2 = "https://www.saucedemo.com/inventory.html" driver = await cdp_driver.start_async() page = await driver.get(url_1) await driver.cookies.set_all(cookies) await driver.get(url_2) await page.select("div.inventory_list") time.sleep(2) driver.stop() if __name__ == "__main__": loop = asyncio.new_event_loop() cookies = loop.run_until_complete(get_login_cookies()) loop.run_until_complete(login_with_cookies(cookies)) ================================================ FILE: examples/cdp_mode/raw_copilot.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://copilot.microsoft.com/" sb.activate_cdp_mode(url) textarea = "textarea#userInput" sb.wait_for_element(textarea) sb.sleep(1.3) sb.click_if_visible('[aria-label="Dismiss"]') sb.sleep(0.5) sb.click('button[data-testid*="chat-mode-"]') sb.sleep(1.1) sb.click_if_visible('button[title^="Think"]') sb.sleep(1.1) query = "How to start automating with SeleniumBase?" sb.press_keys(textarea, query) sb.sleep(1.1) seen_text = sb.get_text(textarea) if seen_text != query and seen_text in query: # When CAPTCHA appears while typing text sb.sleep(1.1) sb.solve_captcha() sb.sleep(2.2) sb.type(textarea, "") sb.press_keys(textarea, query) sb.sleep(0.5) sb.click('button[data-testid="submit-button"]') sb.sleep(2.5) sb.solve_captcha() sb.sleep(3.5) sb.solve_captcha() sb.sleep(2.5) stop_button = '[data-testid="stop-button"]' thumbs_up = 'button[data-testid*="-thumbs-up-"]' sb.wait_for_element_absent(stop_button, timeout=50) sb.wait_for_element(thumbs_up, timeout=20) sb.sleep(0.6) scroll = 'button[data-testid*="scroll-to-bottom"]' sb.click_if_visible(scroll) sb.sleep(2.2) folder = "downloaded_files" file_name = "copilot_results.html" sb.save_page_source(file_name, folder) print('"./%s/%s" was saved!' % (folder, file_name)) ================================================ FILE: examples/cdp_mode/raw_demo_site.py ================================================ """Example of using various CDP Mode commands""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/demo_page" sb.activate_cdp_mode(url) # Assert various things sb.cdp.assert_title("Web Testing Page") sb.cdp.assert_element("tbody#tbodyId") sb.cdp.assert_text("Demo Page", "h1") # Type text into various text fields and then assert sb.cdp.type("#myTextInput", "This is Automated") sb.cdp.type("textarea.area1", "Testing Time!\n") sb.cdp.type('[name="preText2"]', "Typing Text!") sb.cdp.assert_text("This is Automated", "#myTextInput") sb.cdp.assert_text("Testing Time!\n", "textarea.area1") sb.cdp.assert_text("Typing Text!", '[name="preText2"]') # Hover & click a dropdown element and assert results sb.cdp.assert_text("Automation Practice", "h3") sb.cdp.hover_and_click("#myDropdown", "#dropOption2") sb.cdp.assert_text("Link Two Selected", "h3") # Click a button and then verify the expected results sb.cdp.assert_text("This Text is Green", "#pText") sb.cdp.click('button:contains("Click Me")') sb.cdp.assert_text("This Text is Purple", "#pText") # Verify that a slider control updates a progress bar sb.cdp.assert_element('progress[value="50"]') sb.cdp.set_value("input#mySlider", "100") sb.cdp.assert_element('progress[value="100"]') # Verify that a "select" option updates a meter bar sb.cdp.assert_element('meter[value="0.25"]') sb.cdp.select_option_by_text("#mySelect", "Set to 75%") sb.cdp.assert_element('meter[value="0.75"]') # Verify that clicking a radio button selects it sb.cdp.assert_false(sb.cdp.is_selected("#radioButton2")) sb.cdp.click("#radioButton2") sb.cdp.assert_true(sb.cdp.is_selected("#radioButton2")) # Verify that clicking a checkbox makes it selected sb.cdp.assert_element_not_visible("img#logo") sb.cdp.assert_false(sb.cdp.is_selected("#checkBox1")) sb.cdp.click("#checkBox1") sb.cdp.assert_true(sb.cdp.is_selected("#checkBox1")) sb.cdp.assert_element("img#logo") # Verify clicking on multiple elements with one call sb.cdp.assert_false(sb.cdp.is_selected("#checkBox2")) sb.cdp.assert_false(sb.cdp.is_selected("#checkBox3")) sb.cdp.assert_false(sb.cdp.is_selected("#checkBox4")) sb.cdp.click_visible_elements("input.checkBoxClassB") sb.cdp.assert_true(sb.cdp.is_selected("#checkBox2")) sb.cdp.assert_true(sb.cdp.is_selected("#checkBox3")) sb.cdp.assert_true(sb.cdp.is_selected("#checkBox4")) # Verify Drag and Drop sb.cdp.assert_element_not_visible("div#drop2 img#logo") sb.cdp.gui_drag_and_drop("img#logo", "div#drop2") sb.cdp.assert_element("div#drop2 img#logo") # Click inside an iframe and test highlighting sb.cdp.flash("iframe#myFrame3") sb.cdp.sleep(1) sb.cdp.nested_click("iframe#myFrame3", ".fBox") sb.cdp.sleep(0.5) sb.cdp.highlight("iframe#myFrame3") ================================================ FILE: examples/cdp_mode/raw_drag_and_drop.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True) as sb: url = "https://seleniumbase.io/other/drag_and_drop" sb.activate_cdp_mode(url) sb.assert_element_not_visible("#div1 img#drag1") sb.cdp.gui_drag_and_drop("#drag1", "#div1") sb.assert_element("#div1 img#drag1") sb.sleep(1) with SB(uc=True, test=True, incognito=True) as sb: url = "https://jqueryui.com/draggable/" sb.activate_cdp_mode(url) sb.switch_to_frame("iframe") x, y = sb.get_gui_element_center("#draggable") sb.switch_to_default_content() sb.scroll_to_top() sb.cdp.gui_drag_drop_points(x, y, x + 180, y + 90) sb.cdp.gui_drag_drop_points(x + 180, y + 90, x + 60, y + 120) sb.cdp.gui_drag_drop_points(x + 60, y + 120, x + 40, y + 40) sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_driver.py ================================================ import atexit from seleniumbase import Driver driver = Driver(uc=True, guest=True) atexit.register(driver.quit) url = "www.planetminecraft.com/account" driver.activate_cdp_mode(url) driver.sleep(2) driver.solve_captcha() driver.wait_for_element_absent("input[disabled]") driver.sleep(2) ================================================ FILE: examples/cdp_mode/raw_easyjet.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.easyjet.com/en/" sb.activate_cdp_mode(url) sb.sleep(1.5) sb.click_if_visible("button#ensRejectAds", timeout=2) sb.sleep(1) sb.click('input[name="from"]') sb.sleep(1) sb.type('input[name="from"]', "London Gatwick") sb.sleep(1) sb.click('span[data-testid="airport-name"]') sb.sleep(1) sb.type('input[name="to"]', "Paris") sb.sleep(1) sb.click('span[data-testid="airport-name"]') sb.sleep(1) sb.click('input[name="when"]') sb.sleep(1) sb.click('[data-testid="month"]:last-of-type [aria-disabled="false"]') sb.sleep(1) sb.click('[data-testid="month"]:last-of-type [aria-disabled="false"]') sb.sleep(1) sb.click('button[data-testid="submit"]') sb.sleep(4) sb.connect() sb.sleep(1) for window in sb.driver.window_handles: sb.switch_to_window(window) if "/buy/flights" in sb.get_current_url(): break sb.click_if_visible("button#ensCloseBanner") days = sb.find_elements('div[class*="FlightGridLayout_column"]') for day in days: if not day.text.strip(): continue print("**** " + " ".join(day.text.split("\n")[0:2]) + " ****") fares = day.find_elements("css selector", 'button[class*="flightDet"]') if not fares: print("No flights today!") for fare in fares: info = fare.text info = info.replace("LOWEST FARE\n", "") info = info.replace("\n", " ") print(info) ================================================ FILE: examples/cdp_mode/raw_elal.py ================================================ import datetime import re from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "www.elal.com/flight-deals/en-us/flights-from-boston-to-tel-aviv" sb.activate_cdp_mode(url) sb.sleep(3) sb.click('label:contains("Departure date")') sb.sleep(1) today = datetime.date.today() days_ahead = (4 - today.weekday() + 7) % 7 next_friday = today + datetime.timedelta(days=days_ahead) formatted_date = next_friday.strftime("%m/%d/%Y") sb.cdp.gui_click_element('input[aria-describedby*="date-input"]') sb.sleep(1) sb.cdp.gui_press_keys("\b" * 10 + formatted_date + "\n") sb.sleep(1) days_ahead = (4 - today.weekday() + 8) % 14 following_saturday = today + datetime.timedelta(days=days_ahead) formatted_date = following_saturday.strftime("%m/%d/%Y") sb.cdp.gui_click_element( '[data-att="end-date-toggler"] [aria-describedby*="date-input"]' ) sb.sleep(1) sb.cdp.gui_press_keys("\b" * 10 + formatted_date + "\n") sb.sleep(1) sb.click('button[data-att="done"]') sb.sleep(1) sb.click('button[data-att="search"]') sb.sleep(5) sb.click_if_visible("#onetrust-close-btn-container button") sb.sleep(1) view_other_dates = 'button[aria-label*="viewOtherDates.cta"]' if sb.is_element_visible(view_other_dates): sb.click(view_other_dates) sb.sleep(5) if sb.is_element_visible("flexible-search-calendar"): print("*** Flight Calendar for El Al (Boston to Tel Aviv): ***") print(sb.get_text("flexible-search-calendar")) prices = [] elements = sb.find_elements("span.matric-cell__content__price") if elements: print("*** Prices List: ***") for element in elements: prices.append(element.text) prices.sort(key=lambda x: int(re.sub("[^0-9]", "", x))) for price in prices: print(price) print("*** Lowest Price: ***") lowest_price = prices[0] print(lowest_price) sb.scroll_down(12) sb.sleep(1) sb.find_element_by_text(lowest_price).click() sb.sleep(2) search_cell = 'button[aria-label*="Search.cell.buttonTitle"]' sb.scroll_into_view(search_cell) sb.sleep(1) sb.click(search_cell) sb.sleep(5) else: print("*** Lowest Prices: ***") departure_prices = "#uiFlightPanel0 div.ui-bound__price__value" return_prices = "#uiFlightPanel1 div.ui-bound__price__value" elements = sb.find_elements(departure_prices) for element in elements: if "lowest price" in element.text: print("Departure Flight:") print(element.text) break elements = sb.find_elements(return_prices) for element in elements: if "lowest price" in element.text: print("Return Flight:") print(element.text) break dates = sb.find_elements('div[class*="flight-date"]') if len(dates) == 2: print("*** Departure Date: ***") print(dates[0].text) print("*** Return Date: ***") print(dates[1].text) ================================================ FILE: examples/cdp_mode/raw_facebook.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.facebook.com/SeleniumBase" sb.activate_cdp_mode(url) sb.sleep(1) sb.click_if_visible('[aria-label="Close"] i') sb.sleep(1) for i in range(16): sb.cdp.scroll_down(16) print(sb.get_page_title()) sb.save_as_pdf_to_logs() sb.save_page_source_to_logs() sb.save_screenshot_to_logs() print("Logs have been saved to: ./latest_logs/") ================================================ FILE: examples/cdp_mode/raw_fingerprint.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://demo.fingerprint.com/playground" sb.activate_cdp_mode(url) sb.sleep(1) sb.cdp.highlight('a[href*="browser-bot-detection"]') bot_row_selector = 'table:contains("Bot") tr:nth-of-type(3)' print(sb.get_text(bot_row_selector)) sb.assert_text("Bot Not detected", bot_row_selector) sb.cdp.highlight(bot_row_selector) sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_footlocker.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.footlocker.com/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.click_if_visible('button[id*="Agree"]') sb.sleep(1.5) sb.click('input[name="query"]') sb.sleep(1.5) search = "Nike Shoes" sb.press_keys('input[name="query"]', search) sb.sleep(2.5) sb.click('ul[id*="typeahead"] li div') sb.sleep(3.5) elements = sb.select_all("a.ProductCard-link") if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("------------------ >>>") print("* " + element.text) sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_form_turnstile.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "seleniumbase.io/apps/form_turnstile" sb.activate_cdp_mode(url) sb.press_keys("#name", "SeleniumBase") sb.press_keys("#email", "test@test.test") sb.press_keys("#phone", "1-555-555-5555") sb.click('[for="date"]') sb.click("td.is-today button") sb.click('div[class="select-wrapper"] input') sb.click('span:contains("9:00 PM")') sb.highlight_click('input[value="AR"] + span') sb.click('input[value="cc"] + span') sb.scroll_down(40) sb.solve_captcha() sb.highlight("img#captcha-success", timeout=3) sb.highlight_click('button:contains("Request & Pay")') sb.highlight("img#submit-success") sb.highlight('button:contains("Success!")') ================================================ FILE: examples/cdp_mode/raw_gas_records.py ================================================ """(Bypasses the Imperva/Incapsula hCaptcha)""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = ( "https://www.gassaferegister.co.uk/gas-safety" "/gas-safety-certificates-records/building-regulations-certificate" "/order-replacement-building-regulations-certificate/" ) sb.activate_cdp_mode(url) sb.sleep(0.6) sb.solve_captcha() sb.wait_for_element("#SearchTerm", timeout=5) sb.sleep(1.4) allow_cookies = 'button:contains("Allow all cookies")' sb.click_if_visible(allow_cookies, timeout=2) sb.sleep(1) sb.press_keys("#SearchTerm", "Hydrogen") sb.click("button.search-button") sb.sleep(3) results = sb.find_elements("div.search-result") for result in results: print(result.text.replace(" " * 12, " ").strip() + "\n") sb.scroll_to_bottom() sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_geolocation.py ================================================ """Geolocation example using CDP Mode without WebDriver""" from seleniumbase import decorators from seleniumbase import sb_cdp @decorators.print_runtime("Geolocation CDP Example") def main(): url = "https://www.openstreetmap.org/" location = (48.87645, 2.26340) sb = sb_cdp.Chrome(url, geoloc=location, use_chromium=True) sb.sleep(2) sb.click('a[aria-label="Show My Location"]') sb.assert_url_contains("48.876450/2.263400") sb.sleep(5) sb.driver.stop() if __name__ == "__main__": main() ================================================ FILE: examples/cdp_mode/raw_geolocation_sb.py ================================================ """Geolocation example with CDP Mode""" from seleniumbase import SB with SB(uc=True, test=True, use_chromium=True) as sb: url = "https://www.openstreetmap.org/" location = (31.774390, 35.222450) sb.activate_cdp_mode(url, geoloc=location) sb.click('a[aria-label="Show My Location"]') sb.assert_url_contains("31.774390/35.222450") sb.sleep(5) ================================================ FILE: examples/cdp_mode/raw_gettyimages.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", pls="none") as sb: sb.activate_cdp_mode("https://www.gettyimages.com/") sb.click('label:contains("Editorial")') sb.press_keys("form input", "comic con 2024 sci fi panel\n") sb.sleep(3) items = sb.find_elements("figure picture img") for item in items: item.flash(color="44CC88") sb.sleep(0.08) ================================================ FILE: examples/cdp_mode/raw_gitlab.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) ================================================ FILE: examples/cdp_mode/raw_glassdoor.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.glassdoor.com/Reviews/index.htm" sb.activate_cdp_mode(url) sb.sleep(1.5) sb.solve_captcha() sb.sleep(0.5) sb.highlight('[data-test="global-nav-glassdoor-logo"]') sb.highlight('[data-test="site-header-companies"]') sb.highlight('[data-test="search-button"]') sb.highlight('[data-test="sign-in-button"]') sb.highlight('[data-test="company-search-autocomplete"]') ================================================ FILE: examples/cdp_mode/raw_handle_alerts.py ================================================ """An example of handling alerts in CDP Mode.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://the-internet.herokuapp.com/javascript_alerts" sb.activate_cdp_mode(url) sb.click('button[onclick="jsAlert()"]') sb.sleep(1) sb.uc_gui_press_key("\n") # Accept Alert sb.sleep(1) sb.click('button[onclick="jsConfirm()"]') sb.sleep(1) sb.uc_gui_press_key("ESC") # Dismiss Alert sb.sleep(1) sb.click('button[onclick="jsPrompt()"]') sb.sleep(1) sb.uc_gui_write("Here is my prompt answer\n") sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_homedepot.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.homedepot.com/" sb.activate_cdp_mode(url) sb.sleep(1.8) search_box = "input#typeahead-search-field-input" search = "Computer Chair" category = "Gaming Chairs" required_text = "Chair" sb.click(search_box) sb.sleep(1.2) sb.press_keys(search_box, search) sb.sleep(0.6) sb.click("button#typeahead-search-icon-button") sb.sleep(3.8) sb.click('a[aria-label="%s"]' % category) sb.sleep(3.2) print('*** Home Depot Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] items = sb.find_elements('div[data-testid="product-pod"]') for item in items: if required_text in item.text: description = item.querySelector( 'span[data-testid="attribute-product-label"]' ) if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector('[class*="sm:sui-text-4xl"]') if price: price_text = "$%s" % price.text print(" (" + price_text + ")") ================================================ FILE: examples/cdp_mode/raw_hyatt.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.hyatt.com/" sb.activate_cdp_mode(url) sb.sleep(3.2) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(0.1) sb.click_if_visible("#onetrust-reject-all-handler") sb.sleep(1.2) location = "Anaheim, CA, USA" sb.type('input[id="search-term"]', location) sb.sleep(1.2) sb.click('li[data-js="suggestion"]') sb.sleep(0.6) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(0.6) sb.click("button.be-button-shop") sb.sleep(1) sb.click_if_visible('[label="Find Hotels"]') sb.sleep(5) card_info = 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]' hotels = sb.select_all(card_info) print("Hyatt Hotels in %s:" % location) print("(" + sb.get_text('span[class*="summary_destination"]') + ")") if len(hotels) == 0: print("No availability over the selected dates!") for hotel in hotels: info = hotel.text.strip() if "Avg/Night" in info and not info.startswith("Rates from"): name = info.split(" (")[0].split(" + ")[0].split(" Award Cat")[0] name = name.split(" Rates from :")[0] price = "?" if "Rates from : " in info: price = info.split("Rates from : ")[1].split(" Avg/Night")[0] print("* %s => %s" % (name, price)) ================================================ FILE: examples/cdp_mode/raw_idealista.py ================================================ """(Bypasses the DataDome slider CAPTCHA)""" from seleniumbase import SB with SB(uc=True, test=True, locale="es") as sb: url = "https://www.idealista.com/venta-viviendas/barcelona-provincia/" sb.activate_cdp_mode(url) sb.sleep(1) sb.solve_captcha() sb.sleep(2) sb.click("button#didomi-notice-agree-button") print("*** " + sb.get_text("h1")) items = sb.find_elements("div.item-info-container") for item in items: print(item.querySelector("a.item-link").text) print(item.querySelector("span.item-price").text) ================================================ FILE: examples/cdp_mode/raw_indeed.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://www.indeed.com/companies/search" sb.activate_cdp_mode(url) search_box = "input#company-search" if not sb.is_element_present(search_box): sb.sleep(2) sb.solve_captcha() sb.sleep(1) company = "NASA Jet Propulsion Laboratory" sb.click(search_box) sb.sleep(0.1) sb.press_keys(search_box, company) sb.click('button[type="submit"]') sb.click('a:contains("%s")' % company) name_header = 'div[itemprop="name"]' sb.sleep(1) if not sb.is_element_present(name_header): sb.sleep(2) sb.solve_captcha() sb.sleep(1) sb.highlight(name_header) sb.sleep(1) sb.cdp.highlight('h2:contains("About the company")') sb.sleep(1) for i in range(10): sb.scroll_down(12) sb.sleep(0.14) info = sb.find_element('[data-testid="AboutSection-section"]') soup = sb.get_beautiful_soup(info.get_html()).get_text("\n").strip() print("*** %s: ***\n%s" % (company, soup.replace("\n:", ":"))) ================================================ FILE: examples/cdp_mode/raw_indeed_login.py ================================================ """An example of clicking at custom CAPTCHA coordinates.""" from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://secure.indeed.com/auth" sb.activate_cdp_mode(url) sb.sleep(1) sb.type('input[type="email"]', "test@test.com") sb.sleep(1) sb.click('button[type="submit"]') sb.sleep(3.5) selector = 'div[class*="pass-Captcha"]' sb.click_with_offset(selector, 32, 42) sb.sleep(4.5) ================================================ FILE: examples/cdp_mode/raw_kohls.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", incognito=True) as sb: url = "https://www.kohls.com/" sb.activate_cdp_mode(url, ad_block=True) sb.sleep(2.6) search = "Mickey Mouse Blanket" req_1 = "Mickey" req_2 = "Blanket" if not sb.is_element_present('input[name="search"]'): sb.refresh() sb.sleep(2.6) sb.press_keys('input[name="search"]', search + "\n") sb.sleep(5) item_selector = 'div[data-testid*="wallet-wrapper"]' if not sb.is_element_present(item_selector): item_selector = "li.products_grid" for item in sb.find_elements(item_selector): if "Sponsored" in item.text: item.remove_from_dom() sb.remove_elements("#tce-sticky-wrapper") sb.remove_elements("li.sponsored-product") sb.remove_elements("#tce-dec-ces-3-banner") print('*** Kohls Search for "%s":' % search) print(' (Results must contain "%s" and "%s".)' % (req_1, req_2)) title_selector = "div.prod_nameBlock p" if not sb.is_element_present(title_selector): title_selector = 'a[class*="sm:text"][href*="/product/"]' for item in sb.find_elements(title_selector): if item: item.flash(color="44CC88") title = item.text if title: if ( req_1.lower() in title.lower() and req_2.lower() in title.lower() ): print("* " + title) sb.sleep(0.1) sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_linkedin.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.linkedin.com/company/selenium" sb.activate_cdp_mode(url) sb.sleep(2) sb.click_if_visible('button[aria-label="Dismiss"]') sb.sleep(1) for i in range(42): sb.cdp.scroll_down(16) print(sb.get_page_title()) sb.save_as_pdf_to_logs() sb.save_page_source_to_logs() sb.save_screenshot_to_logs() print("Logs have been saved to: ./latest_logs/") ================================================ FILE: examples/cdp_mode/raw_mfa_login.py ================================================ from seleniumbase import sb_cdp url = "https://seleniumbase.io/realworld/login" sb = sb_cdp.Chrome(url) sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") sb.assert_text("Welcome!", "h1") sb.click('a:contains("This Page")') sb.highlight("h1") sb.highlight("img#image1") sb.driver.stop() ================================================ FILE: examples/cdp_mode/raw_mobile_agents.py ================================================ from seleniumbase import SB agent = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36" agent += " (KHTML, like Gecko) Mobile Safari/537.36" sites = ["facebook", "twitter", "linkedin", "youtube", "firefox", "amazon"] sites += ["chatgpt", "gmail", "perplexity", "snapchat", "tiktok", "roblox"] urls = [f"https://www.{site}.com" for site in sites] for url in urls: with SB(uc=True, test=True, mobile=True) as sb: sb.set_window_position(20, 54) sb.activate_cdp_mode() sb.open(url) sb.sleep(2) sb.get_new_driver() sb.set_window_position(550, 54) sb.activate_cdp_mode(agent=agent) sb.open(url) sb.sleep(8) ================================================ FILE: examples/cdp_mode/raw_mobile_async.py ================================================ import asyncio import mycdp import time from seleniumbase import cdp_driver from seleniumbase import decorators async def main(): url = "https://gitlab.com/users/sign_in" driver = await cdp_driver.start_async() await driver.page.send( mycdp.emulation.set_device_metrics_override( width=412, height=732, device_scale_factor=3, mobile=True ) ) page = await driver.get(url, lang="en") time.sleep(3) await page.solve_captcha() element = await page.select('label[for="user_login"]') await element.flash_async(duration=1.5, color="44EE44") time.sleep(1) element = await page.select('[data-testid="sign-in-button"]') await element.flash_async(duration=2, color="44EE44") time.sleep(2) driver.stop() if __name__ == "__main__": loop = asyncio.new_event_loop() with decorators.print_runtime("raw_mobile_async.py"): loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/raw_mobile_gitlab.py ================================================ import mycdp from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode() tab = sb.cdp.get_active_tab() loop = sb.cdp.get_event_loop() loop.run_until_complete( tab.send( mycdp.emulation.set_device_metrics_override( width=412, height=732, device_scale_factor=3, mobile=True ) ) ) sb.open(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) ================================================ FILE: examples/cdp_mode/raw_mobile_roblox.py ================================================ import mycdp from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://www.roblox.com/" agent = ( "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 " "(KHTML, like Gecko) Mobile Safari/537.36" ) sb.activate_cdp_mode(agent=agent) tab = sb.cdp.get_active_tab() loop = sb.cdp.get_event_loop() loop.run_until_complete( tab.send( mycdp.emulation.set_device_metrics_override( width=412, height=732, device_scale_factor=3, mobile=True ) ) ) sb.open(url) sb.assert_element("#download-the-app-container") sb.assert_text("Roblox for Android") sb.highlight('span:contains("Roblox for Android")', loops=8) sb.highlight('span:contains("Continue in App")', loops=8) ================================================ FILE: examples/cdp_mode/raw_mouser.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.mouser.com/" sb.activate_cdp_mode(url) sb.sleep(5) sb.press_keys('input[name="keyword"]', "FLUKE-TC01B 25HZ") sb.click('button[type="submit"]') sb.sleep(2) sb.highlight("h1") sb.highlight("span#spnDescription") sb.highlight("td.ext-price-col") print(sb.get_text("h1")) print(sb.get_text("span#spnDescription")) print(sb.get_text("td.ext-price-col")) ================================================ FILE: examples/cdp_mode/raw_multi_async.py ================================================ # Testing multiple CDP drivers using the async API import asyncio from concurrent.futures import ThreadPoolExecutor from random import randint from seleniumbase import cdp_driver from seleniumbase import decorators async def main(url): driver = await cdp_driver.start_async() page = await driver.get(url) await page.set_window_rect(randint(4, 600), randint(8, 410), 860, 500) await page.sleep(0.5) await page.type("input", "Text") await page.click("button") await page.sleep(2) driver.stop() def set_up_loop(url): loop = asyncio.new_event_loop() loop.run_until_complete(main(url)) if __name__ == "__main__": urls = ["https://seleniumbase.io/demo_page" for i in range(5)] with decorators.print_runtime("raw_multi_async.py"): with ThreadPoolExecutor(max_workers=len(urls)) as executor: for url in urls: executor.submit(set_up_loop, url) ================================================ FILE: examples/cdp_mode/raw_multi_captcha.py ================================================ # Testing multiple CDP drivers using the sync API from concurrent.futures import ThreadPoolExecutor from random import randint from seleniumbase import decorators from seleniumbase import sb_cdp def main(url): sb = sb_cdp.Chrome(url, lang="en", incognito=True) sb.set_window_rect(randint(4, 680), randint(8, 380), 840, 520) sb.sleep(2) sb.gui_click_captcha() sb.sleep(2) sb.driver.quit() if __name__ == "__main__": urls = ["https://seleniumbase.io/apps/turnstile" for i in range(5)] with decorators.print_runtime("raw_multi_captcha.py"): with ThreadPoolExecutor(max_workers=len(urls)) as executor: for url in urls: executor.submit(main, url) ================================================ FILE: examples/cdp_mode/raw_multi_cdp.py ================================================ # Testing multiple CDP drivers using the sync API from concurrent.futures import ThreadPoolExecutor from random import randint from seleniumbase import decorators from seleniumbase import sb_cdp def main(url): sb = sb_cdp.Chrome(url, lang="en") sb.set_window_rect(randint(4, 680), randint(8, 380), 840, 520) sb.press_keys("input", "Text") sb.highlight("button") sb.click("button") sb.sleep(2) sb.driver.quit() if __name__ == "__main__": urls = ["https://seleniumbase.io/demo_page" for i in range(5)] with decorators.print_runtime("raw_multi_cdp.py"): with ThreadPoolExecutor(max_workers=len(urls)) as executor: for url in urls: executor.submit(main, url) ================================================ FILE: examples/cdp_mode/raw_mycdp_cookies.py ================================================ import mycdp from seleniumbase import SB with SB(uc=True, test=True) as sb: sb.activate_cdp_mode("https://learn.microsoft.com/en-us/") tab = sb.cdp.get_active_tab() loop = sb.cdp.get_event_loop() print(loop.run_until_complete(tab.send(mycdp.storage.get_cookies()))) ================================================ FILE: examples/cdp_mode/raw_nevada_search.py ================================================ """Business Entity Search / Bypasses hCaptcha.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://www.nvsilverflume.gov/home" sb.activate_cdp_mode(url) sb.sleep(3) sb.click('a[href="/redirectToCenuity/be"]') sb.sleep(3.6) sb.assert_element('label:contains("Business Search")') sb.click('input#BusinessSearch_Index_rdContains') sb.sleep(0.6) name_field = 'input[data-automation-id*="EntityName"]' search = "Laser Tag" sb.press_keys(name_field, search + "\n") sb.sleep(6.5) print('*** Business Search for "%s":' % search) businesses = sb.select_all("td a[onclick]") for business in businesses: print(business.text) ================================================ FILE: examples/cdp_mode/raw_nike.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", pls="none") as sb: url = "https://www.nike.com/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.click('[data-testid="user-tools-container"] search') sb.sleep(1.5) search = "Nike Air Force 1" sb.press_keys('input[type="search"]', search) sb.sleep(4) details = 'ul[data-testid*="products"] figure .details' elements = sb.select_all(details) if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("* " + element.text) sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_nordstrom.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.nordstrom.com/" sb.activate_cdp_mode(url) sb.sleep(2.2) sb.click("input#keyword-search-input") sb.sleep(0.8) search = "cocktail dresses for women teal" sb.press_keys("input#keyword-search-input", search + "\n") sb.sleep(2.2) for i in range(17): sb.scroll_down(16) sb.sleep(0.14) print('*** Nordstrom Search for "%s":' % search) unique_item_text = [] items = sb.find_elements("article") for item in items: description = item.querySelector("article h3") if description and description.text not in unique_item_text: unique_item_text.append(description.text) price_text = "" price = item.querySelector('div div span[aria-hidden="true"]') if price: price_text = price.text print("* %s (%s)" % (description.text, price_text)) ================================================ FILE: examples/cdp_mode/raw_pixelscan.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True, ad_block=True) as sb: url = "https://pixelscan.net/fingerprint-check" sb.activate_cdp_mode(url) sb.wait_for_element("pxlscn-dynamic-ad") sb.sleep(0.5) sb.remove_elements("pxlscn-dynamic-ad") sb.sleep(2) sb.assert_text("No masking detected", "pxlscn-fingerprint-masking") sb.assert_text("No automated behavior", "pxlscn-bot-detection") sb.cdp.highlight("pxlscn-fingerprint-masking p") sb.sleep(1) sb.cdp.highlight("pxlscn-bot-detection p") sb.sleep(1) sb.cdp.highlight('span.status-success') sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_planetmc.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: url = "www.planetminecraft.com/account/sign_in/" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() sb.wait_for_element_absent("input[disabled]") sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_pokemon.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.pokemon.com/us" sb.activate_cdp_mode(url) sb.sleep(1.5) sb.click_if_visible("button#onetrust-accept-btn-handler") sb.sleep(1.2) sb.click("a span.icon_pokeball") sb.sleep(2.5) sb.click('b:contains("Show Advanced Search")') sb.sleep(2.5) sb.click('span[data-type="type"][data-value="electric"]') sb.sleep(0.7) sb.scroll_into_view("a#advSearch") sb.sleep(0.7) sb.click("a#advSearch") sb.sleep(1.2) sb.click('img[src*="img/pokedex/detail/025.png"]') sb.assert_text("Pikachu", 'div[class*="title"]') sb.assert_element('img[alt="Pikachu"]') sb.scroll_into_view("div.pokemon-ability-info") sb.sleep(1.2) sb.cdp.flash('div[class*="title"]') sb.cdp.flash('img[alt="Pikachu"]') sb.cdp.flash("div.pokemon-ability-info") name = sb.get_text("label.styled-select") info = sb.get_text("div.version-descriptions p.active") print("*** %s: ***\n* %s" % (name, info)) sb.sleep(2) sb.cdp.highlight_overlay("div.pokemon-ability-info") sb.sleep(2) sb.open("https://events.pokemon.com/EventLocator/") sb.sleep(2) sb.click('span:contains("Championship")') sb.sleep(2) events = sb.select_all("div.event-info__title") print("*** Pokémon Championship Events: ***") for event in events: print("* " + event.text) sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_priceline.py ================================================ """Priceline does a lot of A/B testing. Selectors change frequently.""" from seleniumbase import SB with SB(uc=True, test=True, locale="en", guest=True, pls="none") as sb: url = "https://www.priceline.com" sb.activate_cdp_mode(url) sb.sleep(2.6) input_selector = '[name="endLocation"]' if not sb.is_element_present(input_selector): input_selector = "div.location-input input" sb.click(input_selector) sb.sleep(1.2) location = "Portland, OR" selection = "Oregon, United States" # (Dropdown option) sb.press_keys(input_selector, location) sb.sleep(0.6) sb.click(selection) sb.sleep(0.4) sb.scroll_down(25) sb.sleep(0.4) calendar_close = 'button[aria-label="Dismiss calendar"]' if not sb.is_element_visible(calendar_close): calendar_close = '[data-mode="range"] span.px-1' sb.click(calendar_close) sb.sleep(0.6) sb.click('form button[type="submit"]') sb.sleep(4.8) if len(sb.cdp.get_tabs()) > 1: sb.cdp.close_active_tab() sb.cdp.switch_to_newest_tab() sb.sleep(0.6) sb.sleep(0.8) for y in range(1, 9): sb.scroll_to_y(y * 400) sb.sleep(0.5) hotel_names = sb.find_elements('h3 div[class*="TitleName"]') if not hotel_names: hotel_names = sb.find_elements( 'a[data-autobot-element-id*="HOTEL_NAME"]' ) price_selector = '[class*="PriceWrap"] .relative > .items-center' if sb.is_element_visible(price_selector): hotel_prices = sb.find_elements(price_selector) elif sb.is_element_present( '[font-size="12px"] + [font-size="20px"]' ): hotel_prices = sb.find_elements( '[font-size="12px"] + [font-size="20px"]' ) else: hotel_prices = sb.find_elements( 'span.text-priceSuper-heading4 + div > span' ) print("Priceline Hotels in %s:" % location) print(sb.get_text('[data-testid="POPOVER-DATE-PICKER"]')) if len(hotel_names) == 0: print("No availability over the selected dates!") count = 0 for i, hotel in enumerate(hotel_names): if hotel_prices[i] and hotel_prices[i].text: count += 1 hotel_price = "$" + hotel_prices[i].text if hotel_price.startswith("$$ "): hotel_price = hotel_price.replace("$$ ", "$") print("* %s: %s => %s" % (count, hotel.text, hotel_price)) ================================================ FILE: examples/cdp_mode/raw_print_to_pdf.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, pls="none") as sb: url = "https://seleniumbase.io" sb.activate_cdp_mode(url) sb.assert_title("SeleniumBase Docs") file_path = "downloaded_files/sb.pdf" sb.print_to_pdf(file_path) sb.assert_downloaded_file("sb.pdf") sb.assert_pdf_text(file_path, "SeleniumBase") ================================================ FILE: examples/cdp_mode/raw_proxy.py ================================================ from seleniumbase import decorators from seleniumbase import sb_cdp # Change this to "ip:port" or "user:pass@ip:port" proxy = None @decorators.print_runtime("CDP Proxy Example") def main(): url = "https://api.ipify.org/" sb = sb_cdp.Chrome(url, lang="en", pls="none", proxy=proxy) ip_address = sb.get_text("body") if "ERR" in ip_address: raise Exception("Failed to determine IP Address!") print("\n\nMy IP Address = %s\n" % ip_address) sb.open("https://ipinfo.io/%s" % ip_address) sb.sleep(2) sb.wait_for_text(ip_address, "h1", timeout=20) sb.find_element('[href="/signup"]') sb.wait_for_text("Hosted domains", timeout=20) sb.highlight("h1") pop_up = '[role="dialog"] span.cursor-pointer' sb.click_if_visible(pop_up) sb.highlight("#block-summary") sb.click_if_visible(pop_up) sb.highlight("#block-geolocation") sb.click_if_visible(pop_up) sb.sleep(2) print("Displaying Host Info:") text = sb.get_text("#block-summary").split("Hosted domains")[0] rows = text.split("\n") data = [] for row in rows: if row.strip() != "": data.append(row.strip()) print("\n".join(data).replace('\n"', ' "')) print("\nDisplaying GeoLocation Info:") text = sb.get_text("#block-geolocation") text = text.split("IP Geolocation data")[0] rows = text.split("\n") data = [] for row in rows: if row.strip() != "": data.append(row.strip()) print("\n".join(data).replace('\n"', ' "')) sb.click_if_visible(pop_up) sb.sleep(3) sb.driver.stop() if __name__ == "__main__": main() ================================================ FILE: examples/cdp_mode/raw_publication.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.researchgate.net/search/publication" sb.activate_cdp_mode(url) sb.sleep(2.2) shadow_head = "div.main-content div" if sb.is_element_present(shadow_head): sb.cdp.gui_click_element(shadow_head) sb.sleep(1.5) sb.assert_text("Discover the world's scientific knowledge") sb.click_if_visible('button[aria-label="Close"]') sb.highlight('h1[class*="nova"]') sb.highlight('input[name="q"]') sb.highlight("a.menu-item.selected") sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_radwell.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", incognito=True) as sb: url = "https://www.radwell.com/en-US/Search/Advanced/" sb.activate_cdp_mode(url) sb.sleep(1) sb.press_keys("form#basicsearch input", "821C-PM-111DA-142") sb.click('[value="Search Icon"]') sb.sleep(2) if not sb.is_element_visible("a.manufacturer-link"): sb.solve_captcha() sb.sleep(0.5) sb.assert_text("MAC VALVES INC", "a.manufacturer-link") sb.highlight("a.manufacturer-link") description = sb.get_text("div.product-information") description = description.replace(" ", "") description = description.replace("\n \n", "\n") print(description) sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_ralphlauren.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.ralphlauren.com.au/" sb.activate_cdp_mode(url) sb.sleep(1.6) if not sb.is_element_present('[title="Locate Stores"]'): sb.evaluate("window.location.reload();") sb.sleep(1.2) category = "women" search = "Dresses" sb.click('a[data-cgid="%s"]' % category) sb.sleep(2.2) sb.click('a:contains("%s")' % search) sb.sleep(3.8) for i in range(6): sb.scroll_down(25) sb.sleep(0.2) print('*** Ralph Lauren Search for "%s":' % search) unique_item_text = [] items = sb.find_elements('div.product-data') for item in items: description = item.querySelector("a.name-link") if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector('span[title="Price"]') if price: print(" (" + price.text.replace(" ", " ") + ")") ================================================ FILE: examples/cdp_mode/raw_reddit.py ================================================ """Reddit Search / Bypasses reCAPTCHA.""" from seleniumbase import SB with SB(uc=True, test=True, use_chromium=True) as sb: search = "reddit+scraper" url = f"https://www.reddit.com/r/webscraping/search/?q={search}" sb.activate_cdp_mode(url) sb.solve_captcha() # Might not be needed post_title = '[data-testid="post-title"]' sb.wait_for_element(post_title) for i in range(8): sb.scroll_down(25) sb.sleep(0.2) posts = sb.select_all(post_title) print('*** Reddit Posts for "%s":' % search) for post in posts: print("* " + post.text) ================================================ FILE: examples/cdp_mode/raw_reddit_async.py ================================================ import asyncio from seleniumbase import cdp_driver async def main(): search = "reddit+scraper" url = f"https://www.reddit.com/r/webscraping/search/?q={search}" driver = await cdp_driver.start_async(use_chromium=True) page = await driver.get(url) await page.solve_captcha() # Might not be needed post_title = '[data-testid="post-title"]' await page.select(post_title) for i in range(8): await page.scroll_down(25) await page.sleep(0.2) posts = await page.select_all(post_title) print('*** Reddit Posts for "%s":' % search) for post in posts: print("* " + post.text) driver.stop() if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ================================================ FILE: examples/cdp_mode/raw_req_async.py ================================================ """Using CDP.fetch.RequestPaused to filter content in real-time.""" import asyncio import colorama import mycdp import sys from seleniumbase import decorators from seleniumbase import cdp_driver c1 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = c2 = cr = "" class RequestPausedTest(): async def request_paused_handler(self, event, tab): r = event.request is_image = ".png" in r.url or ".jpg" in r.url or ".gif" in r.url if not is_image: # Let the data through tab.feed_cdp( mycdp.fetch.continue_request(request_id=event.request_id) ) else: # Block the data (images) TIMED_OUT = mycdp.network.ErrorReason.TIMED_OUT s = f"{c1}BLOCKING{cr} | {c2}{r.method}{cr} | {r.url}" print(f" >>> ------------\n{s}") tab.feed_cdp( mycdp.fetch.fail_request(event.request_id, TIMED_OUT) ) async def start_test(self): driver = await cdp_driver.start_async() tab = await driver.get("about:blank") tab.add_handler(mycdp.fetch.RequestPaused, self.request_paused_handler) url = "https://gettyimages.com/photos/firefly-2003-nathan" await driver.get(url) await asyncio.sleep(5) driver.stop() @decorators.print_runtime("RequestPausedTest") def main(): test = RequestPausedTest() loop = asyncio.new_event_loop() loop.run_until_complete(test.start_test()) if __name__ == "__main__": main() ================================================ FILE: examples/cdp_mode/raw_req_mod.py ================================================ """Using CDP.fetch.RequestPaused to modify requests in real-time.""" import mycdp from seleniumbase import SB async def request_paused_handler(event, tab): r = event.request rid = event.request_id is_image = ".png" in r.url or ".jpg" in r.url or ".gif" in r.url if not is_image: # Let the data through tab.feed_cdp(mycdp.fetch.continue_request(request_id=rid)) else: # Modify the data (Change the image URL) new_url = "https://seleniumbase.io/other/with_frakes.jpg" tab.feed_cdp(mycdp.fetch.continue_request(request_id=rid, url=new_url)) with SB(uc=True, test=True, locale="en", pls="none") as sb: sb.activate_cdp_mode("about:blank") sb.cdp.add_handler(mycdp.fetch.RequestPaused, request_paused_handler) sb.cdp.open("https://gettyimages.com/photos/jonathan-frakes-cast-2022") new_size = "--width:100;--height:100;" sb.cdp.set_attributes('[style*="--width:"]', "style", new_size) sb.sleep(6) ================================================ FILE: examples/cdp_mode/raw_req_sb.py ================================================ """Using CDP.fetch.RequestPaused to filter content in real-time.""" import colorama import mycdp import sys from seleniumbase import SB c1 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = c2 = cr = "" async def request_paused_handler(event, tab): r = event.request is_image = ".png" in r.url or ".jpg" in r.url or ".gif" in r.url if not is_image: # Let the data through tab.feed_cdp(mycdp.fetch.continue_request(request_id=event.request_id)) else: # Block the data (images) TIMED_OUT = mycdp.network.ErrorReason.TIMED_OUT s = f"{c1}BLOCKING{cr} | {c2}{r.method}{cr} | {r.url}" print(f" >>> ------------\n{s}") tab.feed_cdp(mycdp.fetch.fail_request(event.request_id, TIMED_OUT)) with SB(uc=True, test=True, locale="en") as sb: sb.activate_cdp_mode("about:blank") sb.cdp.add_handler(mycdp.fetch.RequestPaused, request_paused_handler) url = "https://gettyimages.com/photos/firefly-2003-nathan" sb.open(url) sb.sleep(5) ================================================ FILE: examples/cdp_mode/raw_res_nike.py ================================================ """Using CDP.network.RequestWillBeSent and CDP.network.ResponseReceived.""" import colorama import mycdp import sys from seleniumbase import SB c1 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = c2 = cr = "" async def send_handler(event: mycdp.network.RequestWillBeSent): r = event.request s = f"{r.method} {r.url}" for k, v in r.headers.items(): s += f"\n\t{k} : {v}" print(c1 + "*** ==> RequestWillBeSent <== ***" + cr) print(s) async def receive_handler(event: mycdp.network.ResponseReceived): print(c2 + "*** ==> ResponseReceived <== ***" + cr) print(event.response) with SB(uc=True, test=True, locale="en", pls="none") as sb: url = "https://www.nike.com/" sb.activate_cdp_mode(url) sb.cdp.add_handler(mycdp.network.RequestWillBeSent, send_handler) sb.cdp.add_handler(mycdp.network.ResponseReceived, receive_handler) sb.sleep(2.5) sb.click('[data-testid="user-tools-container"] search') sb.sleep(1.5) search = "Nike Air Force 1" sb.press_keys('input[type="search"]', search) sb.sleep(4) elements = sb.select_all('ul[data-testid*="products"] figure .details') if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("* " + element.text) sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_res_sb.py ================================================ """Using CDP.network.RequestWillBeSent and CDP.network.ResponseReceived.""" import colorama import mycdp import sys from seleniumbase import SB c1 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = c2 = cr = "" async def send_handler(event: mycdp.network.RequestWillBeSent): r = event.request s = f"{r.method} {r.url}" for k, v in r.headers.items(): s += f"\n\t{k} : {v}" print(c1 + "*** ==> RequestWillBeSent <== ***" + cr) print(s) async def receive_handler(event: mycdp.network.ResponseReceived): print(c2 + "*** ==> ResponseReceived <== ***" + cr) print(event.response) with SB(uc=True, test=True, locale="en") as sb: sb.activate_cdp_mode("about:blank") sb.cdp.add_handler(mycdp.network.RequestWillBeSent, send_handler) sb.cdp.add_handler(mycdp.network.ResponseReceived, receive_handler) url = "https://seleniumbase.io/apps/calculator" sb.open(url) sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_reuse_browser.py ================================================ """Test connecting to an existing browser.""" from seleniumbase import sb_cdp sb1 = sb_cdp.Chrome("https://example.com") port = sb1.get_rd_port() sb2 = sb_cdp.Chrome(host="127.0.0.1", port=port) print("The remote-debugging port: %s" % port) assert sb1.get_rd_port() == sb2.get_rd_port() assert sb1.get_current_url() == sb2.get_current_url() ================================================ FILE: examples/cdp_mode/raw_science.py ================================================ from seleniumbase import SB with SB(uc=True, incognito=True, test=True) as sb: url = "https://earth.esa.int/eogateway/search" sb.activate_cdp_mode(url) sb.sleep(1) sb.click_if_visible('button:contains("Accept cookies")') for i in range(20): sb.scroll_to_bottom() sb.click_if_visible('button:contains("READ MORE")') sb.sleep(1) elements = sb.find_elements("h4 a span") for element in elements: print(element.text) print("*** Total entries: %s" % len(elements)) ================================================ FILE: examples/cdp_mode/raw_seatgeek.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://seatgeek.com/" sb.activate_cdp_mode(url) input_field = 'input[name="search"]' sb.wait_for_element(input_field) sb.sleep(1.6) query = "Jerry Seinfeld" sb.press_keys(input_field, query) sb.sleep(1.6) sb.click("li#active-result-item") sb.sleep(4.2) print('*** SeatGeek Search for "%s":' % query) item_selector = '[data-testid="listing-item"]' for item in sb.find_elements(item_selector): print(item.text) ================================================ FILE: examples/cdp_mode/raw_socialblade.py ================================================ """Bypass bot-detection to view SocialBlade ranks for YouTube""" from seleniumbase import SB with SB(uc=True, test=True, ad_block=True, pls="none") as sb: url = "https://socialblade.com/" sb.activate_cdp_mode(url) sb.sleep(1.5) if not sb.is_element_visible('input[placeholder*="Search"]'): sb.solve_captcha() sb.sleep(0.5) channel_name = "michaelmintz" channel_title = "Michael Mintz" sb.press_keys('input[placeholder*="Search"]', channel_name) sb.sleep(2) sb.click('a:contains("%s")' % channel_title) sb.sleep(2) sb.remove_elements("#lngtd-top-sticky") sb.sleep(1.5) name = sb.get_text("h3") ch_name = name.split(" ")[-1] name = name.split(" @")[0] link = "https://www.youtube.com/%s" % ch_name print("********** SocialBlade Stats for %s: **********" % name) print(">>> (Link: %s) <<<" % link) print(sb.get_text('[class*="grid lg:hidden"]')) print("********** SocialBlade Ranks: **********") print(sb.get_text('[class*="gap-3 flex-1"]')) for i in range(17): sb.scroll_down(6) sb.sleep(0.1) sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_softpedia.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.softpedia.com/" sb.activate_cdp_mode(url) search_box = 'input[name="search_term"]' search = "3D Model Lab" sb.click(search_box) sb.press_keys(search_box, search + "\n") sb.sleep(2) sb.remove_elements("#adcontainer1") sb.sleep(2.5) print('*** Softpedia Search for "%s":' % search) links = [] item_container = 'div[style="min-height:100px;"]' sb.wait_for_element(item_container) items = sb.find_elements(item_container) for item in items: result = item.querySelector("h4 a") links.append(result.get_attribute("href")) print("* " + result.text) print(item.querySelector("p").get_attribute("title")) for link in links: sb.open(link) sb.remove_elements("div.ad") sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_southwest.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.southwest.com/air/booking/" sb.activate_cdp_mode(url) sb.sleep(2.8) origin = "BOS" destination = "MDW" sb.cdp.click_if_visible("button#onetrust-accept-btn-handler") sb.sleep(0.5) sb.cdp.gui_click_element("input#originationAirportCode") sb.sleep(0.2) sb.cdp.select("input#originationAirportCode").clear_input() sb.sleep(0.2) sb.uc_gui_press_keys(" " + "\n") sb.sleep(0.5) sb.cdp.gui_click_element("input#originationAirportCode") sb.sleep(0.4) sb.uc_gui_press_keys(origin + "\n") sb.sleep(0.4) sb.cdp.gui_click_element("h1") sb.sleep(0.3) sb.cdp.gui_click_element("input#destinationAirportCode") sb.sleep(0.2) sb.cdp.select("input#destinationAirportCode").clear_input() sb.sleep(0.2) sb.uc_gui_press_keys(destination + "\n") sb.sleep(0.4) sb.cdp.gui_click_element("h1") sb.sleep(0.2) sb.cdp.click_if_visible("button#onetrust-accept-btn-handler") sb.sleep(0.1) sb.cdp.click('form button[data-test="submitField"]') sb.sleep(2.5) sb.cdp.click('button[aria-labelledby*="nearby-airport-drawer-"]') sb.sleep(4) day = sb.cdp.get_text('[aria-current="true"] span[class*="cal"]') print("**** Flights from %s to %s ****" % (origin, destination)) flights = sb.find_elements("li.air-booking-select-detail") for flight in flights: info = flight.text departs = info.split("Departs")[-1].split("M")[0].strip() + "M" arrives = info.split("Arrives")[-1].split("M")[0].strip() + "M" stops = flight.query_selector(".flight-stops-badge").text duration = flight.query_selector('[class*="flight-duration"]').text p_elm = flight.query_selector('span.currency span[aria-hidden]') if not p_elm: continue price = p_elm.text print(f"* {day}, {departs} -> {arrives} ({stops}: {duration}) {price}") ================================================ FILE: examples/cdp_mode/raw_stopandshop.py ================================================ """Test Stop & Shop search. Non-US IPs might be blocked.""" from seleniumbase import SB with SB(uc=True, test=True, incognito=True) as sb: url = "https://stopandshop.com/" sb.activate_cdp_mode(url) sb.sleep(2.6) if not sb.is_element_present("#brand-logo_link"): sb.refresh() sb.sleep(2.6) sb.wait_for_element("#brand-logo_link", timeout=3) query = "Fresh Turkey" required_text = "Turkey" search_box = 'input[type="search"]' sb.wait_for_element(search_box) sb.sleep(1.2) sb.press_keys(search_box, query) sb.sleep(1.2) sb.click("button.search-btn") sb.sleep(3.2) print('*** Stop & Shop Search for "%s":' % query) print(' (Results must contain "%s".)' % required_text) print(' (Results cannot contain "Out of Stock")') unique_item_text = [] item_selector = "div.product-tile_content" items = sb.find_elements(item_selector) for item in items: sb.sleep(0.06) if "Out of Stock" not in item.text: if required_text in item.text: item.flash(color="44CC88") sb.sleep(0.025) if item.text not in unique_item_text: unique_item_text.append(item.text) print("* " + item.text) ================================================ FILE: examples/cdp_mode/raw_tab_switching.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: sb.activate_cdp_mode() sb.open("data:text/html,

Page A

") sb.assert_text("Page A") sb.open_new_tab() sb.open("data:text/html,

Page B

") sb.assert_text("Page B") sb.switch_to_tab(0) sb.assert_text("Page A") sb.assert_text_not_visible("Page B") sb.switch_to_tab(1) sb.assert_text("Page B") sb.assert_text_not_visible("Page A") ================================================ FILE: examples/cdp_mode/raw_theaters.py ================================================ """Simple web-scraping example in CDP Mode""" from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://architectureofcities.com/roman-theaters" sb.activate_cdp_mode(url) sb.sleep(0.5) sb.click_if_visible("#cn-close-notice") sb.click_if_visible('[aria-label="Reject All"]') sb.click_if_visible('span:contains("Continue")') sb.sleep(1) print("*** " + sb.get_text("h1") + " ***") for item in sb.find_elements("h3"): if item.text and "." in item.text: item.flash(color="44CC88") sb.scroll_down(34) print("* " + item.text.replace(" ", " ")) sb.sleep(0.15) sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_tiktok.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: url = "https://www.tiktok.com/@startrek?lang=en" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.click_if_visible('button[data-close-button="true"]') print("*** " + sb.get_text('h2[data-e2e="user-bio"]')) for i in range(33): sb.scroll_by_y(33) sb.sleep(0.03) items = sb.find_elements("picture img") for i, item in enumerate(items): print("* %s: %s" % (i, str(item.get_attribute("alt")))) print("*** %s total items found!" % len(items)) sb.sleep(1) ================================================ FILE: examples/cdp_mode/raw_timezone.py ================================================ """Timezone example using CDP Mode without WebDriver""" import mycdp from seleniumbase import decorators from seleniumbase import sb_cdp async def request_paused_handler(event, tab): r = event.request is_image = ".png" in r.url or ".jpg" in r.url or ".gif" in r.url if not is_image: # Let the data through tab.feed_cdp(mycdp.fetch.continue_request(request_id=event.request_id)) else: # Block the data (images) TIMED_OUT = mycdp.network.ErrorReason.TIMED_OUT tab.feed_cdp(mycdp.fetch.fail_request(event.request_id, TIMED_OUT)) @decorators.print_runtime("Timezone CDP Example") def main(): url = "https://www.randymajors.org/what-time-zone-am-i-in" sb = sb_cdp.Chrome( url, ad_block=True, lang="bn", tzone="Asia/Kolkata", geoloc=(26.855323, 80.937710) ) sb.add_handler(mycdp.fetch.RequestPaused, request_paused_handler) sb.remove_elements("#right-sidebar") sb.sleep(6) sb.driver.stop() if __name__ == "__main__": main() ================================================ FILE: examples/cdp_mode/raw_timezone_sb.py ================================================ """An example of changing settings during CDP Mode""" from seleniumbase import SB with SB(uc=True, test=True, pls="eager", ad_block=True) as sb: url = "https://www.randymajors.org/what-time-zone-am-i-in" sb.activate_cdp_mode(url, tzone="Asia/Kolkata", geoloc=(26.863, 80.94)) sb.remove_elements("#right-sidebar") sb.sleep(5) sb.cdp.open(url, tzone="Asia/Tokyo", geoloc=(35.050681, 136.844728)) sb.remove_elements("#right-sidebar") sb.sleep(5) ================================================ FILE: examples/cdp_mode/raw_totalwine.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.totalwine.com/" sb.activate_cdp_mode() sb.open(url) sb.sleep(1.8) search_box = 'input[data-at="header-search-text"]' search = "The Land by Psagot Cabernet" if not sb.is_element_present(search_box): sb.evaluate("window.location.reload();") sb.sleep(1.8) sb.click_if_visible("#onetrust-close-btn-container button") sb.sleep(0.5) sb.click_if_visible('button[aria-label="Close modal"]') sb.sleep(1.2) sb.click(search_box) sb.sleep(1.2) sb.press_keys(search_box, search) sb.sleep(0.6) sb.click_if_visible('button[aria-label="Close modal"]') sb.click('button[data-at="header-search-button"]') sb.sleep(1.8) sb.click_if_visible('button[aria-label="Close modal"]') sb.click('img[data-at="product-search-productimage"]') sb.sleep(2.2) print('*** Total Wine Search for "%s":' % search) print(sb.get_text('h1[data-at="product-name-title"]')) print(sb.get_text('span[data-at="product-mixCaseprice-text"]')) print("Product Highlights:") print(sb.get_text('p[class*="productInformationReview"]')) print("Product Details:") print(sb.get_text('div[data-at="origin-details-table-container"]')) ================================================ FILE: examples/cdp_mode/raw_trails.py ================================================ from seleniumbase import SB with SB(uc=True, ad_block=True, test=True) as sb: url = "https://www.alltrails.com/" sb.activate_cdp_mode(url) sb.sleep(3.5) sb.click_if_visible("button.osano-cm-close") sb.sleep(0.5) search_box = 'input[aria-describedby="search-instructions"]' search_term = "Thundering Brook Falls" sb.wait_for_element(search_box) sb.sleep(1.5) sb.type(search_box, search_term + " Trail") sb.sleep(1.5) sb.click('a span:contains("%s")' % search_term) sb.sleep(3.5) sb.click('button:contains("more")') sb.sleep(0.7) sb.click_if_visible('button[data-testid="modal-close"]') sb.sleep(0.7) print("Description: (%s)\n" % sb.get_text("h1")) print(sb.get_text('div[class*="Description_expanded"]')) sb.scroll_to_bottom() sb.sleep(1.7) sb.click_if_visible('button[data-testid="modal-close"]') sb.sleep(1.7) summary = '[class*="ReviewSummary_summary"] span' print("\nReview Summary:\n\n%s" % sb.get_text(summary)) reviews = sb.select_all('p[class*="styles_reviewText"]') print("\nReviews:") for review in reviews: print("\n" + review.text) folder = "images_exported" file_name = "thundering_brook_falls.png" sb.save_screenshot(file_name, folder, selector="body") print('\n"./%s/%s" was saved!' % (folder, file_name)) folder = "downloaded_files" file_name = "thundering_brook_falls.html" sb.save_page_source(file_name, folder) print('"./%s/%s" was saved!' % (folder, file_name)) ================================================ FILE: examples/cdp_mode/raw_turnstile.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/apps/turnstile" sb.activate_cdp_mode(url) sb.sleep(1) sb.solve_captcha() sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ================================================ FILE: examples/cdp_mode/raw_united.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.united.com/en/us" sb.activate_cdp_mode(url) sb.sleep(3.5) origin_input = 'input[placeholder="Origin"]' origin = "JFK" destination_input = 'input[placeholder="Destination"]' destination = "MCO" sb.wait_for_element(origin_input, timeout=20) sb.sleep(0.5) sb.click(origin_input) sb.sleep(0.5) sb.type(origin_input, origin) sb.sleep(1.2) sb.click('strong:contains("%s")' % origin) sb.sleep(0.6) sb.click_if_visible('button[class*="__close--"]') sb.sleep(0.6) sb.click(destination_input) sb.sleep(0.5) sb.type(destination_input, destination) sb.sleep(1.2) sb.click('strong:contains("%s")' % destination) sb.sleep(1.2) sb.click('button[aria-label="Find flights"]') sb.sleep(4) sb.click_if_visible('button[class*="__close--"]') sb.sleep(2) flights = sb.find_elements('div[class*="CardContainer__block"]') print("**** Flights from %s to %s ****" % (origin, destination)) print(" (" + sb.get_text("h2.atm-c-heading") + ")") if not flights: print("* No flights found!") for flight in flights: flight_info = flight.text.split(" Destination")[0] part_1 = flight_info.split(" Departing at")[0] part_2 = flight_info.split("Arriving at ")[-1] part_2 = part_2.split(" Duration")[0] part_3 = flight.text.split(" Destination")[-1].split(" Aircraft")[0] parts = "%s - %s %s" % (part_1, part_2, part_3) print("* " + parts) for category in ["ECONOMY"]: prices = sb.find_elements('[aria-describedby="%s"]' % category) full_prices = [] for item in prices: item_text = item.text if "Not available" not in item.text: full_prices.append("$%s" % item.text.split("$")[-1]) else: full_prices.append("N/A") print("**** %s Prices:" % category) print(full_prices) sb.scroll_down(50) sb.sleep(1.5) ================================================ FILE: examples/cdp_mode/raw_walmart.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.walmart.com/" sb.activate_cdp_mode(url) sb.sleep(1.8) continue_button = 'button:contains("Continue shopping")' if sb.is_element_visible(continue_button): sb.cdp.gui_click_element(continue_button) sb.sleep(0.6) sb.click('input[aria-label="Search"]') sb.sleep(1.2) search = "Settlers of Catan Board Game" required_text = "Catan" sb.press_keys('input[aria-label="Search"]', search + "\n") sb.sleep(3.8) if sb.is_element_visible("#px-captcha"): sb.cdp.gui_click_and_hold("#px-captcha", 7.2) sb.sleep(4.2) if sb.is_element_visible("#px-captcha"): sb.cdp.gui_click_and_hold("#px-captcha", 4.2) sb.sleep(3.2) sb.remove_elements('[data-testid="skyline-ad"]') sb.remove_elements('[data-testid="sba-container"]') print('*** Walmart Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] sb.click_if_visible('[data-automation-id="sb-btn-close-mark"]') items = sb.find_elements('[data-item-id]') for item in items: if required_text in item.text: description = item.querySelector( '[data-automation-id="product-title"]' ) if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector( '[data-automation-id="product-price"]' ) if price: price_text = price.text price_text = price_text.split("current price Now ")[-1] price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") ================================================ FILE: examples/cdp_mode/raw_wsform.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en", incognito=True) as sb: url = "https://wsform.com/demo/" sb.activate_cdp_mode(url) sb.sleep(2) sb.scroll_into_view("div.grid") sb.uc_gui_click_captcha() sb.sleep(2) ================================================ FILE: examples/cdp_mode/raw_xhr_async.py ================================================ """CDP.network.ResponseReceived with CDP.network.ResourceType.XHR.""" import ast import asyncio import colorama import mycdp import sys import time from seleniumbase import cdp_driver xhr_requests = [] last_xhr_request = None c1 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = c2 = cr = "" def listenXHR(page): async def handler(evt): # Get AJAX requests if evt.type_ is mycdp.network.ResourceType.XHR: xhr_requests.append([evt.response.url, evt.request_id]) global last_xhr_request last_xhr_request = time.time() page.add_handler(mycdp.network.ResponseReceived, handler) async def receiveXHR(page, requests): responses = [] retries = 0 max_retries = 5 # Wait at least 2 seconds after last XHR request for more while True: if last_xhr_request is None or retries > max_retries: break if time.time() - last_xhr_request <= 2: retries = retries + 1 time.sleep(2) continue else: break await page # Loop through gathered requests and get response body for request in requests: try: res = await page.send(mycdp.network.get_response_body(request[1])) if res is None: continue responses.append({ "url": request[0], "body": res[0], "is_base64": res[1], }) except Exception as e: print("Error getting response:", e) return responses async def crawl(): driver = await cdp_driver.start_async() tab = await driver.get("about:blank") listenXHR(tab) # Change url to something that makes ajax requests tab = await driver.get("https://learn.microsoft.com/en-us/") time.sleep(1.5) for i in range(18): await tab.scroll_down(3) time.sleep(0.02) xhr_responses = await receiveXHR(tab, xhr_requests) for response in xhr_responses: print(c1 + "*** ==> XHR Request URL <== ***" + cr) print(f'{response["url"]}') is_base64 = response["is_base64"] b64_data = "Base64 encoded data" try: headers = ast.literal_eval(response["body"])["headers"] print(c2 + "*** ==> XHR Response Headers <== ***" + cr) print(headers if not is_base64 else b64_data) except Exception: response_body = response["body"] print(c2 + "*** ==> XHR Response Body <== ***" + cr) print(response_body if not is_base64 else b64_data) if __name__ == "__main__": print("<============= START: XHR Example =============>") asyncio.run(crawl()) print("<============== END: XHR Example ==============>") ================================================ FILE: examples/cdp_mode/raw_xhr_sb.py ================================================ """CDP.network.ResponseReceived with CDP.network.ResourceType.XHR.""" import ast import colorama import mycdp import sys import time from seleniumbase import SB xhr_requests = [] last_xhr_request = None c1 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = c2 = cr = "" def listenXHR(page): async def handler(evt): # Get AJAX requests if evt.type_ is mycdp.network.ResourceType.XHR: xhr_requests.append([evt.response.url, evt.request_id]) global last_xhr_request last_xhr_request = time.time() page.add_handler(mycdp.network.ResponseReceived, handler) async def receiveXHR(page, requests): responses = [] retries = 0 max_retries = 5 # Wait at least 2 seconds after last XHR request for more while True: if last_xhr_request is None or retries > max_retries: break if time.time() - last_xhr_request <= 2: retries = retries + 1 time.sleep(2) continue else: break await page # Loop through gathered requests and get response body for request in requests: try: res = await page.send(mycdp.network.get_response_body(request[1])) if res is None: continue responses.append({ "url": request[0], "body": res[0], "is_base64": res[1], }) except Exception as e: print("Error getting response:", e) return responses with SB(uc=True, test=True, locale="en") as sb: sb.activate_cdp_mode("about:blank") tab = sb.cdp.page listenXHR(tab) # Change url to something that makes ajax requests sb.cdp.open("https://learn.microsoft.com/en-us/") time.sleep(1) for i in range(9): sb.cdp.scroll_down(6) loop = sb.cdp.get_event_loop() xhr_responses = loop.run_until_complete(receiveXHR(tab, xhr_requests)) for response in xhr_responses: print(c1 + "*** ==> XHR Request URL <== ***" + cr) print(f'{response["url"]}') is_base64 = response["is_base64"] b64_data = "Base64 encoded data" try: headers = ast.literal_eval(response["body"])["headers"] print(c2 + "*** ==> XHR Response Headers <== ***" + cr) print(headers if not is_base64 else b64_data) except Exception: response_body = response["body"] print(c2 + "*** ==> XHR Response Body <== ***" + cr) print(response_body if not is_base64 else b64_data) ================================================ FILE: examples/cdp_mode/raw_xpath.py ================================================ """Test that CDP Mode can autodetect and use XPath selectors.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/demo_page" sb.activate_cdp_mode(url) sb.cdp.highlight('//input[@id="myTextInput"]') sb.cdp.type('//*[@id="myTextInput"]', "XPath Test!") sb.sleep(0.5) sb.cdp.highlight('//button[contains(text(),"(Green)")]') sb.cdp.click('//button[starts-with(text(),"Click Me")]') sb.cdp.assert_element('//button[contains(., "Purple")]') sb.sleep(0.5) sb.cdp.highlight("//table/tbody/tr/td/h3") sb.cdp.highlight("//table/tbody/tr[1]/td[2]/h2") sb.cdp.assert_text("SeleniumBase", "//table//h2") ================================================ FILE: examples/cdp_mode/raw_zoro.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://www.zoro.com/" sb.activate_cdp_mode() sb.open(url) sb.sleep(1.2) search_box = "input#searchInput" search = "Flir Thermal Camera" required_text = "Camera" if not sb.is_element_present(search_box): sb.evaluate("window.location.reload();") sb.sleep(1.2) sb.click(search_box) sb.sleep(1.2) sb.press_keys(search_box, search) sb.sleep(0.6) sb.click('button[data-za="searchButton"]') sb.sleep(3.2) sb.wait_for_element('[data-za="product-cards-list"]', timeout=5) print('*** Zoro Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] items = sb.find_elements('[data-za="search-product-card"]') for item in items: if required_text in item.text: description = item.querySelector("h2") if description and description.text not in unique_item_text: unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector("div.price-main") if price: print(" (" + price.text + ")") ================================================ FILE: examples/chart_maker/ReadMe.md ================================================

📊 ChartMaker 📶

SeleniumBase ChartMaker lets you use Python to generate HTML charts.


([Click to see a presentation with multiple charts](https://seleniumbase.io/other/chart_presentation.html)) Here's how to run a simple pie chart presentation from [GitHub => seleniumbase/SeleniumBase/examples/chart_maker](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/chart_maker): ```zsh cd examples/chart_maker pytest my_chart.py ``` Here's the code for that pie chart presentation ([GitHub => seleniumbase/SeleniumBase/examples/chart_maker/my_chart.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/chart_maker/my_chart.py)): ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_chart_maker(self): self.create_presentation() self.create_pie_chart(title="Automated Tests") self.add_data_point("Passed", 7, color="#95d96f") self.add_data_point("Untested", 2, color="#eaeaea") self.add_data_point("Failed", 1, color="#f1888f") self.add_slide("

Pie Chart

" + self.extract_chart()) self.begin_presentation(filename="my_chart.html") ``` Here's how to run an example presentation with multiple charts: ```zsh cd examples/chart_maker pytest chart_presentation.py ``` Here are screenshots from the examples:





Here's a line chart example:

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_chart_maker(self): self.create_presentation() self.create_line_chart( title="Time Outside", subtitle="Last Week", unit="Minutes") self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.add_slide("

Line Chart

" + self.extract_chart()) self.begin_presentation(filename="line_chart.html", interval=8) ``` This example is from [test_line_chart.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/chart_maker/test_line_chart.py), which you can run from the ``examples/chart_maker`` folder with the following command: ```zsh pytest test_line_chart.py ``` Because that presentation above has an ``interval`` set to ``8``, it will automatically advance to the next slide after 8 seconds. (Or exit if there are no more slides.)

For a more advanced example (multiple charts in a presentation):

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_chart_maker_presentation(self): self.create_presentation(theme="sky") self.create_pie_chart(title="Automated Tests") self.add_data_point("Passed", 7, color="#95d96f") self.add_data_point("Untested", 2, color="#eaeaea") self.add_data_point("Failed", 1, color="#f1888f") self.add_slide("

Pie Chart

" + self.extract_chart()) self.create_bar_chart(title="Language") self.add_data_point("Python", 33, color="Orange") self.add_data_point("JavaScript", 27, color="Teal") self.add_data_point("HTML + CSS", 21, color="Purple") self.add_slide("

Bar Chart

" + self.extract_chart()) self.create_column_chart(title="Colors") self.add_data_point("Red", 10, color="Red") self.add_data_point("Green", 25, color="Green") self.add_data_point("Blue", 15, color="Blue") self.add_slide("

Column Chart

" + self.extract_chart()) self.create_line_chart(title="Last Week's Data") self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.add_slide("

Line Chart

" + self.extract_chart()) self.begin_presentation(filename="chart_presentation.html") ``` Here's how to run that example: ```zsh cd examples/chart_maker pytest chart_presentation.py ``` (Press the Right Arrow to advance to the next slide in that chart presentation) ([Click to see a live example of that presentation](https://seleniumbase.io/other/chart_presentation.html)) Multi-Series charts can also be created. Try the available examples to learn more.

ChartMaker API

```python self.create_pie_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, libs=True): """ Creates a JavaScript pie chart using "HighCharts". @Params chart_name - If creating multiple charts, use this to select which one. title - The title displayed for the chart. subtitle - The subtitle displayed for the chart. data_name - The series name. Useful for multi-series charts. If no data_name, will default to using "Series 1". unit - The description label given to the chart's y-axis values. libs - The option to include Chart libraries (JS and CSS files). Should be set to True (default) for the first time creating a chart on a web page. If creating multiple charts on the same web page, you won't need to re-import the libraries when creating additional charts. labels - If True, displays labels on the chart for data points. legend - If True, displays the data point legend on the chart. """ ``` ```python self.create_bar_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, libs=True): """ Creates a JavaScript bar chart using "HighCharts". @Params chart_name - If creating multiple charts, use this to select which one. title - The title displayed for the chart. subtitle - The subtitle displayed for the chart. data_name - The series name. Useful for multi-series charts. If no data_name, will default to using "Series 1". unit - The description label given to the chart's y-axis values. libs - The option to include Chart libraries (JS and CSS files). Should be set to True (default) for the first time creating a chart on a web page. If creating multiple charts on the same web page, you won't need to re-import the libraries when creating additional charts. labels - If True, displays labels on the chart for data points. legend - If True, displays the data point legend on the chart. """ ``` ```python self.create_column_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, libs=True): """ Creates a JavaScript column chart using "HighCharts". @Params chart_name - If creating multiple charts, use this to select which one. title - The title displayed for the chart. subtitle - The subtitle displayed for the chart. data_name - The series name. Useful for multi-series charts. If no data_name, will default to using "Series 1". unit - The description label given to the chart's y-axis values. libs - The option to include Chart libraries (JS and CSS files). Should be set to True (default) for the first time creating a chart on a web page. If creating multiple charts on the same web page, you won't need to re-import the libraries when creating additional charts. labels - If True, displays labels on the chart for data points. legend - If True, displays the data point legend on the chart. """ ``` ```python self.create_line_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, zero=False, libs=True): """ Creates a JavaScript line chart using "HighCharts". @Params chart_name - If creating multiple charts, use this to select which one. title - The title displayed for the chart. subtitle - The subtitle displayed for the chart. data_name - The series name. Useful for multi-series charts. If no data_name, will default to using "Series 1". unit - The description label given to the chart's y-axis values. zero - If True, the y-axis always starts at 0. (Default: False). libs - The option to include Chart libraries (JS and CSS files). Should be set to True (default) for the first time creating a chart on a web page. If creating multiple charts on the same web page, you won't need to re-import the libraries when creating additional charts. labels - If True, displays labels on the chart for data points. legend - If True, displays the data point legend on the chart. """ ``` ```python self.create_area_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, zero=False, libs=True): """ Creates a JavaScript area chart using "HighCharts". @Params chart_name - If creating multiple charts, use this to select which one. title - The title displayed for the chart. subtitle - The subtitle displayed for the chart. data_name - The series name. Useful for multi-series charts. If no data_name, will default to using "Series 1". unit - The description label given to the chart's y-axis values. zero - If True, the y-axis always starts at 0. (Default: False). libs - The option to include Chart libraries (JS and CSS files). Should be set to True (default) for the first time creating a chart on a web page. If creating multiple charts on the same web page, you won't need to re-import the libraries when creating additional charts. labels - If True, displays labels on the chart for data points. legend - If True, displays the data point legend on the chart. """ ``` If creating multiple charts at the same time, you can pass the ``chart_name`` parameter to distinguish between different charts.

Adding a data point to a chart:

```python self.add_data_point(label, value, color=None, chart_name=None): """ Add a data point to a SeleniumBase-generated chart. @Params label - The label name for the data point. value - The numeric value of the data point. color - The HTML color of the data point. Can be an RGB color. Eg: "#55ACDC". Can also be a named color. Eg: "Teal". chart_name - If creating multiple charts, use this to select which one. """ ```

Adding a new data series to an existing chart:

```python self.add_series_to_chart(self, data_name=None, chart_name=None): """ Add a new data series to an existing chart. This allows charts to have multiple data sets. @Params data_name - Set the series name. Useful for multi-series charts. chart_name - If creating multiple charts, use this to select which one. """ ```

Saving a chart to a file:

```python self.save_chart(chart_name=None, filename=None): """ Saves a SeleniumBase-generated chart to a file for later use. @Params chart_name - If creating multiple charts at the same time, use this to select the one you wish to use. filename - The name of the HTML file that you wish to save the chart to. (filename must end in ".html") """ ``` The full HTML of the chart is saved to the ``saved_charts/`` folder.

Extracting the HTML of a chart:

```python self.extract_chart(chart_name=None): """ Extracts the HTML from a SeleniumBase-generated chart. @Params chart_name - If creating multiple charts at the same time, use this to select the one you wish to use. """ ```

Displaying a chart in the browser window:

```python self.display_chart(chart_name=None, filename=None): """ Displays a SeleniumBase-generated chart in the browser window. @Params chart_name - If creating multiple charts at the same time, use this to select the one you wish to use. filename - The name of the HTML file that you wish to save the chart to. (filename must end in ".html") interval - The delay time for auto-advancing charts. (in seconds) If set to 0 (default), auto-advancing is disabled. """ ``` All methods have the optional ``chart_name`` argument, which is only needed when storing multiple charts at the same time. --------

(Video: Pie Charts on Pi Day)

================================================ FILE: examples/chart_maker/chart_presentation.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ChartMakerPresentation(BaseCase): def test_chart_maker_presentation(self): self.create_presentation(theme="sky", transition="zoom") self.create_pie_chart(title="Automated Tests") self.add_data_point("Passed", 7, color="#95d96f") self.add_data_point("Untested", 2, color="#eaeaea") self.add_data_point("Failed", 1, color="#f1888f") self.add_slide("

Pie Chart

" + self.extract_chart()) self.create_bar_chart(title="Language", legend=False) self.add_data_point("Python", 33, color="Orange") self.add_data_point("JavaScript", 27, color="Teal") self.add_data_point("HTML + CSS", 21, color="Purple") self.add_slide("

Bar Chart

" + self.extract_chart()) self.create_column_chart(title="Colors", legend=False) self.add_data_point("Red", 10, color="Red") self.add_data_point("Green", 25, color="Green") self.add_data_point("Blue", 15, color="Blue") self.add_slide("

Column Chart

" + self.extract_chart()) self.create_line_chart(title="Last Week's Data") self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.add_slide("

Line Chart

" + self.extract_chart()) self.begin_presentation(filename="chart_presentation.html") ================================================ FILE: examples/chart_maker/my_chart.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_chart_maker(self): self.create_presentation() self.create_pie_chart(title="Automated Tests") self.add_data_point("Passed", 7, color="#95d96f") self.add_data_point("Untested", 2, color="#eaeaea") self.add_data_point("Failed", 1, color="#f1888f") self.add_slide("

Pie Chart

" + self.extract_chart()) self.begin_presentation(filename="my_chart.html") ================================================ FILE: examples/chart_maker/pie_charts.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class PieCharts(BaseCase): def test_pie_charts(self): self.create_presentation(theme="serif", transition="convex") self.create_pie_chart(labels=False) self.add_data_point("Meringue Cream", 3, color="#f1eeea") self.add_data_point("Lemon Filling", 3, color="#e9d655") self.add_data_point("Graham Cracker Crust", 1, color="#9d5b34") self.add_slide("

Lemon Meringue Pie

" + self.extract_chart()) self.create_pie_chart(labels=False) self.add_data_point("Blueberries", 1, color="#5c81b7") self.add_data_point("Blueberry Filling", 2, color="#12405e") self.add_data_point("Golden Brown Crust", 1, color="#cd7b54") self.add_slide("

Blueberry Pie

" + self.extract_chart()) self.create_pie_chart(labels=False) self.add_data_point("Strawberries", 1, color="#ff282c") self.add_data_point("Kiwis", 1, color="#a9c208") self.add_data_point("Apricots", 1, color="#f47a14") self.add_data_point("Raspberries", 1, color="#b10019") self.add_data_point("Black Berries", 1, color="#44001e") self.add_data_point("Blueberries", 1, color="#5c81b7") self.add_data_point("Custard", 3, color="#eee896") self.add_data_point("Golden Crust", 4, color="#dca422") self.add_slide("

Fruit Tart Pie

" + self.extract_chart()) self.create_pie_chart(labels=False) self.add_data_point("Apple Crust", 4, color="#b66327") self.add_data_point("Apple Filling", 5, color="#c5903e") self.add_data_point("Cinnamon", 1, color="#76210d") self.add_data_point("Whipped Cream", 2, color="#f2f2f2") self.add_slide("

Apple Pie

" + self.extract_chart()) self.create_pie_chart(labels=False) self.add_data_point("Sponge Cake", 4, color="#e0d5a0") self.add_data_point("Custard", 3, color="#eee896") self.add_data_point("Chocolate", 1, color="#5c3625") self.add_slide("

Boston Cream Pie

" + self.extract_chart()) self.begin_presentation(filename="pie_charts.html") ================================================ FILE: examples/chart_maker/test_area_chart.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_area_chart(self): self.create_presentation(theme="moon") self.create_area_chart( title="Time Outside", subtitle="Last Week", unit="Minutes" ) self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.add_slide("

Area Chart

" + self.extract_chart()) self.begin_presentation(filename="line_chart.html", interval=4) ================================================ FILE: examples/chart_maker/test_display_chart.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_display_chart(self): self.create_pie_chart(title="Pie Chart") self.add_data_point("Passed", 7, color="#95d96f") self.add_data_point("Untested", 2, color="#eaeaea") self.add_data_point("Failed", 1, color="#f1888f") self.display_chart(filename="pie_chart.html", interval=2.5) self.create_bar_chart(title="Bar Chart", legend=False) self.add_data_point("Python", 33, color="Orange") self.add_data_point("JavaScript", 27, color="Teal") self.add_data_point("HTML + CSS", 21, color="Purple") self.display_chart(filename="bar_chart.html", interval=2.5) self.create_column_chart(title="Column Chart", legend=False) self.add_data_point("Red", 10, color="Red") self.add_data_point("Green", 25, color="Green") self.add_data_point("Blue", 15, color="Blue") self.display_chart(filename="column_chart.html", interval=2.5) self.create_line_chart(title="Line Chart") self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.display_chart(filename="line_chart.html", interval=2.5) ================================================ FILE: examples/chart_maker/test_line_chart.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_line_chart(self): self.create_presentation() self.create_line_chart( title="Time Outside", subtitle="Last Week", unit="Minutes" ) self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.add_slide("

Line Chart

" + self.extract_chart()) self.begin_presentation(filename="line_chart.html", interval=4) ================================================ FILE: examples/chart_maker/test_multi_series.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_multi_series(self): self.create_presentation(theme="league") self.create_line_chart( title="Fruit Sold Last Week", data_name="Apples", unit="Count" ) self.add_data_point("Sun", 33) self.add_data_point("Mon", 16) self.add_data_point("Tue", 19) self.add_data_point("Wed", 28) self.add_data_point("Thu", 20) self.add_data_point("Fri", 30) self.add_data_point("Sat", 36) self.add_series_to_chart(data_name="Oranges") self.add_data_point("Sun", 22) self.add_data_point("Mon", 27) self.add_data_point("Tue", 23) self.add_data_point("Wed", 21) self.add_data_point("Thu", 26) self.add_data_point("Fri", 17) self.add_data_point("Sat", 25) self.add_series_to_chart(data_name="Strawberries") self.add_data_point("Sun", 41) self.add_data_point("Mon", 32) self.add_data_point("Tue", 38) self.add_data_point("Wed", 33) self.add_data_point("Thu", 31) self.add_data_point("Fri", 42) self.add_data_point("Sat", 40) self.add_series_to_chart(data_name="Cherries") self.add_data_point("Sun", 28) self.add_data_point("Mon", 37) self.add_data_point("Tue", 29) self.add_data_point("Wed", 24) self.add_data_point("Thu", 34) self.add_data_point("Fri", 26) self.add_data_point("Sat", 31) self.add_slide("

Multi-Series Line Chart

" + self.extract_chart()) self.begin_presentation(filename="multi_series_chart.html", interval=4) ================================================ FILE: examples/chart_maker/test_save_chart.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyChartMakerClass(BaseCase): def test_save_chart(self): self.create_pie_chart(title="Pie Chart") self.add_data_point("Passed", 7, color="#95d96f") self.add_data_point("Untested", 2, color="#eaeaea") self.add_data_point("Failed", 1, color="#f1888f") self.save_chart(filename="pie_chart.html") self.create_bar_chart(title="Bar Chart") self.add_data_point("Python", 33, color="Orange") self.add_data_point("JavaScript", 27, color="Teal") self.add_data_point("HTML + CSS", 21, color="Purple") self.save_chart(filename="bar_chart.html") self.create_column_chart(title="Column Chart") self.add_data_point("Red", 10, color="Red") self.add_data_point("Green", 25, color="Green") self.add_data_point("Blue", 15, color="Blue") self.save_chart(filename="column_chart.html") self.create_line_chart(title="Line Chart") self.add_data_point("Sun", 5) self.add_data_point("Mon", 10) self.add_data_point("Tue", 20) self.add_data_point("Wed", 40) self.add_data_point("Thu", 80) self.add_data_point("Fri", 65) self.add_data_point("Sat", 50) self.save_chart(filename="line_chart.html") ================================================ FILE: examples/coffee_cart_tests.py ================================================ """Use SeleniumBase to test the Coffee Cart App.""" from parameterized import parameterized from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CoffeeCartTests(BaseCase): def test_1_verify_nav_link_to_coffee_cart(self): self.open("https://seleniumbase.io/help_docs/customizing_test_runs/") self.js_click('nav a:contains("Coffee Cart")') self.assert_title("Coffee Cart") self.assert_element('h4:contains("Espresso")') def test_buy_one_cappuccino(self): self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.click('div[data-test="Cappuccino"]') self.assert_exact_text("cart (1)", 'a[aria-label="Cart page"]') self.click('a[aria-label="Cart page"]') self.assert_exact_text("Total: $19.00", 'button[data-test="checkout"]') self.click('button[data-test="checkout"]') self.type("input#name", "Selenium Coffee") self.type("input#email", "test@test.test") self.click("button#submit-payment") self.assert_text("Thanks for your purchase.", "div#app div.success") self.assert_exact_text("cart (0)", 'a[aria-label="Cart page"]') self.assert_exact_text("Total: $0.00", 'button[data-test="checkout"]') @parameterized.expand([[False], [True]]) def test_coffee_promo_with_preview(self, accept_promo): self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.click('div[data-test="Espresso"]') self.click('div[data-test="Americano"]') self.click('div[data-test="Cafe_Latte"]') self.assert_exact_text("cart (3)", 'a[aria-label="Cart page"]') promo = False total_string = "Total: $33.00" if self.is_element_visible("div.promo"): self.assert_text("Get an extra cup of Mocha for $4.", "div.promo") if accept_promo: self.click("div.promo button.yes") self.assert_exact_text("cart (4)", 'a[aria-label="Cart page"]') promo = True total_string = "Total: $37.00" else: self.click("div.promo button.no") checkout_button = 'button[data-test="checkout"]' if promo and not self.browser == "safari": self.hover(checkout_button) if not self.is_element_visible("ul.cart-preview"): self.highlight(checkout_button) self.post_message("STOP moving the mouse!
Hover blocked!") self.hover(checkout_button) self.assert_text("(Discounted) Mocha", "ul.cart-preview") self.assert_exact_text(total_string, checkout_button) self.click(checkout_button) self.type("input#name", "Selenium Coffee") self.type("input#email", "test@test.test") self.click("button#submit-payment") self.assert_text("Thanks for your purchase.", "div#app div.success") def test_context_click_add_coffee(self): self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.context_click('div[data-test="Espresso_Macchiato"]') self.click('form button:contains("Yes")') self.assert_exact_text("cart (1)", 'a[aria-label="Cart page"]') self.click('a[aria-label="Cart page"]') self.assert_exact_text("Total: $12.00", 'button[data-test="checkout"]') self.click('button[data-test="checkout"]') self.type("input#name", "Selenium Coffee") self.type("input#email", "test@test.test") self.click("button#submit-payment") self.assert_text("Thanks for your purchase.", "div#app div.success") def test_remove_added_coffee(self): self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.assert_exact_text("cart (0)", 'a[aria-label="Cart page"]') self.assert_exact_text("Total: $0.00", "button.pay") self.wait_for_element('div[class="cup-body"]') self.click_visible_elements('div[class="cup-body"]', limit=6) self.assert_exact_text("cart (6)", 'a[aria-label="Cart page"]') self.assert_exact_text("Total: $74.00", 'button[data-test="checkout"]') self.click('a[aria-label="Cart page"]') self.click_visible_elements("button.delete") self.assert_text("No coffee, go add some.", "div#app") self.click('a[aria-label="Menu page"]') self.assert_exact_text("cart (0)", 'a[aria-label="Cart page"]') self.assert_exact_text("Total: $0.00", 'button[data-test="checkout"]') ================================================ FILE: examples/custom_settings.py ================================================ """ To override default settings stored in seleniumbase/config/settings.py, change the values here and add "--settings=custom_settings.py" when running. """ # Default timeout values for waiting for page elements to appear. MINI_TIMEOUT = 2 SMALL_TIMEOUT = 7 LARGE_TIMEOUT = 10 EXTREME_TIMEOUT = 30 # Default page load timeout. # In global Selenium settings, this value is set to 300 seconds by default. # When the timeout is exceeded: "Timed out receiving message from renderer" PAGE_LOAD_TIMEOUT = 120 # Default page load strategy. # ["normal", "eager", "none"] # Selenium default = "normal" PAGE_LOAD_STRATEGY = "normal" # If True, existing logs from past test runs will be saved and take up space. # If False, only the logs from the most recent test run will be saved locally. # You can also archive existing logs on the command line with: "--archive_logs" ARCHIVE_EXISTING_LOGS = False # If True, existing downloads from past runs will be saved and take up space. # If False, only the downloads from the most recent run will be saved locally. ARCHIVE_EXISTING_DOWNLOADS = False # If True, the last page screenshot will include the and background. # If False, the last page screenshot will only include the section. # Depending on the screen size, including a background could make the # appear very small in the screenshot, which may require manually zooming in # on the to see page details if you decide to include the background. SCREENSHOT_WITH_BACKGROUND = False # If True, switch to new tabs automatically if a click opens a new one. # (Only happens if the initial tab is still on same URL as before.) SWITCH_TO_NEW_TABS_ON_CLICK = True """ These methods add JS waits, such as self.wait_for_ready_state_complete(), which waits for document.readyState to be "complete" after Selenium actions. """ # Called after self.open(URL), NOT driver.get(URL) WAIT_FOR_RSC_ON_PAGE_LOADS = True # Called after self.click(selector), NOT element.click() WAIT_FOR_RSC_ON_CLICKS = False # Wait for AngularJS calls to complete after various browser actions. WAIT_FOR_ANGULARJS = True # Skip ALL calls to wait_for_ready_state_complete() and wait_for_angularjs(). SKIP_JS_WAITS = False # Changing the default behavior of Demo Mode. Activate with: --demo_mode DEFAULT_DEMO_MODE_TIMEOUT = 0.5 HIGHLIGHTS = 4 DEFAULT_MESSAGE_DURATION = 2.55 # Disabling the Content Security Policy of the browser by default. DISABLE_CSP_ON_FIREFOX = True DISABLE_CSP_ON_CHROME = False # If True and --proxy=IP_ADDRESS:PORT is invalid, then error immediately. RAISE_INVALID_PROXY_STRING_EXCEPTION = True # Default browser coordinates when opening new windows for tests. WINDOW_START_X = 20 WINDOW_START_Y = 54 # Default browser resolutions when opening new windows for tests. # (Headless resolutions take priority, and include all browsers.) # (Firefox starts maximized by default when running in GUI Mode.) CHROME_START_WIDTH = 1250 CHROME_START_HEIGHT = 840 HEADLESS_START_WIDTH = 1440 HEADLESS_START_HEIGHT = 1880 # If True, hides messages related to downloading drivers. # If False, you'll see details about downloading drivers. # (This only affects driver downloads coming from tests.) # (Eg. Using "sbase get chromedriver" won't hide output.) HIDE_DRIVER_DOWNLOADS = False # Changing the default behavior of MasterQA Mode. MASTERQA_DEFAULT_VALIDATION_MESSAGE = "Does the page look good?" MASTERQA_WAIT_TIME_BEFORE_VERIFY = 0.5 MASTERQA_START_IN_FULL_SCREEN_MODE = False MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT = 600 # Google Authenticator # (For 2-factor authentication using a time-based one-time password algorithm) # (See https://github.com/pyotp/pyotp and https://pypi.org/project/pyotp/ ) # (Also works with Authy and other compatible apps.) # Usage: "self.get_google_auth_password()" (output based on timestamp) # Usage with override: "self.get_google_auth_password(totp_key=TOTP_KEY)" TOTP_KEY = "base32secretABCD" # MySQL DB Credentials # (For saving data from tests to a MySQL DB) # Usage: "--with-db_reporting" DB_HOST = "127.0.0.1" DB_PORT = 3306 DB_USERNAME = "root" DB_PASSWORD = "test" DB_SCHEMA = "test_db" # Amazon S3 Bucket Credentials # (For saving screenshots and other log files from tests) # (Bucket names are unique across all existing bucket names in Amazon S3) # Usage: "--with-s3_logging" S3_LOG_BUCKET = "[S3 BUCKET NAME]" S3_BUCKET_URL = "https://s3.amazonaws.com/[S3 BUCKET NAME]/" S3_SELENIUM_ACCESS_KEY = "[S3 ACCESS KEY]" S3_SELENIUM_SECRET_KEY = "[S3 SECRET KEY]" # Encryption Settings # (Used for string/password obfuscation) # (You should reset the Encryption Key for every clone of SeleniumBase) ENCRYPTION_KEY = "Pg^.l!8UdJ+Y7dMIe&fl*%!p9@ej]/#tL~3E4%6?" # These tokens are added to the beginning and end of obfuscated passwords. # Helps identify which strings/passwords have been obfuscated. OBFUSCATION_START_TOKEN = "$^*ENCRYPT=" OBFUSCATION_END_TOKEN = "?&#$" ================================================ FILE: examples/desktop_apps/ReadMe.md ================================================

SeleniumBase

Desktop Apps

* **Recorder** (Run using `python recorder.py` or `sbase recorder`) ================================================ FILE: examples/desktop_apps/recorder.py ================================================ """ Run this file using ``python recorder.py`` """ import os def open_recorder_desktop_app(): command = "sbase recorder" os.system(command) if __name__ == "__main__": open_recorder_desktop_app() ================================================ FILE: examples/dialog_boxes/ReadMe.md ================================================

Dialog Boxes 🛂

SeleniumBase Dialog Boxes let your users provide input in the middle of automation scripts. * This feature utilizes the [jquery-confirm](https://craftpip.github.io/jquery-confirm/) library. * A Python API is used to call the JavaScript API. SeleniumBase

↕️ (Example: dialog_box_tour.py) ↕️

SeleniumBase

Here's how to run that example:

```zsh cd examples/dialog_boxes pytest test_dialog_boxes.py ```

Here's a code snippet from that:

```python self.open("https://xkcd.com/1920/") skip_button = ["SKIP", "red"] # Can be a [text, color] list or tuple. buttons = ["Fencing", "Football", "Metaball", "Go/Chess", skip_button] message = "Choose a sport:" choice = self.get_jqc_button_input(message, buttons) if choice == "Fencing": self.open("https://xkcd.com/1424/") ``` * You can create forms that include buttons and input fields.

Here's a simple form with only buttons as input:

```python choice = self.get_jqc_button_input("Ready?", ["YES", "NO"]) print(choice) # This prints "YES" or "NO" # You may want to customize the color of buttons: buttons = [("YES", "green"), ("NO", "red")] choice = self.get_jqc_button_input("Ready?", buttons) ```

Here's a simple form with an input field:

```python text = self.get_jqc_text_input("Enter text:", ["Search"]) print(text) # This prints the text entered ```

This form has an input field and buttons:

```python message = "Type your name and choose a language:" buttons = ["Python", "JavaScript"] text, choice = self.get_jqc_form_inputs(message, buttons) print("Your name is: %s" % text) print("You picked %s!" % choice) ```

You can customize options if you want:

```python # Themes: bootstrap, modern, material, supervan, light, dark, seamless options = [("theme", "modern"), ("width", "50%")] self.get_jqc_text_input("You Won!", ["OK"], options) ```

Default options can be set with set_jqc_theme():

```python self.set_jqc_theme("light", color="green", width="38%") # To reset jqc theme settings to factory defaults: self.reset_jqc_theme() ```

All methods for Dialog Boxes:

```python self.get_jqc_button_input(message, buttons, options=None) self.get_jqc_text_input(message, button=None, options=None) self.get_jqc_form_inputs(message, buttons, options=None) self.set_jqc_theme(theme, color=None, width=None) self.reset_jqc_theme() self.activate_jquery_confirm() # Automatic for jqc methods ```

Detailed method summaries for Dialog Boxes:

```python self.get_jqc_button_input(message, buttons, options=None) """ Pop up a jquery-confirm box and return the text of the button clicked. If running in headless mode, the last button text is returned. @Params message: The message to display in the jquery-confirm dialog. buttons: A list of tuples for text and color. Example: [("Yes!", "green"), ("No!", "red")] Available colors: blue, green, red, orange, purple, default, dark. A simple text string also works: "My Button". (Uses default color.) options: A list of tuples for options to set. Example: [("theme", "bootstrap"), ("width", "450px")] Available theme options: bootstrap, modern, material, supervan, light, dark, and seamless. Available colors: (For the BORDER color, NOT the button color.) "blue", "default", "green", "red", "purple", "orange", "dark". Example option for changing the border color: ("color", "default") Width can be set using percent or pixels. Eg: "36.0%", "450px". """ self.get_jqc_text_input(message, button=None, options=None) """ Pop up a jquery-confirm box and return the text submitted by the input. If running in headless mode, the text returned is "" by default. @Params message: The message to display in the jquery-confirm dialog. button: A 2-item list or tuple for text and color. Or just the text. Example: ["Submit", "blue"] -> (default button if not specified) Available colors: blue, green, red, orange, purple, default, dark. A simple text string also works: "My Button". (Uses default color.) options: A list of tuples for options to set. Example: [("theme", "bootstrap"), ("width", "450px")] Available theme options: bootstrap, modern, material, supervan, light, dark, and seamless. Available colors: (For the BORDER color, NOT the button color.) "blue", "default", "green", "red", "purple", "orange", "dark". Example option for changing the border color: ("color", "default") Width can be set using percent or pixels. Eg: "36.0%", "450px". """ self.get_jqc_form_inputs(message, buttons, options=None) """ Pop up a jquery-confirm box and return the input/button texts as tuple. If running in headless mode, returns the ("", buttons[-1][0]) tuple. @Params message: The message to display in the jquery-confirm dialog. buttons: A list of tuples for text and color. Example: [("Yes!", "green"), ("No!", "red")] Available colors: blue, green, red, orange, purple, default, dark. A simple text string also works: "My Button". (Uses default color.) options: A list of tuples for options to set. Example: [("theme", "bootstrap"), ("width", "450px")] Available theme options: bootstrap, modern, material, supervan, light, dark, and seamless. Available colors: (For the BORDER color, NOT the button color.) "blue", "default", "green", "red", "purple", "orange", "dark". Example option for changing the border color: ("color", "default") Width can be set using percent or pixels. Eg: "36.0%", "450px". """ self.set_jqc_theme(theme, color=None, width=None) """ Sets the default jquery-confirm theme and width (optional). Available themes: "bootstrap", "modern", "material", "supervan", "light", "dark", and "seamless". Available colors: (This sets the BORDER color, NOT the button color.) "blue", "default", "green", "red", "purple", "orange", "dark". Width can be set using percent or pixels. Eg: "36.0%", "450px". """ self.reset_jqc_theme() """ Resets the jqc theme settings to factory defaults. """ self.activate_jquery_confirm() # Automatic for jqc methods """ See https://craftpip.github.io/jquery-confirm/ for usage. """ ``` --------

✅ 🛂 Automated/Manual Hybrid Mode (MasterQA)

MasterQA uses SeleniumBase Dialog Boxes to speed up manual testing by having automation perform all the browser actions while the manual tester handles validation. See the MasterQA GitHub page for examples.

================================================ FILE: examples/dialog_boxes/dialog_box_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DialogBoxTests(BaseCase): def test_dialog_boxes(self): self.open("https://xkcd.com/1920/") self.assert_element('img[alt="Emoji Sports"]') self.highlight("#comic img") skip_button = ["SKIP", "red"] # Can be a [text, color] list or tuple. buttons = ["Fencing", "Football", "Metaball", "Go/Chess", skip_button] message = "Choose a sport:" choice = None while choice != "STOP": choice = self.get_jqc_button_input(message, buttons) if choice == "Fencing": self.open("https://xkcd.com/1424/") buttons.remove("Fencing") elif choice == "Football": self.open("https://xkcd.com/1107/") buttons.remove("Football") elif choice == "Metaball": self.open("https://xkcd.com/1507/") buttons.remove("Metaball") elif choice == "Go/Chess": self.open("https://xkcd.com/1287/") buttons.remove("Go/Chess") else: break self.highlight("#comic img") if len(buttons) == 2: message = "One Sport Remaining:" if len(buttons) == 1: message = "Part One Complete. You saw all 4 sports!" btn_text_1 = "NEXT Tutorial Please!" btn_text_2 = "WAIT, Go/Chess is a sport?" buttons = [(btn_text_1, "green"), (btn_text_2, "purple")] choice_2 = self.get_jqc_button_input(message, buttons) if choice_2 == btn_text_2: self.open_if_not_url("https://xkcd.com/1287/") message = "Brain sports count as sports!

" message += "Are you ready for more?" self.get_jqc_button_input(message, ["Let's Go!"]) break self.open("https://xkcd.com/1117/") sb_banner_logo = "//seleniumbase.io/cdn/img/sb_logo_10.png" self.set_attributes("#news img", "src", sb_banner_logo) options = [("theme", "material"), ("width", "52%")] message = 'With one button, you can press "Enter/Return", "Y", or "1".' self.get_jqc_button_input(message, ["OK"], options) self.open("https://xkcd.com/556/") self.set_attributes("#news img", "src", sb_banner_logo) options = [("theme", "bootstrap"), ("width", "52%")] message = 'If the lowercase button text is "yes" or "no", ' message += '

you can use the "Y" or "N" keys as shortcuts. ' message += "


Other shortcuts include:

" message += '"1": 1st button, "2": 2nd button, etc. Got it?' buttons = [("YES", "green"), ("NO", "red")] choice = self.get_jqc_button_input(message, buttons, options) message = 'You said "%s"!

' % choice if choice == "YES": message += "Wonderful! Let's continue with the next example..." else: message += "You can learn more from SeleniumBase Docs..." choice = self.get_jqc_button_input(message, ["OK"], options) self.open("https://seleniumbase.io") self.set_jqc_theme("light", color="green", width="38%") message = "This is the SeleniumBase Docs website!

" message += "What would you like to search for?
" text = self.get_jqc_text_input(message, ["Search"]) self.type('input[aria-label="Search"]', text + "\n") self.wait_for_ready_state_complete() self.set_jqc_theme("bootstrap", color="red", width="32%") if self.is_text_visible("No matching documents", ".md-search-result"): self.get_jqc_button_input("Your search had no results!", ["OK"]) elif self.is_text_visible("Type to start searching", "div.md-search"): self.get_jqc_button_input("You did not do a search!", ["OK"]) else: self.click_if_visible("a.md-search-result__link") self.set_jqc_theme("bootstrap", color="green", width="32%") self.get_jqc_button_input("You found search results!", ["OK"]) self.open("https://seleniumbase.io/help_docs/ReadMe/") self.highlight("h1") self.slow_scroll_to('article p a[href*="/examples/ReadMe/"]') zoom_in = 'article p a[href*="/examples/ReadMe/"]{zoom: 1.8;}' self.add_css_style(zoom_in) self.highlight_click('article p a[href*="/examples/ReadMe/"]') self.highlight("h1") self.set_jqc_theme("bootstrap", color="green", width="52%") message = 'See the "SeleniumBase/examples" section for more info!' self.get_jqc_button_input(message, ["OK"]) self.set_jqc_theme("bootstrap", color="purple", width="56%") message = "Now let's combine form inputs with multiple button options!" message += "

" message += "Pick something to search. Then pick the site to search on." buttons = ["SeleniumBase.io", "Wikipedia.org"] text, choice = self.get_jqc_form_inputs(message, buttons) if choice == "SeleniumBase.io": self.open("https://seleniumbase.io/") self.highlight_type('input[aria-label="Search"]', text + "\n") else: self.open("https://en.wikipedia.org/wiki/Special:Search") self.highlight_type('input[id*="search"]', text) self.sleep(1) self.click("#searchform button") self.wait_for_ready_state_complete() self.sleep(1) self.highlight("body") self.reset_jqc_theme() self.get_jqc_button_input("Here are your results.", ["OK"]) message = "

You've reached the end of this tutorial!


" message += "Now you know about SeleniumBase Dialog Boxes!
" message += "
Check out SeleniumBase on GitHub for more!" self.set_jqc_theme("modern", color="purple", width="56%") self.get_jqc_button_input(message, ["Goodbye!"]) ================================================ FILE: examples/edge_test.py ================================================ """This test is only for Microsoft Edge (Chromium)!""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--edge") class EdgeTests(BaseCase): def test_edge(self): if self.browser != "edge": self.open_if_not_url("about:blank") print("\n This test is only for Microsoft Edge (Chromium)!") print(' (Run this test using "--edge" or "--browser=edge")') self.skip('Use "--edge" or "--browser=edge"') elif self.headless: self.open_if_not_url("about:blank") print("\n This test is NOT designed for Headless Mode!") self.skip('Do NOT use "--headless" with this test!') self.open("edge://settings/help") self.assert_element("app-shell") self.assert_text("Microsoft Edge", "app-shell") self.sleep(2) ================================================ FILE: examples/example_config.cfg ================================================ [nosetests] nocapture=1 logging-level=INFO browser=chrome ================================================ FILE: examples/example_logs/ReadMe.md ================================================ ## [](https://github.com/seleniumbase/SeleniumBase/) Logs, The Dashboard, and Reports:

(The Dashboard Tutorial on YouTube)

🔵 During test failures, logs and screenshots from the most recent test run will get saved to the ``latest_logs/`` folder. If ``--archive-logs`` is specified (or if ARCHIVE_EXISTING_LOGS is set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py)), test logs will also get archived to the ``archived_logs/`` folder. Otherwise, the log files will be cleaned out when the next test run begins (by default). ```zsh pytest test_fail.py ``` (Log files in [SeleniumBase/examples/example_logs](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/example_logs) were generated when [test_fail.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_fail.py) ran and failed.) Examples of expected log files generated during failures: In addition to log files, you can also generate dashboards and test reports. --------

The SeleniumBase Dashboard:

🔵 The ``--dashboard`` option for pytest generates a SeleniumBase Dashboard located at ``dashboard.html``, which updates automatically as tests run and produce results. Example: ```zsh pytest --dashboard --rs --headless ``` The SeleniumBase Dashboard 🔵 Additionally, you can host your own SeleniumBase Dashboard Server on a port of your choice. Here's an example of that using Python 3's ``http.server``: ```zsh python -m http.server 1948 ``` 🔵 Now you can navigate to ``http://localhost:1948/dashboard.html`` in order to view the dashboard as a web app. This requires two different terminal windows: one for running the server, and another for running the tests, which should be run from the same directory. (Use Ctrl+C to stop the http server.) 🔵 Here's a full example of what the SeleniumBase Dashboard may look like: ```zsh pytest test_suite.py test_image_saving.py --dashboard --rs --headless ``` The SeleniumBase Dashboard --------

Pytest Reports:

🔵 Using ``--html=report.html`` gives you a fancy report of the name specified after your test suite completes. ```zsh pytest test_suite.py --html=report.html ``` Example Pytest Report 🔵 When combining pytest html reports with SeleniumBase Dashboard usage, the pie chart from the Dashboard will get added to the html report. Additionally, if you set the html report URL to be the same as the Dashboard URL when also using the dashboard, (example: ``--dashboard --html=dashboard.html``), then the Dashboard will become an advanced html report when all the tests complete. 🔵 Here's an example of an upgraded html report: ```zsh pytest test_suite.py --dashboard --html=report.html ``` Dashboard Pytest HTML Report -------- If viewing ``pytest-html`` reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356/7058266) for the HTML to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/security/configuring-content-security-policy/). That setting can be changed from ``Manage Jenkins`` > ``Script Console`` by running: ```js System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "") ``` -------- You can also use ``--junit-xml=report.xml`` to get an xml report instead. Jenkins can use this file to display better reporting for your tests. ```zsh pytest test_suite.py --junit-xml=report.xml ``` --------

pynose Test Reports:

The ``pynose`` ``--report`` option gives you a fancy report after your tests complete. ```zsh pynose test_suite.py --report ``` Example pynose Test Report (NOTE: You can add ``--show-report`` to immediately display pynose reports after the test suite completes. Only use ``--show-report`` when running tests locally because it pauses the test run.) --------

🐝⚪ Behave Dashboard & Reports:

(The [behave_bdd/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/behave_bdd) folder can be found in the [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder.) ```zsh behave behave_bdd/features/ -D dashboard -D headless ``` You can also use ``--junit`` to get ``.xml`` reports for each Behave feature. Jenkins can use these files to display better reporting for your tests. ```zsh behave behave_bdd/features/ --junit -D rs -D headless ``` --------
SeleniumBase
================================================ FILE: examples/example_logs/basic_test_info.txt ================================================ test_fail.py::FailingTests::test_find_army_of_robots_on_xkcd_desert_island -------------------------------------------------------------------- Last Page: https://xkcd.com/731/ Duration: 1.63s Browser: Chrome 108.0.5359.124 Driver: chromedriver 108.0.5359.71 Timestamp: 1672785363 (Unix Timestamp) Date: Tuesday, January 3, 2023 Time: 5:36:03 PM (EDT, UTC-05:00) -------------------------------------------------------------------- Traceback: File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/case.py", line 57, in testPartExecutor yield File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/case.py", line 623, in run self._callTestMethod(testMethod) File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/unittest/case.py", line 579, in _callTestMethod if method() is not None: ^^^^^^^^ File "/Users/michael/github/SeleniumBase/examples/test_fail.py", line 16, in test_find_army_of_robots_on_xkcd_desert_island self.assert_element("div#ARMY_OF_ROBOTS", timeout=1) File "/Users/michael/github/SeleniumBase/seleniumbase/fixtures/base_case.py", line 8279, in assert_element self.wait_for_element_visible(selector, by=by, timeout=timeout) File "/Users/michael/github/SeleniumBase/seleniumbase/fixtures/base_case.py", line 7718, in wait_for_element_visible return page_actions.wait_for_element_visible( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/michael/github/SeleniumBase/seleniumbase/fixtures/page_actions.py", line 428, in wait_for_element_visible timeout_exception(NoSuchElementException, message) File "/Users/michael/github/SeleniumBase/seleniumbase/fixtures/page_actions.py", line 191, in timeout_exception raise exc(msg) Exception: Message: Element {div#ARMY_OF_ROBOTS} was not present after 1 second! ================================================ FILE: examples/example_logs/page_source.html ================================================ xkcd: Desert Island
xkcd.com logo A webcomic of romance,
sarcasm, math, and language.
xkcd updates every Monday, Wednesday, and Friday.
Desert Island
Desert Island

Permanent link to this comic: https://xkcd.com/731/
Image URL (for hotlinking/embedding): https://imgs.xkcd.com/comics/desert_island.png
Selected Comics Grownups Circuit Diagram Angular Momentum Self-Description Alternative Energy Revolution


xkcd.com is best viewed with Netscape Navigator 4.0 or below on a Pentium 3±1 emulated in Javascript on an Apple IIGS
at a screen resolution of 1024x1. Please enable your ad blockers, disable high-heat drying, and remove your device
from Airplane Mode and set it to Boat Mode. For security reasons, please leave caps lock on while browsing.

This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License.

This means you're free to copy and share these comics (but not to sell them). More details.

================================================ FILE: examples/github_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class GitHubTests(BaseCase): def test_github(self): if self.headless or self.page_load_strategy == "none": self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") self.open("https://github.com/seleniumbase/SeleniumBase") self.click_if_visible('[data-action="click:signup-prompt#dismiss"]') self.highlight("div.Layout-main") self.highlight("div.Layout-sidebar") self.assert_element("div.repository-content") self.assert_text("SeleniumBase", "strong a") self.js_click('a[title="seleniumbase"]') self.slow_click('td[class*="large"] a[title="fixtures"]') self.highlight('td[class*="large"] a[title="base_case.py"]', loops=8) ================================================ FILE: examples/gui_test_runner.py ================================================ """GUI TEST RUNNER Run by Typing: "python gui_test_runner.py" (Use Python 3)""" import subprocess from tkinter import Tk, Frame, Button, Label class App: def __init__(self, master): frame = Frame(master) frame.pack() self.label = Label(root, width=40).pack() self.title = Label(frame, text="", fg="black").pack() self.title1 = Label( frame, text=("Run a Test in Chrome (default):"), fg="blue", ).pack() self.run1 = Button( frame, command=self.run_1, text=("pytest my_first_test.py"), fg="green", ).pack() self.title2 = Label( frame, text=("Run a Test in Firefox:"), fg="blue", ).pack() self.run2 = Button( frame, command=self.run_2, text=("pytest my_first_test.py --firefox"), fg="green", ).pack() self.title3 = Label( frame, text="Run a Test with Demo Mode:", fg="blue", ).pack() self.run3 = Button( frame, command=self.run_3, text=("pytest my_first_test.py --demo_mode"), fg="green", ).pack() self.title4 = Label( frame, text="Run a Parameterized Test and reuse session:", fg="blue", ).pack() self.run4 = Button( frame, command=self.run_4, text=("pytest parameterized_test.py --rs"), fg="green", ).pack() self.title5 = Label( frame, text="Run a Failing Test with a Test Report:", fg="blue", ).pack() self.run5 = Button( frame, command=self.run_5, text=("pytest test_fail.py --html=report.html"), fg="red", ).pack() self.title6 = Label( frame, text="Run a Failing Test Suite with the Dashboard:", fg="blue", ).pack() self.run6 = Button( frame, command=self.run_6, text=("pytest test_suite.py --rs --dashboard"), fg="red", ).pack() self.title7 = Label( frame, text="Run a Failing Test with Deferred Asserts:", fg="blue", ).pack() self.run7 = Button( frame, command=self.run_7, text=("pytest test_deferred_asserts.py"), fg="red", ).pack() self.end_title = Label(frame, text="", fg="black").pack() self.quit = Button(frame, text="QUIT", command=frame.quit).pack() def run_1(self): subprocess.Popen("pytest my_first_test.py", shell=True) def run_2(self): subprocess.Popen("pytest my_first_test.py --firefox", shell=True) def run_3(self): subprocess.Popen("pytest my_first_test.py --demo_mode", shell=True) def run_4(self): subprocess.Popen("pytest parameterized_test.py --rs", shell=True) def run_5(self): subprocess.Popen("pytest test_fail.py --html=report.html", shell=True) def run_6(self): subprocess.Popen("pytest test_suite.py --rs --dashboard", shell=True) def run_7(self): subprocess.Popen("pytest test_deferred_asserts.py", shell=True) if __name__ == "__main__": root = Tk() root.title("Select Test Job To Run") root.minsize(320, 420) app = App(root) root.mainloop() ================================================ FILE: examples/hack_the_planet.py ================================================ """ Video Link: https://youtu.be/1s-Tj65AKZA """ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class HackTests(BaseCase): def test_all_your_base_are_belong_to_us(self): self.set_window_size(1250, 740) ayb = "ALL YOUR BASE" abtu = "ARE BELONG TO US" aybabtu = "%s %s" % (ayb, abtu) sb_banner_logo = "//seleniumbase.github.io/cdn/img/sb_logo_10.png" sb_dashboard_logo = "//seleniumbase.github.io/img/dash_pie_3.png" wiki = "https://en.wikipedia.org/wiki/All_your_base_are_belong_to_us" self.open(wiki) self.click_if_visible('button[aria-label="Close"]') self.set_text_content("h1#firstHeading", aybabtu) self.set_text_content("#ca-history a", aybabtu) self.set_text_content("#n-mainpage-description a", "ALL") self.set_text_content("#n-contents a", "YOUR") self.set_text_content("#n-currentevents a", "BASE") self.set_text_content("#n-randompage a", "ARE") self.set_text_content("#n-aboutsite a", "BELONG") self.set_text_content("#n-contactpage a", "TO US") self.highlight("h1#firstHeading", loops=5, scroll=False) zoom_in = "#ca-history a{zoom: 1.8;-moz-transform: scale(1.8);}" self.add_css_style(zoom_in) self.highlight("#ca-history a", loops=5, scroll=False) zoom_in = "img[src*=Ayb]{zoom: 1.6;-moz-transform: scale(1.6);}" self.add_css_style(zoom_in) self.highlight("img[src*=Ayb]", loops=10, scroll=False) if not self.headless: self.open("https://www.apple.com/store") self.set_text_content("div.rs-shop-subheader", aybabtu) self.set_text_content('#shelf-1 a[href*="mac"]', "ALL") self.set_text_content('#shelf-1 a[href*="iphone"]', "YOUR") self.set_text_content('#shelf-1 a[href*="ipad"]', "BASE") self.remove_element('#shelf-1 [role="listitem"]:nth-child(5)') self.set_text_content('#shelf-1 a[href*="watch"]', "ARE") self.set_text_content('#shelf-1 a[href*="airpods"]', "BELONG") self.set_text_content('#shelf-1 a[href*="airtag"]', "TO") self.set_text_content('#shelf-1 a[href*="tv"]', "US") self.set_text_content('#shelf-1 a[href*="homepod"]', ".") self.set_text_content("#shelf-2_section h2", ayb + ". ") self.set_text_content("#shelf-2_section span", abtu + ". ") self.highlight("div.rs-shop-subheader", loops=6, scroll=False) self.highlight("#shelf-1", loops=2, scroll=False) self.highlight('#shelf-1 a[href*="mac"]', loops=1, scroll=False) self.highlight('#shelf-1 a[href*="iphone"]', loops=1, scroll=False) self.highlight('#shelf-1 a[href*="ipad"]', loops=3, scroll=False) self.highlight('#shelf-1 a[href*="watch"]', loops=1, scroll=False) self.highlight('#shelf-1 a[href*="airpod"]', loops=1, scroll=False) self.highlight('#shelf-1 a[href*="airtag"]', loops=1, scroll=False) self.highlight('#shelf-1 a[href*="tv"]', loops=3, scroll=False) self.highlight("#shelf-2_section h2", loops=5, scroll=False) self.highlight("#shelf-2_section span", loops=6, scroll=False) self.open("https://google.com/ncr") self.hide_elements("iframe") if self.is_element_visible('a[href*="about.google"]'): self.set_text_content('a[href*="about.google"]', ayb) if self.is_element_visible('a[href*="store.google"]'): self.set_text_content('a[href*="store.google"]', abtu) self.set_text_content('a[href*="mail.google.com"]', ayb) self.set_text_content('a[href*="google.com/img"]', abtu) self.set_attributes('[value="Google Search"]', "value", ayb) self.set_attributes('[value="I\'m Feeling Lucky"]', "value", abtu) self.hide_elements("iframe") zoom_in = "a{zoom: 1.2;-moz-transform: scale(1.2);}" self.add_css_style(zoom_in) zoom_in = ( '[value="ALL YOUR BASE"]{zoom: 1.3;-moz-transform: scale(1.3);}' '[value="ARE BELONG TO US"]{zoom: 1.3;-moz-transform: scale(1.3);}' ) self.add_css_style(zoom_in) self.hide_elements("iframe") if self.is_element_visible('a[href*="about.google"]'): self.highlight('a[href*="about.google"]', loops=3) if self.is_element_visible('a[href*="store.google"]'): self.highlight('a[href*="store.google"]', loops=3) self.highlight('a[href*="mail.google.com"]', loops=3) self.highlight('a[href*="google.com/img"]', loops=3) self.highlight('form[role="search"]', loops=8) self.open("https://github.com/features/actions") self.set_text_content("#hero-section-brand-heading", aybabtu) self.highlight("#hero-section-brand-heading", loops=14, scroll=False) self.open("https://dev.to/top/infinity") self.click_if_visible('button[aria-label="Close campaign banner"]') self.click_if_visible('svg[aria-label="Close campaign banner"]') self.click_if_visible('button[id*="sponsorship-close-trigger"]') if self.is_element_visible('main div:contains("Pinned")'): self.hide_elements('main div:contains("Pinned")') if self.is_element_visible('[data-type-of="in_house"]'): self.hide_elements('[data-type-of="in_house"]') self.set_text_content('nav a[data-text="Relevant"]', "ALL") self.set_text_content('nav a[data-text="Latest"]', "YOUR") self.set_text_content('nav a[data-text="Top"]', "BASE") self.set_text_content('nav a[data-text="Week"]', "ARE") self.set_text_content('nav a[data-text="Month"]', "BELONG") self.set_text_content('nav a[data-text="Year"]', "TO") self.set_text_content('nav a[data-text="Infinity"]', "US") self.click_if_visible('button[id*="sponsorship-close-trigger"]') self.set_text_content('aside a[class*="tful"]', aybabtu) self.set_text_content('aside a[aria-label="Create new account"]', ayb) self.set_text_content('aside a[aria-label="Log in"]', abtu) self.set_text_content('aside a[class*="tful"]:nth-child(2)', aybabtu) self.set_text_content('aside a[class*="tful"]:nth-child(3)', aybabtu) self.set_text_content('aside a[class*="tful"]:nth-child(4)', aybabtu) self.set_text_content('aside a[class*="tful"]:nth-child(5)', aybabtu) self.set_attribute("a.crayons-avatar img", "src", sb_dashboard_logo) self.set_text_content(".profile-preview-card button", "SeleniumBase") if self.is_element_visible('h2 a[href*="simonh"]'): self.set_text_content('h2 a[href*="simonh"]', aybabtu) if self.is_element_visible('main h2 a[id*="article"]'): self.set_text_content('main h2 a[id*="article"]', aybabtu) self.highlight('[aria-label="Primary sidebar"] div div', scroll=False) self.highlight('nav a[data-text="Relevant"]', loops=1, scroll=False) self.highlight('nav a[data-text="Latest"]', loops=1, scroll=False) self.highlight('nav a[data-text="Top"]', loops=2, scroll=False) self.highlight('nav a[data-text="Week"]', loops=1, scroll=False) self.highlight('nav a[data-text="Month"]', loops=1, scroll=False) self.highlight('nav a[data-text="Year"]', loops=1, scroll=False) self.highlight('nav a[data-text="Infinity"]', loops=3, scroll=False) if self.is_element_visible('main h2 a[id*="article"]'): self.highlight('main h2 a[id*="article"]', loops=7, scroll=False) self.highlight("section.crayons-card", loops=7, scroll=False) self.open("https://store.steampowered.com/") self.set_text_content('a[href*="steamcommunity.com/"]', " ") self.set_text_content('div.content a[href*="/about/"]', " ") self.set_text_content('div.content a[href*="help.steam"]', aybabtu) zoom_in = '[href*="help.steam"]{zoom: 1.5;-moz-transform: scale(1.5);}' self.add_css_style(zoom_in) self.highlight('div.content a[href*="help.steam"]', loops=12) self.open("https://xkcd.com/286/") self.set_text_content('a[href="/archive"]', "ALL") self.set_text_content('a[href*="what-if"]', "YOUR BASE") self.set_text_content('a[href*="/about"]', abtu) self.remove_element('li:contains("Feed")') self.remove_element('li:contains("TW")') self.remove_element('li:contains("Books")') self.remove_element('li:contains("What")') self.remove_element('li:contains("WI")') self.set_attributes("#countdown img", "src", sb_banner_logo) self.set_text_content("#ctitle", aybabtu) self.set_text_content('a[rel="prev"]', "All") self.set_text_content('a[href*="random"]', "Your") self.set_text_content('a[rel="next"]', "Base") self.highlight("#topLeft ul", loops=5, scroll=False) self.highlight('a[href="/archive"]', loops=1, scroll=False) self.highlight('a[href*="what-if"]', loops=3, scroll=False) self.highlight('a[href*="/about"]', loops=5, scroll=False) self.highlight('a[rel="prev"]', loops=1, scroll=False) self.highlight('a[href*="random"]', loops=1, scroll=False) self.highlight('a[rel="next"]', loops=3, scroll=False) self.highlight("#ctitle", loops=7, scroll=False) self.open("https://www.nintendo.com/whatsnew/") self.set_text_content("main section h1", aybabtu) self.highlight("main section h1", loops=10, scroll=False) if not self.headless: self.open("https://support.gog.com/hc/en-us?product=gog") self.set_text_content("div.intro-title", aybabtu) self.set_text_content("h4", aybabtu) self.highlight("div.intro-title", loops=8, scroll=False) self.highlight("h4", loops=8, scroll=False) self.open("https://slack.com/help/articles/204714258-Giphy-for-Slack") self.set_text_content("h1", aybabtu) self.set_text_content('a[prettyslug="getting-started"]', "ALL") self.set_text_content('a[prettyslug="using-slack"]', "YOUR") self.set_text_content('a[prettyslug="your-profile"]', "BASE") self.set_text_content('a[prettyslug="connect-tools"]', "ARE") self.set_text_content('a[prettyslug="administration"]', "BELONG") self.set_text_content('a[prettyslug*="tutorials"]', "TO US") self.set_text_content("h1.article_title", aybabtu) self.highlight("h1", loops=4, scroll=False) self.highlight("div#global_menu", loops=2, scroll=False) self.highlight('a[prettyslug*="g-started"]', loops=1, scroll=False) self.highlight('a[prettyslug="using-slack"]', loops=1, scroll=False) self.highlight('a[prettyslug="your-profile"]', loops=2, scroll=False) self.highlight('a[prettyslug="connect-tools"]', loops=1, scroll=False) self.highlight('a[prettyslug="administration"]', loops=1, scroll=False) self.highlight('a[prettyslug*="tutorials"]', loops=2, scroll=False) self.highlight("h1.article_title", loops=5, scroll=False) self.open("https://kubernetes.io/") self.set_text_content('nav a[href="/docs/"]', "ALL") self.set_text_content('nav a[href="/blog/"]', "YOUR") self.set_text_content('nav a[href="/training/"]', "BASE") self.set_text_content('nav a[href="/careers/"]', "ARE") self.set_text_content('nav a[href="/partners/"]', "BELONG") self.set_text_content('nav a[href="/community/"]', "TO") self.set_text_content("nav #navbarDropdown", "US") self.set_text_content("nav #navbarDropdownMenuLink", ".") if self.is_element_visible("h1"): self.set_text_content("h1", aybabtu) self.highlight("nav ul.navbar-nav", loops=3, scroll=False) self.highlight('nav a[href="/docs/"]', loops=1, scroll=False) self.highlight('nav a[href="/blog/"]', loops=1, scroll=False) self.highlight('nav a[href="/training/"]', loops=2, scroll=False) self.highlight('nav a[href="/careers/"]', loops=1, scroll=False) self.highlight('nav a[href="/partners/"]', loops=1, scroll=False) self.highlight('nav a[href="/community/"]', loops=1, scroll=False) self.highlight("nav #navbarDropdown", loops=2, scroll=False) if self.is_element_visible("h1"): self.highlight("h1", loops=6, scroll=False) self.open("https://www.selenium.dev/") if self.is_element_visible('button[data-dismiss="alert"] span'): self.js_click('button[data-dismiss="alert"] span', scroll=False) self.set_attributes("a.dropdown-toggle", "class", "nav-link") self.set_text_content('li a:contains("About")', "ALL") self.set_text_content('li a:contains("Downloads")', "YOUR") self.set_text_content('li a:contains("Documentation")', "BASE") self.set_text_content('li a:contains("Projects")', "ARE") self.set_text_content('li a:contains("Support")', "BELONG") self.set_text_content('li a:contains("Blog")', "TO") self.set_text_content('li a:contains("English")', "US") self.set_text_content("div.mx-auto p", aybabtu) self.set_text_content("h2", aybabtu) if self.is_element_visible('button[data-dismiss="alert"] span'): self.js_click('button[data-dismiss="alert"] span', scroll=False) zoom_in = "div.mx-auto p{zoom: 1.1;-moz-transform: scale(1.1);}" self.add_css_style(zoom_in) self.highlight("div#main_navbar", loops=1, scroll=False) self.highlight('li a:contains("ALL")', loops=1, scroll=False) self.highlight('li a:contains("YOUR")', loops=1, scroll=False) self.highlight('li a:contains("BASE")', loops=2, scroll=False) self.highlight('li a:contains("ARE")', loops=1, scroll=False) self.highlight('li a:contains("BELONG")', loops=1, scroll=False) self.highlight('li a:contains("TO")', loops=1, scroll=False) self.highlight('li a:contains("US")', loops=2, scroll=False) self.highlight("div.mx-auto p", loops=6, scroll=False) self.highlight("h2", loops=8, scroll=False) self.open("https://www.python.org/") self.set_text_content('a[class="donate-button"]', ayb) self.set_text_content("#about a", "ALL") self.set_text_content("#downloads a", "YOUR") self.set_text_content("#documentation a", "BASE") self.set_text_content("#community a", "ARE") self.set_text_content("#success-stories a", "BELONG") self.set_text_content("#news a", "TO") self.set_text_content("#events a", "US") self.highlight('a[class="donate-button"]', loops=4, scroll=False) self.highlight("nav#mainnav", loops=5, scroll=False) self.highlight("#about a", loops=1, scroll=False) self.highlight("#downloads a", loops=1, scroll=False) self.highlight("#documentation a", loops=2, scroll=False) self.highlight("#community a", loops=1, scroll=False) self.highlight("#success-stories a", loops=1, scroll=False) self.highlight("#news a", loops=1, scroll=False) self.highlight("#events a", loops=2, scroll=False) self.open("https://docs.pytest.org/") self.set_text_content("h1", "pytest: " + aybabtu) self.highlight("h1", loops=10, scroll=False) self.open("https://wordpress.com/") zoom_out = "h1{zoom: 0.8;-moz-transform: scale(0.8);}" self.add_css_style(zoom_out) zoom_in = "a.wp-element-button{zoom: 1.4;-moz-transform: scale(1.4);}" self.add_css_style(zoom_in) self.set_text_content("h1", aybabtu) self.set_text_content("a.wp-element-button", "Use SeleniumBase!") self.highlight("h1", loops=8, scroll=False) self.highlight("a.wp-element-button", loops=8, scroll=False) self.open("https://seleniumbase.com/") self.set_text_content("h1", aybabtu) self.highlight("h1", loops=10, scroll=False) self.open("https://pypi.org/") self.set_text_content('a[href="/sponsors/"]', aybabtu) self.set_text_content("h1", aybabtu) self.set_value("input#search", aybabtu, scroll=False) self.highlight('a[href="/sponsors/"]', loops=6, scroll=False) self.highlight("h1", loops=6, scroll=False) self.highlight("input#search", loops=8, scroll=False) self.open("https://status.iboss.com/ibcloud/app/cloudStatus.html") self.wait_for_element_clickable('div[translate*="cloudStatus"]') self.set_text_content('div[translate*="cloudStatus"]', ayb) self.set_text_content('div[translate*="maintenance"]', "ARE") self.set_text_content('div[translate*="advisory"]', "BELONG") self.set_text_content('div[translate*="incident"]', "TO US") self.set_text_content("h1", "Cloud Status - " + aybabtu) self.highlight("nav div.ibcloud-header-contents", loops=3) self.highlight('div[translate*="cloudStatus"]', loops=4) self.highlight('div[translate*="maintenance"]', loops=1) self.highlight('div[translate*="advisory"]', loops=1) self.highlight('div[translate*="incident"]', loops=3) self.highlight("h1", loops=9, scroll=False) self.open("https://git-scm.com/") self.set_text_content("span#tagline", aybabtu) self.highlight("span#tagline", loops=10, scroll=False) self.open("https://pragprog.com/") self.set_text_content("header p", aybabtu) zoom_in = "header p{zoom: 1.35;-moz-transform: scale(1.35);}" self.add_css_style(zoom_in) self.highlight("header p", loops=10, scroll=False) self.open("https://seleniumbase.io/") self.set_text_content("h1", aybabtu) self.highlight("h1", loops=10, scroll=False) ================================================ FILE: examples/handle_alert_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class HandleAlertTests(BaseCase): def test_alerts(self): self.open("about:blank") self.execute_script('window.alert("ALERT!!!");') self.sleep(1) # Not needed (Lets you see the alert pop up) self.assert_true(self.is_alert_present()) self.accept_alert() self.sleep(1) # Not needed (Lets you see the alert go away) self.execute_script('window.prompt("My Prompt","defaultText");') self.sleep(1) # Not needed (Lets you see the alert pop up) alert = self.switch_to_alert() self.assert_equal(alert.text, "My Prompt") # Not input field self.dismiss_alert() self.sleep(1) # Not needed (Lets you see the alert go away) self.assert_false(self.is_alert_present()) if self.browser == "safari" and self._reuse_session: # Alerts can freeze Safari if reusing the browser session self.driver.quit() ================================================ FILE: examples/iframe_tests.py ================================================ """Use SeleniumBase methods to interact with iframes.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class FrameTests(BaseCase): def test_iframe_basics(self): self.open("https://seleniumbase.io/w3schools/iframes.html") self.assert_title("iframe Testing") self.click("button#runbtn") self.switch_to_frame("iframeResult") # Enter the iframe self.assert_text("HTML Iframes", "h2") self.switch_to_frame('[title*="Iframe"]') # Enter iframe inside iframe self.assert_text("This page is displayed in an iframe", "h1") self.switch_to_parent_frame() # Exit only the inner iframe self.assert_text("Use CSS width & height to specify", "p") self.switch_to_frame('[title*="Iframe"]') # Enter iframe inside iframe self.assert_text("seleniumbase.io/w3schools/iframes", "a") self.switch_to_default_content() # Exit all iframes self.click("button#runbtn") self.switch_to_frame("iframeResult") # Go back inside 1st iframe self.highlight('iframe[title="Iframe Example"]') def test_iframes_with_context_manager(self): self.open("https://seleniumbase.io/w3schools/iframes.html") self.assert_title("iframe Testing") self.click("button#runbtn") with self.frame_switch("iframeResult"): self.assert_text("HTML Iframes", "h2") with self.frame_switch('[title*="Iframe"]'): self.assert_text("This page is displayed in an iframe", "h1") self.assert_text("Use CSS width & height to specify", "p") with self.frame_switch('[title*="Iframe"]'): self.assert_text("seleniumbase.io/w3schools/iframes", "a") self.click("button#runbtn") with self.frame_switch("iframeResult"): self.highlight('iframe[title="Iframe Example"]') def test_set_content_to_frame(self): self.open("https://seleniumbase.io/w3schools/iframes.html") self.assert_title("iframe Testing") self.click("button#runbtn") self.set_content_to_frame("iframeResult") self.highlight('iframe[title="Iframe Example"]') self.set_content_to_frame("iframe") self.assert_element_not_visible("iframe") self.highlight("body") self.set_content_to_parent() self.highlight('iframe[title="Iframe Example"]') self.set_content_to_default() self.click("button#runbtn") self.highlight("#iframeResult") ================================================ FILE: examples/locale_code_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class LocaleCodeTests(BaseCase): def test_locale_code(self): self.open("about:blank") locale_code = self.get_locale_code() # navigator.language print("\nYour Browser's Locale Code: %s" % locale_code) if self.browser == "chrome" and not self.headless: self.open("chrome://settings/languages") language_info = self.get_text( "settings-ui::shadow " "settings-main::shadow " "settings-languages-page-index::shadow " "settings-languages-page::shadow " "settings-section " "#languagesSection div.start div" ) print("Language info (chrome://settings/languages):") print(language_info) self.sleep(1) ================================================ FILE: examples/master_qa/ReadMe.md ================================================ ![](https://seleniumbase.github.io/cdn/img/masterqa_logo.png "MasterQA")

MasterQA combines automation with manual verification steps.

![](https://seleniumbase.github.io/cdn/gif/masterqa6.gif "MasterQA") Here's code from [basic_masterqa_test_0.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/basic_masterqa_test_0.py): ```python from seleniumbase import MasterQA class MasterQATests(MasterQA): def test_masterqa(self): self.open("https://xkcd.com/1700/") self.verify("Do you see a webcomic?") self.open("https://seleniumbase.io/demo_page") self.highlight('table') self.verify("Do you see elements in a table?") self.open("https://seleniumbase.io/devices/") self.highlight("div.mockup-wrapper") self.verify("Do you see 4 computer devices?") ``` After each automation checkpoint, a pop-up window will ask the user questions for each verification command. When the test run completes, as seen from [this longer example](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/masterqa_test_1.py), you'll reach the results page that appears after answering all the verification questions. (Failed verifications generate links to screenshots and log files.) ![](https://seleniumbase.github.io/cdn/img/mqa_hybrid.png "MasterQA") You may have noticed the ``Incomplete Test Runs`` row on the results page. If the value for that is not zero, it means that one of the automated steps failed. This could happen if you tell your script to perform an action on an element that doesn't exist. Now that we're mixing automation with manual QA, it's good to tell apart the failures from each. The results_table CSV file contains a spreadsheet with the details of each failure (if any) for both manual and automated steps. **How to run the example tests from scratch:** ```zsh git clone https://github.com/seleniumbase/SeleniumBase.git cd SeleniumBase pip install . cd examples/master_qa pytest basic_masterqa_test_0.py pytest masterqa_test_1.py ``` At the end of your test run, you'll receive a report with results, screenshots, and log files. Close the Results Page window when you're done. **Check out [masterqa_test_1.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/masterqa_test_1.py) to learn how to write your own MasterQA tests:** You'll notice that tests are written the same way as regular [SeleniumBase](https://seleniumbase.com) tests, with the key difference being a different import: ``from seleniumbase import MasterQA`` rather than ``from seleniumbase import BaseCase``. Now your Python test class will import ``MasterQA`` instead of ``BaseCase``. To add a manual verification step, use ``self.verify()`` in the code after each part of your test that needs a manual verification step. If you want to include a custom question, add text inside that call (in quotes). Example: ```python self.verify() self.verify("Can you find the moon?") ``` -------- MasterQA is powered by [SeleniumBase](https://seleniumbase.com), the most advanced open-source automation framework on the [Planet](https://en.wikipedia.org/wiki/Earth). ================================================ FILE: examples/master_qa/__init__.py ================================================ ================================================ FILE: examples/master_qa/basic_masterqa_test_0.py ================================================ from seleniumbase import MasterQA MasterQA.main(__name__, __file__) class MasterQATests(MasterQA): def test_masterqa(self): self.open("https://seleniumbase.io/devices/") self.highlight("div.mockup-wrapper") self.verify("Do you see 4 computer devices?") self.open("https://seleniumbase.io/demo_page") self.highlight("table") self.verify("Do you see elements in a table?") self.open("https://xkcd.com/1700/") self.highlight("#comic") self.verify("Do you see a webcomic?") ================================================ FILE: examples/master_qa/masterqa_test_1.py ================================================ from seleniumbase import MasterQA MasterQA.main(__name__, __file__) class MasterQATests(MasterQA): def test_xkcd(self): self.open("https://xkcd.com/1512/") for i in range(4): self.click('a[rel="next"]') for i in range(3): self.click('a[rel="prev"]') self.verify() self.open("https://xkcd.com/1520/") for i in range(2): self.click('a[rel="next"]') self.verify("Can you find the moon?") self.click('a[rel="next"]') self.verify("Do the drones look safe?") self.open("https://seleniumbase.io/devices/") self.type("input#urlInput", "seleniumbase.io/error_page\n") self.verify("Do you see Octocat in a Jedi knight robe?") self.open("https://xkcd.com/213/") for i in range(5): self.click('a[rel="prev"]') self.verify("Does the page say 'Abnormal Expressions'?") ================================================ FILE: examples/master_qa/pytest.ini ================================================ [pytest] # Display console output. Disable cacheprovider: addopts = --capture=tee-sys -p no:cacheprovider # Skip these directories during test collection: norecursedirs = .* build dist recordings temp assets # Ignore DeprecationWarning, PytestUnknownMarkWarning filterwarnings = ignore::pytest.PytestWarning ignore:.*U.*mode is deprecated:DeprecationWarning # Configure the junit_family option explicitly: junit_family = legacy # Set pytest discovery rules: # (Most of the rules here are similar to the default rules.) # (Inheriting unittest.TestCase could override these rules.) python_files = test_*.py *_test.py *_tests.py *_suite.py *_test_*.py python_classes = Test* *Test* *Test *Tests *Suite python_functions = test_* # Common pytest markers used in examples: # (pytest may require marker registration to prevent warnings.) # (Future versions may turn those marker warnings into errors.) markers = marker1: custom marker marker2: custom marker marker3: custom marker marker_test_suite: custom marker expected_failure: custom marker local: custom marker remote: custom marker offline: custom marker develop: custom marker qa: custom marker ci: custom marker e2e: custom marker ready: custom marker smoke: custom marker deploy: custom marker active: custom marker master: custom marker release: custom marker staging: custom marker production: custom marker ================================================ FILE: examples/migration/__init__.py ================================================ ================================================ FILE: examples/migration/protractor/ReadMe.md ================================================ ## Support for migrating from Protractor to SeleniumBase 🔵 The Protractor/Angular tests from [github.com/angular/protractor/tree/master/example](https://github.com/angular/protractor/tree/master/example) have been migrated to SeleniumBase and placed in this directory. 🔵 Protractor tests that end in `.spec.js` will now end in `_test.py` for the conversion to SeleniumBase/Python format with pytest auto-discovery. ✅ Here's a test run with `pytest` using `--reuse-session` mode and Chromium `--guest` mode: ```zsh $ pytest --rs -v --guest =========================== test session starts ============================ platform darwin -- Python 3.11.9, pytest-8.3.3, pluggy-1.5.0 -- /Users/michael/.virtualenvs/sbase11/bin/python metadata: {'Python': '3.11.9', 'Platform': 'macOS-13.2.1-arm64-arm-64bit', 'Packages': {'pytest': '8.3.3', 'pluggy': '1.5.0'}, 'Plugins': {'cov': '6.0.0', 'html': '2.0.1', 'metadata': '3.1.1', 'seleniumbase': '4.33.2', 'ordering': '0.6', 'rerunfailures': '15.0', 'xdist': '3.6.1'}} rootdir: /Users/michael/github/SeleniumBase/examples configfile: pytest.ini plugins: html-2.0.1, metadata-3.1.1, seleniumbase-4.33.2, ordering-0.6, rerunfailures-15.0, xdist-3.6.1 collected 4 items example_test.py::AngularJSHomePageTests::test_greet_user PASSED example_test.py::AngularJSHomePageTests::test_todo_list PASSED input_test.py::AngularMaterialInputTests::test_invalid_input PASSED mat_paginator_test.py::AngularMaterialPaginatorTests::test_pagination PASSED ============================ 4 passed in 4.24s ============================= ``` ================================================ FILE: examples/migration/protractor/__init__.py ================================================ ================================================ FILE: examples/migration/protractor/example_spec.js ================================================ describe('angularjs homepage', function() { it('should greet the named user', function() { browser.get('http://www.angularjs.org'); element(by.model('yourName')).sendKeys('Julie'); var greeting = element(by.binding('yourName')); expect(greeting.getText()).toEqual('Hello Julie!'); }); describe('todo list', function() { var todoList; beforeEach(function() { browser.get('http://www.angularjs.org'); todoList = element.all(by.repeater('todo in todoList.todos')); }); it('should list todos', function() { expect(todoList.count()).toEqual(2); expect(todoList.get(1).getText()).toEqual('build an AngularJS app'); }); it('should add a todo', function() { var addTodo = element(by.model('todoList.todoText')); var addButton = element(by.css('[value="add"]')); addTodo.sendKeys('write a protractor test'); addButton.click(); expect(todoList.count()).toEqual(3); expect(todoList.get(2).getText()).toEqual('write a protractor test'); }); }); }); ================================================ FILE: examples/migration/protractor/example_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class AngularJSHomePageTests(BaseCase): def test_greet_user(self): self.open("http://www.angularjs.org") self.type('[ng-model="yourName"]', "Julie") self.assert_exact_text("Hello Julie!", "h1.ng-binding") def test_todo_list(self): self.open("http://www.angularjs.org") todo_selector = '[ng-repeat="todo in todoList.todos"]' # Verify that the todos are listed self.wait_for_element(todo_selector) todos = self.find_visible_elements(todo_selector) self.assert_equal(len(todos), 2) self.assert_equal(todos[1].text.strip(), "build an AngularJS app") # Verify adding a new todo self.type('[ng-model="todoList.todoText"]', "write a protractor test") self.click('[value="add"]') todos = self.find_visible_elements(todo_selector) self.assert_equal(len(todos), 3) self.assert_equal(todos[2].text.strip(), "write a protractor test") ================================================ FILE: examples/migration/protractor/input_spec.js ================================================ describe('angular-material input component page', function() { const EC = protractor.ExpectedConditions; it('Should change input component value', async() => { await browser.get('https://material.angular.io/components/input/examples'); await browser.wait(EC.elementToBeClickable($('.mat-button-wrapper > .mat-icon')), 5000); const emailInputField = $$('#mat-input-1').get(1); await emailInputField.sendKeys('invalid'); expect($('mat-error').isPresent()).toBe(true); }); }); ================================================ FILE: examples/migration/protractor/input_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class AngularMaterialInputTests(BaseCase): def test_invalid_input(self): # Test that there's an error for an invalid input self.open("https://material.angular.io/components/input/examples") self.type('input[type="email"]', "invalid") self.assert_element("mat-error") ================================================ FILE: examples/migration/protractor/mat_paginator_spec.js ================================================ describe('angular-material paginator component page', () => { const EC = protractor.ExpectedConditions; beforeAll(async() => { await browser.get('https://material.angular.io/components/paginator/examples'); await browser.wait(EC.elementToBeClickable($('.mat-button-wrapper > .mat-icon')), 5000); }); it('Should navigate to next page', async() => { await $('button[aria-label=\'Next page\']').click(); await expect($('.mat-paginator-range-label').getAttribute('innerText')).toEqual('Page 2 of 10'); }); it('Should navigate to previous page', async() => { await $('button[aria-label=\'Previous page\']').click(); await expect($('.mat-paginator-range-label').getAttribute('innerText')).toEqual('Page 1 of 10'); }); it('Should change list length to 5 items per page', async() => { await $('mat-select > div').click(); const fiveItemsOption = $$('mat-option > .mat-option-text').first(); await fiveItemsOption.click(); await expect($('.mat-paginator-range-label').getAttribute('innerText')).toEqual('Page 1 of 20'); }); }); ================================================ FILE: examples/migration/protractor/mat_paginator_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class AngularMaterialPaginatorTests(BaseCase): def test_pagination(self): self.open("https://material.angular.io/components/paginator/examples") self.click_if_visible("button.mat-mdc-button") self.scroll_to("div.mat-mdc-paginator-page-size") # Set pagination to 5 items per page self.click("mat-select > div") self.click("mat-option:nth-of-type(1)") # Verify navigation to the next page self.click('button[aria-label="Next page"]') self.assert_exact_text( "6 – 10 of 50", ".mat-mdc-paginator-range-label" ) # Verify navigation to the previous page self.click('button[aria-label="Previous page"]') self.assert_exact_text( "1 – 5 of 50", ".mat-mdc-paginator-range-label" ) # Set pagination to 10 items per page self.click("mat-select > div") self.click("mat-option:nth-of-type(2)") # Verify page with correct number of pages self.assert_exact_text( "1 – 10 of 50", ".mat-mdc-paginator-range-label" ) ================================================ FILE: examples/migration/raw_selenium/ReadMe.md ================================================ ## ✅ Support for migrating from raw Selenium to SeleniumBase ### 🔵 Here are some examples that can help you understand how to migrate from raw Selenium to SeleniumBase The five main examples in the [examples/migration/raw_selenium](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium) folder are: * [flaky_messy_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/flaky_messy_raw.py) * [long_messy_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/long_messy_raw.py) * [messy_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/messy_raw.py) * [refined_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/refined_raw.py) * [simple_sbase.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/simple_sbase.py) Each of these examples is structured as a test that can be run with `pytest`. They all inherit `unittest.TestCase` either directly, or via `seleniumbase.BaseCase`, which extends it. This provides automatically-called `setUp()` and `tearDown()` methods before and after each test. > These examples show the evolution of tests from raw Selenium to SeleniumBase. By understanding common progressions of Selenium engineers, you can avoid making the same mistakes as they did, and learn to write good tests efficiently without the long learning curve. * [flaky_messy_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/flaky_messy_raw.py) This is common example of how newcomers to Selenium write tests (assuming they've already learned how to break out reusuable code into `setUp()` and `tearDown()` methods). It uses `find_element()` calls, which can lead to flaky tests because those calls fail if a page element is slow to load. * [long_messy_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/long_messy_raw.py) At the next stage of learning, newcomers to Selenium realize that their tests are flaky, so they start replacing existing `find_element()` calls with `WebDriverWait` and internal Selenium `expected_conditions` methods, such as `visibility_of_element_located` and `element_to_be_clickable`. This can result in long/messy tests that are unmaintainable if not written carefully. * [messy_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/messy_raw.py) By this stage, newcomers to Selenium have evolved into legitimate test automation engineers. They have become better at writing reusable code, so they've broken down the long `WebDriverWait` and `expected_conditions` calls into shorter method calls, which are easier to read, but could still be improved on for better maintainability. Here, individual page actions are still written out as multiple lines of code, (or multiple method calls per line), which isn't efficient. * [refined_raw.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/refined_raw.py) By now, the test automation engineer has become an expert in breaking out code into reusable methods, and the test itself has been simplified down to a single page action per line. The code is easy to read and easy to maintain. The error output is also simplified. The journey of writing a complete test automation framework for Selenium has begun. * [simple_sbase.py](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/migration/raw_selenium/simple_sbase.py) With a complete test automation framework built, most of the hard work is already done for you. By importing `BaseCase` into your test classes, your tests gain access to all SeleniumBase methods, which can simplify your code. SeleniumBase also provides a lot of additional functionality that isn't included with raw Selenium. ### 🔵 How is SeleniumBase different from raw Selenium?

💡 SeleniumBase is a Python framework for browser automation and testing. SeleniumBase uses Selenium/WebDriver APIs and incorporates test-runners such as pytest, pynose, and behave to provide organized structure, test discovery, test execution, test state (eg. passed, failed, or skipped), and command-line options for changing default settings (eg. browser selection). With raw Selenium, you would need to set up your own options-parser for configuring tests from the command-line.

💡 SeleniumBase's driver manager gives you more control over automatic driver downloads. (Use --driver-version=VER with your pytest run command to specify the version.) By default, SeleniumBase will download a driver version that matches your major browser version if not set.

💡 SeleniumBase automatically detects between CSS Selectors and XPath, which means you don't need to specify the type of selector in your commands (but optionally you could).

💡 SeleniumBase methods often perform multiple actions in a single method call. For example, self.type(selector, text) does the following:
1. Waits for the element to be visible.
2. Waits for the element to be interactive.
3. Clears the text field.
4. Types in the new text.
5. Presses Enter/Submit if the text ends in "\n".
With raw Selenium, those actions require multiple method calls.

💡 SeleniumBase uses default timeout values when not set:
self.click("button")
With raw Selenium, methods would fail instantly (by default) if an element needed more time to load:
self.driver.find_element(by="css selector", value="button").click()
(Reliable code is better than unreliable code.)

💡 SeleniumBase lets you change the explicit timeout values of methods:
self.click("button", timeout=10)
With raw Selenium, that requires more code:
WebDriverWait(driver, 10).until(EC.element_to_be_clickable("css selector", "button")).click()
(Simple code is better than complex code.)

💡 SeleniumBase gives you clean error output when a test fails. With raw Selenium, error messages can get very messy.

💡 SeleniumBase gives you the option to generate a dashboard and reports for tests. It also saves screenshots from failing tests to the ./latest_logs/ folder. Raw Selenium does not have these options out-of-the-box.

💡 SeleniumBase includes desktop GUI apps for running tests, such as SeleniumBase Commander for pytest and SeleniumBase Behave GUI for behave.

💡 SeleniumBase has its own Recorder / Test Generator for creating tests from manual browser actions.

💡 SeleniumBase comes with test case management software, ("Case Plans"), for organizing tests and step descriptions.

💡 SeleniumBase includes tools for building data apps, ("Chart Maker"), which can generate JavaScript from Python.

-------- [](https://github.com/seleniumbase/SeleniumBase) ================================================ FILE: examples/migration/raw_selenium/__init__.py ================================================ ================================================ FILE: examples/migration/raw_selenium/flaky_messy_raw.py ================================================ """Flaky Raw Selenium Example - (ONLY Selenium / NO SeleniumBase)""" import sys from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from unittest import TestCase class FlakyMessyRawSelenium(TestCase): def setUp(self): self.driver = None options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") if "linux" in sys.platform: options.add_argument("--headless=new") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) service = Service(service_args=["--disable-build-check"]) self.driver = webdriver.Chrome(options=options, service=service) def tearDown(self): if self.driver: try: if self.driver.service.process: self.driver.quit() except Exception: pass def is_element_visible(self, selector, by="css selector"): try: element = self.driver.find_element(by, selector) if element.is_displayed(): return True except Exception: pass return False def test_add_item_to_cart(self): self.driver.get("https://www.saucedemo.com") by_css = By.CSS_SELECTOR # "css selector" element = self.driver.find_element(by_css, "#user-name") element.clear() element.send_keys("standard_user") element = self.driver.find_element(by_css, "#password") element.clear() element.send_keys("secret_sauce") element.submit() self.driver.find_element(by_css, "div.inventory_list") element = self.driver.find_element(by_css, "span.title") self.assertEqual(element.text, "Products") self.driver.find_element(by_css, 'button[name*="backpack"]').click() self.driver.find_element(by_css, "#shopping_cart_container a").click() element = self.driver.find_element(by_css, "span.title") self.assertEqual(element.text, "Your Cart") element = self.driver.find_element(by_css, "div.cart_item") self.assertIn("Backpack", element.text) self.driver.find_element(by_css, "#remove-sauce-labs-backpack").click() self.assertFalse(self.is_element_visible("div.cart_item")) self.driver.find_element(by_css, "#react-burger-menu-btn").click() self.driver.find_element(by_css, "a#logout_sidebar_link").click() self.driver.find_element(by_css, "input#login-button") # When run with "python" instead of "pytest" or "python -m unittest" if __name__ == "__main__": from unittest import main main() ================================================ FILE: examples/migration/raw_selenium/long_messy_raw.py ================================================ """Long & Messy Raw Selenium Example - (ONLY Selenium / NO SeleniumBase)""" import sys from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from unittest import TestCase class LongMessyRawSelenium(TestCase): def setUp(self): self.driver = None options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") if "linux" in sys.platform: options.add_argument("--headless=new") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) service = Service(service_args=["--disable-build-check"]) self.driver = webdriver.Chrome(options=options, service=service) def tearDown(self): if self.driver: try: if self.driver.service.process: self.driver.quit() except Exception: pass def test_add_item_to_cart(self): self.driver.get("https://www.saucedemo.com") by_css = By.CSS_SELECTOR # "css selector" element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, "#user-name")) ) element.clear() element.send_keys("standard_user") element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, "#password")) ) element.clear() element.send_keys("secret_sauce") element.submit() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((by_css, "div.inventory_list")) ) element = WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((by_css, "span.title")) ) self.assertEqual(element.text, "Products") element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, 'button[name*="backpack"]')) ) element.click() element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, "#shopping_cart_container a")) ) element.click() element = WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((by_css, "span.title")) ) self.assertEqual(element.text, "Your Cart") element = WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((by_css, "div.cart_item")) ) self.assertIn("Backpack", element.text) element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, "#remove-sauce-labs-backpack")) ) element.click() WebDriverWait(self.driver, 10).until( EC.invisibility_of_element((by_css, "div.cart_item")) ) element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, "#react-burger-menu-btn")) ) element.click() element = WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((by_css, "a#logout_sidebar_link")) ) element.click() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((by_css, "input#login-button")) ) # When run with "python" instead of "pytest" or "python -m unittest" if __name__ == "__main__": from unittest import main main() ================================================ FILE: examples/migration/raw_selenium/messy_raw.py ================================================ """Messy Raw Selenium Example - (ONLY Selenium / NO SeleniumBase)""" import sys from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from unittest import TestCase class MessyRawSelenium(TestCase): def setUp(self): self.driver = None options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") if "linux" in sys.platform: options.add_argument("--headless=new") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) service = Service(service_args=["--disable-build-check"]) self.driver = webdriver.Chrome(options=options, service=service) def tearDown(self): if self.driver: try: if self.driver.service.process: self.driver.quit() except Exception: pass def wait_for_element_visible( self, selector, by="css selector", timeout=10 ): return WebDriverWait(self.driver, timeout).until( EC.visibility_of_element_located((by, selector)) ) def wait_for_element_clickable( self, selector, by="css selector", timeout=10 ): return WebDriverWait(self.driver, timeout).until( EC.element_to_be_clickable((by, selector)) ) def wait_for_element_not_visible( self, selector, by="css selector", timeout=10 ): return WebDriverWait(self.driver, timeout).until( EC.invisibility_of_element((by, selector)) ) def test_add_item_to_cart(self): self.driver.get("https://www.saucedemo.com") element = self.wait_for_element_clickable("#user-name") element.clear() element.send_keys("standard_user") element = self.wait_for_element_clickable("#password") element.clear() element.send_keys("secret_sauce") element.submit() self.wait_for_element_visible("div.inventory_list") element = self.wait_for_element_visible("span.title") self.assertEqual(element.text, "Products") self.wait_for_element_clickable('button[name*="backpack"]').click() self.wait_for_element_clickable("#shopping_cart_container a").click() element = self.wait_for_element_visible("span.title") self.assertEqual(element.text, "Your Cart") element = self.wait_for_element_visible("div.cart_item") self.assertIn("Backpack", element.text) self.wait_for_element_clickable("#remove-sauce-labs-backpack").click() self.wait_for_element_not_visible("div.cart_item") self.wait_for_element_clickable("#react-burger-menu-btn").click() self.wait_for_element_clickable("a#logout_sidebar_link").click() self.wait_for_element_visible("input#login-button") # When run with "python" instead of "pytest" or "python -m unittest" if __name__ == "__main__": from unittest import main main() ================================================ FILE: examples/migration/raw_selenium/pytest.ini ================================================ [pytest] # Display console output. Disable cacheprovider: addopts = --capture=tee-sys -p no:cacheprovider # Skip these directories during test collection: norecursedirs = .* build dist recordings temp assets # Ignore DeprecationWarning, PytestUnknownMarkWarning filterwarnings = ignore::pytest.PytestWarning ignore:.*U.*mode is deprecated:DeprecationWarning # Configure the junit_family option explicitly: junit_family = legacy # Set pytest discovery rules: # (Most of the rules here are similar to the default rules.) # (Inheriting unittest.TestCase could override these rules.) python_files = *.py python_classes = Test* *Test* *Test *Tests *Suite python_functions = test_* # Common pytest markers used in examples: # (pytest may require marker registration to prevent warnings.) # (Future versions may turn those marker warnings into errors.) markers = marker1: custom marker marker2: custom marker marker3: custom marker marker_test_suite: custom marker expected_failure: custom marker local: custom marker remote: custom marker offline: custom marker develop: custom marker qa: custom marker ci: custom marker e2e: custom marker ready: custom marker smoke: custom marker deploy: custom marker active: custom marker master: custom marker release: custom marker staging: custom marker production: custom marker ================================================ FILE: examples/migration/raw_selenium/refined_raw.py ================================================ """Refined Raw Selenium Example - (ONLY Selenium / NO SeleniumBase)""" import sys from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from unittest import TestCase class RefinedRawSelenium(TestCase): def setUp(self): self.driver = None options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") if "linux" in sys.platform: options.add_argument("--headless=new") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) service = Service(service_args=["--disable-build-check"]) self.driver = webdriver.Chrome(options=options, service=service) def tearDown(self): if self.driver: try: if self.driver.service.process: self.driver.quit() except Exception: pass def wait_for_element_visible( self, selector, by="css selector", timeout=10 ): try: return WebDriverWait(self.driver, timeout).until( EC.visibility_of_element_located((by, selector)) ) except Exception: raise Exception( "Element {%s} was not visible after %s seconds!" % (selector, timeout) ) def wait_for_element_clickable( self, selector, by="css selector", timeout=10 ): try: return WebDriverWait(self.driver, timeout).until( EC.element_to_be_clickable((by, selector)) ) except Exception: raise Exception( "Element {%s} was not visible/clickable after %s seconds!" % (selector, timeout) ) def wait_for_element_not_visible( self, selector, by="css selector", timeout=10 ): try: return WebDriverWait(self.driver, timeout).until( EC.invisibility_of_element((by, selector)) ) except Exception: raise Exception( "Element {%s} was still visible after %s seconds!" % (selector, timeout) ) def open(self, url): self.driver.get(url) def click(self, selector, by="css selector", timeout=7): el = self.wait_for_element_clickable(selector, by=by, timeout=timeout) el.click() def type(self, selector, text, by="css selector", timeout=10): el = self.wait_for_element_clickable(selector, by=by, timeout=timeout) el.clear() if not text.endswith("\n"): el.send_keys(text) else: el.send_keys(text[:-1]) el.submit() def assert_element(self, selector, by="css selector", timeout=7): self.wait_for_element_visible(selector, by=by, timeout=timeout) def assert_text(self, text, selector="html", by="css selector", timeout=7): el = self.wait_for_element_visible(selector, by=by, timeout=timeout) self.assertIn(text, el.text) def assert_exact_text(self, text, selector, by="css selector", timeout=7): el = self.wait_for_element_visible(selector, by=by, timeout=timeout) self.assertEqual(text, el.text) def assert_element_not_visible( self, selector, by="css selector", timeout=7 ): self.wait_for_element_not_visible(selector, by=by, timeout=timeout) def test_add_item_to_cart(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("#remove-sauce-labs-backpack") self.assert_element_not_visible("div.cart_item") self.click("#react-burger-menu-btn") self.click("a#logout_sidebar_link") self.assert_element("input#login-button") # When run with "python" instead of "pytest" or "python -m unittest" if __name__ == "__main__": from unittest import main main() ================================================ FILE: examples/migration/raw_selenium/simple_sbase.py ================================================ """Clean SeleniumBase Example - (Uses simple, reliable methods)""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CleanSeleniumBase(BaseCase): def test_add_item_to_cart(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("#remove-sauce-labs-backpack") self.assert_element_not_visible("div.cart_item") self.click("#react-burger-menu-btn") self.click("a#logout_sidebar_link") self.assert_element("input#login-button") ================================================ FILE: examples/multiple_cdp_drivers.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MultipleDriversTest(BaseCase): def test_multiple_drivers(self): url1 = "https://seleniumbase.io/demo_page" self.activate_cdp_mode(url1) driver1 = self.driver url2 = "https://seleniumbase.io/coffee/" driver2 = self.get_new_driver(undetectable=True) self.activate_cdp_mode(url2) print("\n" + driver1.get_current_url()) print(driver2.get_current_url()) ================================================ FILE: examples/my_first_test.py ================================================ """A complete end-to-end test for an e-commerce website.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_swag_labs(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Automation") self.type("#postal-code", "77123") self.click("input#continue") self.assert_text("Checkout: Overview") self.assert_text("Backpack", "div.cart_item") self.assert_text("29.99", "div.inventory_item_price") self.click("button#finish") self.assert_exact_text("Thank you for your order!", "h2") self.assert_element('img[alt="Pony Express"]') self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") ####################################################################### # # **** NOTES / USEFUL INFO **** # # 1. By default, page elements are identified by "css selector". # CSS Guide: "https://www.w3schools.com/cssref/css_selectors.asp". # Other selectors include: "link text", "partial link text", "name", # "class name", and "id", but most of those can be expressed as CSS. # # Here's an example of changing the "by": # [ # self.click('Next', by="partial link text") # ] # # XPath is used by default if the arg starts with "/", "./", or "(": # [ # self.click('/html/body/div[3]/div[4]/p[2]/a') # ] # # If you're completely new to CSS selectors, right-click on a # web page and select "Inspect" to see the CSS in the html. # # 2. Most methods have the optional "timeout" argument. # Here's an example of changing the "timeout": # [ # self.assert_element('img[alt="Python"]', timeout=15) # ] # The "timeout" argument tells the method how many seconds to wait # for an element to appear before failing the test. This is # useful if a web page needs additional time to load an element. # If you don't specify a "timeout", a default timeout is used. # Default timeouts are configured in seleniumbase/config/settings.py # # 3. SeleniumBase methods often perform multiple actions. # Example: self.type(SELECTOR, TEXT) does the following: # * Waits for the element to be visible # * Waits for the element to be interactive # * Clears the text field # * Types in the new text # * Presses Enter/Return if the text ends in "\n": element.submit() # # 4. There are duplicate method names that exist for the same method: # (This makes it easier to switch over from other test frameworks.) # Example: # self.open() = self.visit() = self.open_url() = self.goto() # self.type() = self.update_text() = self.input() = self.fill() # self.send_keys() = self.add_text() # self.get_element() = self.wait_for_element_present() # self.find_element() = self.wait_for_element_visible() # = self.wait_for_element() # self.assert_element() = self.assert_element_visible() # self.assert_text() = self.assert_text_visible() # self.find_text() = self.wait_for_text_visible() # = self.wait_for_text() # self.click_link("LinkText") = self.click("link=LinkText") # = self.click_link_text("LinkText") # = self.click('a:contains("LinkText")') # * self.get(url) is SPECIAL: * # If {url} is a valid URL, self.get() works just like self.open() # Otherwise {url} becomes a selector for calling self.get_element() # # 5. There's usually more than one way to do the same thing. # Example 1: # [ # self.assert_text("xkcd: volume 0", "h3") # ] # Is the same as: # [ # text = self.get_text("h3") # self.assert_true("xkcd: volume 0" in text) # ] # Is also the same as: # [ # element = self.find_element("h3") # text = element.text # self.assert_true("xkcd: volume 0" in text) # ] # # Example 2: # [ # self.assert_exact_text("xkcd.com", "h2") # ] # Is the same as: # [ # text = self.get_text("h2").strip() # self.assert_true("xkcd.com".strip() == text) # ] # Is also the same as: # [ # element = self.find_element("h2") # text = element.text.strip() # self.assert_true("xkcd.com".strip() == text) # ] # # Example 3: # [ # title = self.get_attribute("#comic img", "title") # ] # Is the same as: # [ # element = self.find_element("#comic img") # title = element.get_attribute("title") # ] # # 6. self.assert_exact_text(TEXT) ignores leading and trailing # whitespace in the TEXT assertion. # So, self.assert_exact_text("Some Text") accepts [" Some Text "]. # # 7. self.js_click(SELECTOR) can be used to click on hidden elements. # # 8. self.open(URL) will automatically complete URLs missing a prefix. # Example: google.com will become https://google.com before opened. # # 9. For the full method list, see one of the following: # * SeleniumBase/seleniumbase/fixtures/base_case.py # * SeleniumBase/help_docs/method_summary.md # # 10. BaseCase.main(__name__, __file__) enables "python" to run pytest, # which is useful if someone forgets that tests run with "pytest". ================================================ FILE: examples/nth_child_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class NthChildSelectorTests(BaseCase): def test_locate_rows_with_colors(self): self.open("https://xkcd.com/color/rgb/") tbody = "center > table tbody" if self.headed: self.demo_mode = True self.demo_sleep = 0.5 self.message_duration = 2.0 else: self.demo_mode = False self.message_duration = 0.1 self.highlight(tbody) self.post_message("Part 1: Assert text in given row.") self.assert_text("teal", tbody + " tr:nth-child(2)") self.assert_text("aqua", tbody + " tr:nth-child(4)") self.assert_text("mint", tbody + " tr:nth-child(14)") self.assert_text("jade", tbody + " tr:nth-child(36)") soup = self.get_beautiful_soup(self.get_page_source()) self.post_message("Part 2: Find row with given text.") self.locate_first_row_with_color("rust", tbody, soup) self.locate_first_row_with_color("azure", tbody, soup) self.locate_first_row_with_color("topaz", tbody, soup) def locate_first_row_with_color(self, color, tbody, soup): rows = soup.body.table.find_all("tr") num_rows = len(rows) for row in range(num_rows): row_selector = tbody + " tr:nth-child(%s)" % (row + 1) if color in rows[row].text: message = '"%s" found on row %s' % (color, row + 1) self.post_message_and_highlight(message, row_selector) return # Found row and done self.post_error_message('"%s" could not be found on any row!' % color) ================================================ FILE: examples/offline_examples/__init__.py ================================================ ================================================ FILE: examples/offline_examples/demo_page.html ================================================ Web Testing Page

Demo Page

SeleniumBase

Automation Practice

Text Input Field: Textarea:
Pre-Filled Text Field: Button:
Placeholder Text Field: Read-Only Text Field:
HTML SVG with rect:

Paragraph with Text:

This Text is Green

Input Slider Control:
Select Dropdown:
Image in iFrame:
   RadioButton 1: RadioButton 2:
CheckBox: CheckBoxes: Pre-Check Box: CheckBox in iFrame:  
URL Link: seleniumbase.com Link with Text: SeleniumBase on GitHub
SeleniumBase Docs: seleniumbase.io The Demo Page: SeleniumBase Demo Page
================================================ FILE: examples/offline_examples/load_html_test.py ================================================ import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.offline # Can be run with: "pytest -m offline" class OfflineTests(BaseCase): def test_load_html_string(self): html = "

Hello

  

" self.load_html_string(html) # Open "data:text/html," then replace html self.assert_text("Hello", "h2") self.assert_text("OK!", "button") self.type("input", "Goodbye") self.click("button") new_html = '

Checkbox

Check Me!

' self.set_content(new_html) # Same as load_html_string(), but keeps URL self.assert_text("Checkbox", "h3") self.assert_text("Check Me!", "p") self.assert_false(self.is_selected("input")) self.click("input") self.assert_true(self.is_selected("input")) ================================================ FILE: examples/offline_examples/test_demo_page.py ================================================ import os import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.offline # Can be run with: "pytest -m offline" class OfflineTests(BaseCase): def test_demo_page(self): # Load a local html file into the web browser dir_path = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(dir_path, "demo_page.html") self.load_html_file(file_path) # Assert the title of the current web page self.assert_title("Web Testing Page") # Assert that an element is visible on the page self.assert_element("tbody#tbodyId") # Assert that a text substring appears in an element self.assert_text("Demo Page", "h1") # Type text into various text fields and then assert self.type("#myTextInput", "This is Automated") self.type("textarea.area1", "Testing Time!\n") self.type('[name="preText2"]', "Typing Text!") self.assert_text("This is Automated", "#myTextInput") self.assert_text("Testing Time!\n", "textarea.area1") self.assert_text("Typing Text!", '[name="preText2"]') # Hover & click a dropdown element and assert results self.assert_text("Automation Practice", "h3") try: self.hover_and_click("#myDropdown", "#dropOption2", timeout=1) except Exception: # Someone probably moved the mouse while the test ran self.hover_and_js_click("#myDropdown", "#dropOption2") self.assert_text("Link Two Selected", "h3") # Click a button and then verify the expected results self.assert_text("This Text is Green", "#pText") self.click('button:contains("Click Me")') self.assert_text("This Text is Purple", "#pText") # Assert that the given SVG is visible on the page self.assert_element('svg[name="svgName"]') # Verify that a slider control updates a progress bar self.assert_element('progress[value="50"]') self.set_value("input#mySlider", "100") self.assert_element('progress[value="100"]') # Verify that a "select" option updates a meter bar self.assert_element('meter[value="0.25"]') self.select_option_by_text("#mySelect", "Set to 75%") self.assert_element('meter[value="0.75"]') # Assert an element located inside an iframe self.assert_false(self.is_element_visible("img")) self.switch_to_frame("#myFrame1") self.assert_true(self.is_element_visible("img")) self.switch_to_default_content() # Assert text located inside an iframe self.assert_false(self.is_text_visible("iFrame Text")) self.switch_to_frame("#myFrame2") self.assert_true(self.is_text_visible("iFrame Text")) self.switch_to_default_content() # Verify that clicking a radio button selects it self.assert_false(self.is_selected("#radioButton2")) self.click("#radioButton2") self.assert_true(self.is_selected("#radioButton2")) # Verify that clicking a checkbox makes it selected self.assert_element_not_visible("img#logo") self.assert_false(self.is_selected("#checkBox1")) self.click("#checkBox1") self.assert_true(self.is_selected("#checkBox1")) self.assert_element("img#logo") # Verify clicking on multiple elements with one call self.assert_false(self.is_selected("#checkBox2")) self.assert_false(self.is_selected("#checkBox3")) self.assert_false(self.is_selected("#checkBox4")) self.click_visible_elements("input.checkBoxClassB") self.assert_true(self.is_selected("#checkBox2")) self.assert_true(self.is_selected("#checkBox3")) self.assert_true(self.is_selected("#checkBox4")) # Verify that clicking an iframe checkbox selects it self.assert_false(self.is_element_visible(".fBox")) self.switch_to_frame("#myFrame3") self.assert_true(self.is_element_visible(".fBox")) self.assert_false(self.is_selected(".fBox")) self.click(".fBox") self.assert_true(self.is_selected(".fBox")) self.switch_to_default_content() # Verify Drag and Drop self.assert_element_not_visible("div#drop2 img#logo") self.drag_and_drop("img#logo", "div#drop2") self.assert_element("div#drop2 img#logo") # Assert link text - Use click_link() to click self.assert_link_text("seleniumbase.com") self.assert_link_text("SeleniumBase on GitHub") self.assert_link_text("seleniumbase.io") self.assert_link_text("SeleniumBase Demo Page") # Assert exact text self.assert_exact_text("Demo Page", "h1") # Highlight a page element (Also asserts visibility) self.highlight("h2") ================================================ FILE: examples/offline_examples/test_extended_driver.py ================================================ import os import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.offline # Can be run with: "pytest -m offline" class OfflineTests(BaseCase): def test_extended_driver(self): # Load a local html file into the web browser dir_path = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(dir_path, "demo_page.html") self.load_html_file(file_path) # Get the raw driver driver = self.driver # Assert that an element is visible on the page driver.assert_element("tbody#tbodyId") # Assert that a text substring appears in an element driver.assert_text("Demo Page", "h1") # Type text into various text fields and then assert driver.type("#myTextInput", "This is Automated") driver.type("textarea.area1", "Testing Time!\n") driver.type('[name="preText2"]', "Typing Text!") driver.assert_text("This is Automated", "#myTextInput") driver.assert_text("Testing Time!\n", "textarea.area1") driver.assert_text("Typing Text!", '[name="preText2"]') # Hover & click a dropdown element and assert results driver.assert_text("Automation Practice", "h3") driver.js_click("#dropOption2") driver.assert_text("Link Two Selected", "h3") # Click a button and then verify the expected results driver.assert_text("This Text is Green", "#pText") driver.click('button:contains("Click Me")') driver.assert_text("This Text is Purple", "#pText") # Assert that the given SVG is visible on the page driver.assert_element('svg[name="svgName"]') # Assert an element located inside an iframe self.assert_false(driver.is_element_visible("img")) driver.switch_to.frame("myFrame1") self.assert_true(driver.is_element_visible("img")) driver.switch_to.default_content() # Assert text located inside an iframe self.assert_false(driver.is_text_visible("iFrame Text")) driver.switch_to.frame("myFrame2") self.assert_true(driver.is_text_visible("iFrame Text")) driver.switch_to.default_content() # Assert exact text driver.assert_exact_text("Demo Page", "h1") # Highlight a page element (Also asserts visibility) driver.highlight("h2") ================================================ FILE: examples/offline_examples/test_handle_alerts.py ================================================ import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.offline # Can be run with: "pytest -m offline" class OfflineTests(BaseCase): def test_alerts(self): self.open("data:,") self.execute_script('window.alert("ALERT!!!");') self.sleep(1) # Not needed (Lets you see the alert pop up) self.accept_alert() self.sleep(1) # Not needed (Lets you see the alert go away) self.execute_script('window.prompt("My Prompt","defaultText");') self.sleep(1) # Not needed (Lets you see the alert pop up) alert = self.switch_to_alert() self.assert_equal(alert.text, "My Prompt") # Not input field self.dismiss_alert() self.sleep(1) # Not needed (Lets you see the alert go away) if self.browser == "safari" and self._reuse_session: # Alerts can freeze Safari if reusing the browser session self.driver.quit() ================================================ FILE: examples/offline_examples/test_request_fixture.py ================================================ import pytest # Use the pytest "request" fixture to get the "sb" fixture (no class) @pytest.mark.offline def test_request_fixture(request): sb = request.getfixturevalue("sb") sb.open("data:text/html,

Hello

") sb.assert_element("html > body") sb.assert_text("Hello", "body p") sb.type("input", "Goodbye") sb.click("body p") sb.tearDown() # Use the pytest "request" fixture to get the "sb" fixture (in class) @pytest.mark.offline class RequestTests: def test_request_fixture_in_class(self, request): sb = request.getfixturevalue("sb") sb.open("data:text/html,

Hello

") sb.assert_element("html > body") sb.assert_text("Hello", "body p") sb.type("input", "Goodbye") sb.click("body p") sb.tearDown() ================================================ FILE: examples/offline_examples/test_user_agent.py ================================================ import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.offline # Can be run with: "pytest -m offline" class OfflineTests(BaseCase): def test_get_user_agent(self): self.open("data:,") user_agent = self.get_user_agent() print('\nUser Agent = "%s"' % user_agent) # Now change the user-agent using "execute_cdp_cmd()" if not self.is_chromium(): msg = "\n* execute_cdp_cmd() is only for Chromium browsers" print(msg) self.skip(msg) print("\n--------------------------") if not self.headless: self.open("chrome://version/") self.highlight("#useragent", loops=6) self.sleep(0.8) try: self.execute_cdp_cmd( "Network.setUserAgentOverride", { "userAgent": "Mozilla/5.0 " "(Nintendo Switch; WifiWebAuthApplet) " "AppleWebKit/606.4 (KHTML, like Gecko) " "NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393" }, ) new_user_agent = self.get_user_agent() print('\nOverrided User Agent = "%s"' % new_user_agent) finally: # Reset the user-agent back to the original self.execute_cdp_cmd( "Network.setUserAgentOverride", {"userAgent": user_agent}, ) print("\n--------------------------") user_agent = self.get_user_agent() print('\nUser Agent = "%s"' % user_agent) ================================================ FILE: examples/old_wordle_script.py ================================================ """Solve the Wordle game using SeleniumBase. This test runs on archived versions of Wordle, containing Shadow-DOM.""" import ast import random import requests from seleniumbase import version_tuple from seleniumbase import BaseCase if __name__ == "__main__": from pytest import main main([__file__, "--sjw", "--pls=none"]) class WordleTests(BaseCase): word_list = [] def initialize_word_list(self): txt_file = "https://seleniumbase.github.io/cdn/txt/wordle_words.txt" word_string = requests.get(txt_file, timeout=3).text self.word_list = ast.literal_eval(word_string) def modify_word_list(self, word, letter_status): new_word_list = [] correct_letters = [] present_letters = [] for i in range(len(word)): if letter_status[i] == "correct": correct_letters.append(word[i]) for w in self.word_list: if w[i] == word[i]: new_word_list.append(w) self.word_list = new_word_list new_word_list = [] for i in range(len(word)): if letter_status[i] == "present": present_letters.append(word[i]) for w in self.word_list: if word[i] in w and word[i] != w[i]: new_word_list.append(w) self.word_list = new_word_list new_word_list = [] for i in range(len(word)): if letter_status[i] == "absent": if ( word[i] not in correct_letters and word[i] not in present_letters ): for w in self.word_list: if word[i] not in w: new_word_list.append(w) else: for w in self.word_list: if word[i] != w[i]: new_word_list.append(w) self.word_list = new_word_list new_word_list = [] def skip_if_incorrect_env(self): if self.headless: message = "This test doesn't run in headless mode!" print(message) self.skip(message) if not self.is_chromium(): message = "This test requires a Chromium-based browser!" print(message) self.skip(message) if version_tuple < (4, 0, 0): message = "This test requires SeleniumBase 4.0.0 or newer!" print(message) self.skip(message) def test_wordle(self): self.skip_if_incorrect_env() random.seed() year = "2022" month = random.randint(3, 5) day = random.randint(1, 30) date = str(year) + "0" + str(month) + str(day) archive = "https://web.archive.org/web/" url = "https://www.nytimes.com/games/wordle/index.html" past_wordle = archive + date + "/" + url print("\n" + past_wordle) self.open(past_wordle) self.wait_for_element("#wm-ipp-base") self.remove_elements("#wm-ipp-base") self.click("game-app::shadow game-modal::shadow game-icon") self.initialize_word_list() keyboard_base = "game-app::shadow game-keyboard::shadow " word = random.choice(self.word_list) num_attempts = 0 found_word = False for attempt in range(6): num_attempts += 1 word = random.choice(self.word_list) letters = [] for letter in word: letters.append(letter) button = 'button[data-key="%s"]' % letter self.click(keyboard_base + button) button = "button.one-and-a-half" self.click(keyboard_base + button) row = 'game-app::shadow game-row[letters="%s"]::shadow ' % word tile = row + "game-tile:nth-of-type(%s)" self.wait_for_element(tile % "5" + '::shadow [data-state$="t"]') self.wait_for_element( tile % "5" + '::shadow [data-animation="idle"]' ) letter_status = [] for i in range(1, 6): letter_eval = self.get_attribute(tile % str(i), "evaluation") letter_status.append(letter_eval) if letter_status.count("correct") == 5: found_word = True break self.word_list.remove(word) self.modify_word_list(word, letter_status) self.save_screenshot_to_logs() if found_word: print('Word: "%s"\nAttempts: %s' % (word.upper(), num_attempts)) else: print('Final guess: "%s" (Not the correct word!)' % word.upper()) self.fail("Unable to solve for the correct word in 6 attempts!") self.sleep(3) ================================================ FILE: examples/parameterized_test.py ================================================ from parameterized import parameterized from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class SearchTests(BaseCase): @parameterized.expand( [ ["SeleniumBase Commander", "Commander", "GUI / Commander"], ["SeleniumBase Recorder", "Recorder", "Recorder Mode"], ["SeleniumBase Syntax", "Syntax", "Syntax Formats"], ] ) def test_parameterized_search(self, search_term, keyword, title_text): self.open("https://seleniumbase.io/help_docs/how_it_works/") self.assert_title_contains("SeleniumBase Docs") self.type('input[aria-label="Search"]', search_term) self.click('mark:contains("%s")' % keyword) self.assert_title_contains(title_text) self.save_screenshot_to_logs() ================================================ FILE: examples/performance_test.py ================================================ """Performance test example. Uses decorators.print_runtime(), which prints the runtime duration of a method or "with"-block after the method (or block) completes. Also raises an exception when exceeding the time "limit" if set. Arguments -> description # Optional - Shows description in print output. limit # Optional - Fail if the duration is above the limit. Method / Function example usage -> from seleniumbase import decorators @decorators.print_runtime("My Method") def my_method(): # code ... # code ... "with"-block example usage -> from seleniumbase import decorators with decorators.print_runtime("My Code Block"): # code ... # code ... """ from seleniumbase import BaseCase from seleniumbase import decorators BaseCase.main(__name__, __file__) class PerformanceClass(BaseCase): @decorators.print_runtime("Open Swag Labs and Log In") def login_to_swag_labs(self): print() with decorators.print_runtime("Open Swag Labs"): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") def test_performance_of_swag_labs(self): self.login_to_swag_labs() self.assert_element("div.inventory_list") self.assert_exact_text("Products", "span.title") with decorators.print_runtime("Add backpack and see cart"): self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_text("Backpack", "div.cart_item") with decorators.print_runtime("Remove backpack from cart"): self.click('button:contains("Remove")') # HTML innerText self.assert_text_not_visible("Backpack", "div.cart_item") with decorators.print_runtime("Log out from Swag Labs", 3): self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") ================================================ FILE: examples/presenter/ReadMe.md ================================================

📑 Presenter 🎞️

SeleniumBase Presenter (slide-maker) lets you use Python to generate HTML presentations.

Here's a sample presentation:
([Click on the image/GIF for the actual presentation](https://seleniumbase.io/other/presenter.html)) ([Here's the code for that presentation](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/my_presentation.py)) Slides can include HTML, code, images, and iframes. Here's how to run the example presentation: ```zsh cd examples/presenter pytest my_presentation.py ``` Here's a presentation with a chart:
([Click on the image/GIF for the actual presentation](https://seleniumbase.io/other/core_presentation.html)) ([Here's the code for that presentation](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/core_presentation.py)) Here's how to run that example: ```zsh cd examples/presenter pytest core_presentation.py ```

Creating a new presentation:

```python self.create_presentation(name=None, theme="serif", transition="default") """ Creates a Reveal-JS presentation that you can add slides to. @Params name - If creating multiple presentations at the same time, use this to specify the name of the current presentation. theme - Set a theme with a unique style for the presentation. Valid themes: "serif" (default), "sky", "white", "black", "simple", "league", "moon", "night", "beige", "blood", and "solarized". transition - Set a transition between slides. Valid transitions: "none" (default), "slide", "fade", "zoom", "convex", and "concave". """ ``` If creating multiple presentations at the same time, you can pass the ``name`` parameter to distinguish between different presentations. Notes are disabled by default. You can enable notes by specifying: ``show_notes=True``

Adding a slide to a presentation:

```python self.add_slide(content=None, image=None, code=None, iframe=None, content2=None, notes=None, transition=None, name=None) """ Allows the user to add slides to a presentation. @Params content - The HTML content to display on the presentation slide. image - Attach an image (from a URL link) to the slide. code - Attach code of any programming language to the slide. Language-detection will be used to add syntax formatting. iframe - Attach an iFrame (from a URL link) to the slide. content2 - HTML content to display after adding an image or code. notes - Additional notes to include with the slide. ONLY SEEN if show_notes is set for the presentation. transition - Set a transition between slides. (overrides previous) Valid transitions: "none" (default), "slide", "fade", "zoom", "convex", and "concave". name - If creating multiple presentations at the same time, use this to select the presentation to add slides to. """ ```

Running a presentation:

```python self.begin_presentation( filename="my_presentation.html", show_notes=False, interval=0) """ Begin a Reveal-JS Presentation in the web browser. @Params name - If creating multiple presentations at the same time, use this to select the one you wish to add slides to. filename - The name of the HTML file that you wish to save the presentation to. (filename must end in ".html") show_notes - When set to True, the Notes feature becomes enabled, which allows presenters to see notes next to slides. interval - The delay time between autoplaying slides. (in seconds) If set to 0 (default), autoplay is disabled. """ ``` Before the presentation is run, the full HTML is saved to the ``saved_presentations/`` folder. All methods have the optional ``name`` argument, which is only needed if you're creating multiple presentations at once.

Here's an example of using SeleniumBase Presenter:

```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyPresenterClass(BaseCase): def test_presenter(self): self.create_presentation(theme="serif") self.add_slide( '

Welcome


\n' '

Press the Right Arrow

') self.add_slide( '

SeleniumBase Presenter


\n' '' '' '' '

\n

Create presentations with Python

') self.add_slide( '

Make slides using HTML:


\n' '' '\n\n' '\n' '' '\n' '\n' '\n' '
Row ABCRow XYZ
Value ONEValue TWO
Value THREEValue FOUR

\n

(HTML table example)

') self.add_slide( '

Keyboard Shortcuts:

\n' '\n' '\n' '\n' '' '\n\n' '\n' '\n' '\n' '
KeyAction
=>Next Slide (N also works)
<=Previous Slide (P also works)
FFull Screen Mode
OOverview Mode Toggle
escExit Full Screen / Overview Mode
.Pause/Resume Toggle
spaceNext Slide (alternative)
') self.add_slide( '

Add images to slides:

', image="https://seleniumbase.github.io/other/seagulls.jpg") self.add_slide( '

Add code to slides:

', code=( 'from seleniumbase import BaseCase\n\n' 'class MyTestClass(BaseCase):\n\n' ' def test_basics(self):\n' ' self.open("https://store.xkcd.com/search")\n' ' self.type(\'input[name="q"]\', "xkcd book\\n")\n' ' self.assert_text("xkcd: volume 0", "h3")\n' ' self.open("https://xkcd.com/353/")\n' ' self.assert_title("xkcd: Python")\n' ' self.assert_element(\'img[alt="Python"]\')\n' ' self.click(\'a[rel="license"]\')\n' ' self.assert_text("free to copy and reuse")\n' ' self.go_back()\n' ' self.click_link("About")\n' ' self.assert_exact_text("xkcd.com", "h2")')) self.add_slide( "

Highlight code in slides:

", code=( 'from seleniumbase import BaseCase\n\n' 'class MyTestClass(BaseCase):\n\n' ' def test_basics(self):\n' ' self.open("https://store.xkcd.com/search")\n' ' self.type(\'input[name="q"]\', "xkcd book\\n")\n' ' self.assert_text("xkcd: volume 0", "h3")')) self.add_slide( '

Add iFrames to slides:

', iframe="https://seleniumbase.io/demo_page") self.add_slide( '

Getting started is easy:

', code=( 'from seleniumbase import BaseCase\n\n' 'class MyPresenterClass(BaseCase):\n\n' ' def test_presenter(self):\n' ' self.create_presentation(theme="serif")\n' ' self.add_slide("Welcome to Presenter!")\n' ' self.add_slide(\n' ' "Add code to slides:",\n' ' code=(\n' ' "from seleniumbase import BaseCase\\n\\n"\n' ' "class MyPresenterClass(BaseCase):\\n\\n"\n' ' " def test_presenter(self):\\n"\n' ' " self.create_presentation()\\n"))\n' ' self.begin_presentation(\n' ' filename="demo.html", show_notes=True)')) self.add_slide( '

Include notes with slides:


', code=('self.add_slide("[Your HTML goes here]",\n' ' code="[Your software code goes here]",\n' ' content2="[Additional HTML goes here]",\n' ' notes="[Attached speaker notes go here]"\n' ' "[Note A! -- Note B! -- Note C! ]")'), notes='

  • Note A!
  • Note B!
  • Note C!
  • Note D!

', content2="

(Notes can include HTML tags)

") self.add_slide( '

Multiple themes available:

', code=( 'self.create_presentation(theme="serif")\n\n' 'self.create_presentation(theme="sky")\n\n' 'self.create_presentation(theme="simple")\n\n' 'self.create_presentation(theme="white")\n\n' 'self.create_presentation(theme="moon")\n\n' 'self.create_presentation(theme="black")\n\n' 'self.create_presentation(theme="night")\n\n' 'self.create_presentation(theme="beige")\n\n' 'self.create_presentation(theme="league")')) self.add_slide( '

The End

', image="https://seleniumbase.github.io/img/sb_logo_10.png") self.begin_presentation( filename="presenter.html", show_notes=True, interval=0) ``` That example is from [my_presentation.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/my_presentation.py), which you can run from the ``examples/presenter`` folder with the following command: ```zsh pytest my_presentation.py ```

Saving a presentation:

If you want to save the presentation you created as an HTML file, use: ```python self.save_presentation(filename="my_presentation.html", show_notes=True) ``` Presentations automatically get saved when calling: ```python self.begin_presentation(show_notes=True) ```

Special abilities:

If you want to highlight multiple lines at different times in the same slide with the `` / `` tags, you can use the new ``-``, ``-`` tags, which will generate multiple HTML slides from one Python slide. Example: ```python self.add_slide( code=(

Highlight this on the 1st generated slide

Highlight this on the 2nd generated slide

Highlight this on the 3rd generated slide

Highlight this on the 4th generated slide

) ) ``` Those should automatically get converted to `` ... `` on their turn: Eg. First generated slide: ```html

Highlight this on the first generated slide

Highlight this on the second generated slide

Highlight this on the third generated slide

Highlight this on the fourth generated slide>

``` Eg. Second generated slide: ```html

Highlight this on the first generated slide

Highlight this on the second generated slide

Highlight this on the third generated slide

Highlight this on the fourth generated slide>

``` Etc... --------

SeleniumBase

================================================ FILE: examples/presenter/core_presentation.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class PresentationWithChart(BaseCase): def test_seleniumbase_chart(self): self.create_presentation(theme="league", transition="slide") self.create_pie_chart(title="The 4 core areas of SeleniumBase:") self.add_data_point("Basic API (test methods)", 1) self.add_data_point("Command-line options (pytest options)", 1) self.add_data_point("The Console Scripts interface", 1) self.add_data_point("Advanced API (Tours, Charts, & Presentations)", 1) self.add_slide("

SeleniumBase core areas

" + self.extract_chart()) self.add_slide( "

Basic API (test methods). Example test:

", code=( "from seleniumbase import BaseCase\n\n" "class TestMFALogin(BaseCase):\n\n" " def test_mfa_login(self):\n" ' self.open("seleniumbase.io/realworld/login")\n' ' self.type("#username", "demo_user")\n' ' self.type("#password", "secret_pass")\n' ' self.enter_mfa_code(' '"#totpcode", "GAXG2MTEOR3DMMDG")\n' ' self.assert_text("Welcome!", "h1")\n' ' self.highlight("img#image1")\n' " self.click('a:contains(\"This Page\")')\n" " self.save_screenshot_to_logs()\n" ' self.click_link("Sign out")\n' ' self.assert_element(\'a:contains("Sign in")\')\n' ), ) self.add_slide( "

Command-line options. Examples:

", code=( "$ pytest my_first_test.py\n" "$ pytest test_swag_labs.py --mobile\n" "$ pytest edge_test.py --browser=edge\n" "$ pytest basic_test.py --headless\n" "$ pytest my_first_test.py --demo --guest\n" "$ pytest basic_test.py --slow\n" "$ pytest -v -m marker2 --headless --save-screenshot\n" "$ pytest parameterized_test.py --reuse-session\n" "$ pytest test_suite.py --html=report.html --rs\n" "$ pytest test_suite.py --dashboard --html=report.html\n" "$ pytest github_test.py --demo --disable-csp\n" "$ pytest test_suite.py -n=2 --rs --crumbs\n" "$ pytest test_demo_site.py --incognito\n" "$ pytest verify_undetected.py --uc\n" "$ pytest basic_test.py --sjw --pls=none\n" ), ) self.add_slide( "

The Console Scripts interface. Examples:

", code=( "$ sbase get chromedriver\n" "$ sbase mkdir new_test_folder\n" "$ sbase mkfile new_test.py\n" "$ sbase mkpres new_presentation.py\n" "$ sbase mkchart new_chart.py\n" "$ sbase print basic_test.py -n\n" "$ sbase translate basic_test.py -p --ru -n\n" "$ sbase grid-hub start\n" '$ sbase grid-node start --hub="127.0.0.1"\n' "$ sbase grid-node stop\n" "$ sbase grid-hub stop\n" "$ sbase recorder\n" "$ sbase commander\n" "$ sbase methods\n" "$ sbase options\n" ), ) self.add_slide( '

Advanced API. "Presenter" example:

', code=( "from seleniumbase import BaseCase\n\n" "class MyPresenterClass(BaseCase):\n" " def test_presenter(self):\n" ' self.create_presentation(theme="serif")\n' ' self.add_slide("Welcome to Presenter!")\n' " self.add_slide(\n" ' "Add code to slides:",\n' " code=(\n" ' "from seleniumbase import BaseCase\\n\\n"\n' ' "class MyPresenterClass(BaseCase):\\n\\n"\n' ' " def test_presenter(self):\\n"\n' ' " self.create_presentation()\\n"))\n' " self.begin_presentation(\n" ' filename="demo.html", show_notes=True)' ), ) self.add_slide( "

The End

", image="https://seleniumbase.io/cdn/img/sb_logo_g.png", ) self.begin_presentation(filename="core_presentation.html") ================================================ FILE: examples/presenter/edge_presentation.py ================================================ from seleniumbase import BaseCase if __name__ == "__main__": from pytest import main main([__file__, "--edge", "-s", "--disable-csp"]) class EdgePresentationClass(BaseCase): def test_presentation(self): if not self.browser == "edge" or not self.disable_csp: self.driver.quit() self.get_new_driver(browser="edge", disable_csp=True) self.demo_mode = False self.maximize_window() self._output_file_saves = False self.create_presentation(theme="serif", transition="fade") self.add_slide( "

A deep dive into:

" "

Browser automation

" "

on Edge, with Python!

\n" "


\n" "

Presented by Michael Mintz

\n" ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.open("data:,") self.open("https://www.bostoncodecamp.com/CC34/Schedule/SessionGrid") self.highlight("h2", loops=8) if self.is_element_visible('[data-sessionid="467776"]'): self.highlight('div[data-sessionid="467776"]', loops=10) self.create_tour(theme="driverjs") self.add_tour_step( "

Here we are

", '[data-sessionid="467776"]' ) self.play_tour() self.click('a[onclick*="467776"]') self.create_tour(theme="hopscotch") self.add_tour_step( "

What to expect

", "div.sz-modal-session", alignment="left", ) self.play_tour() self.sleep(0.25) self.open("data:,") self.create_presentation(theme="beige", transition="fade") self.add_slide( "

About the presenter:

\n" "
    \n" "
  • I created SeleniumBase (for Python).
  • \n" "
  • I lead the Automation Team at iboss.
  • \n" "
\n", image="https://seleniumbase.io/other/iboss_booth.png", ) self.add_slide( "

By the end of this presentation, you'll learn:

" "

\n" "
    \n" "
  • How to automate on Edge using Microsoft's WebDriver.
  • " "
    \n" "
  • How Python frameworks can simplify Edge automation.
  • " "
    \n" "
\n", ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.open("data:,") self.open( "https://learn.microsoft.com/en-us/microsoft-edge/" "test-and-automation/test-and-automation" ) self.wait_for_element("h1") self.sleep(1) self.create_tour(theme="default") self.add_tour_step("

Let's begin the overview!

") self.play_tour() if self.is_element_visible('button[data-bi-name="close"]'): self.click('button[data-bi-name="close"]') self.wait_for_element_not_visible('button[data-bi-name="close"]') self.highlight("h1#test-and-automation-in-microsoft-edge") self.create_tour(theme="driverjs") self.add_tour_step( "", "h1#test-and-automation-in-microsoft-edge", alignment="right" ) self.add_tour_step("", "nav#center-doc-outline ~ p", alignment="right") self.add_tour_step( "", 'table[aria-label*="Test and automation"]', alignment="right" ) self.add_tour_step("A framework", "#playwright", alignment="left") self.add_tour_step("Another framework", "#puppeteer", alignment="left") self.add_tour_step("Today's framework", "#webdriver", alignment="left") self.add_tour_step( "", 'a[href="../webdriver-chromium/"]', alignment="right" ) self.play_tour() self.highlight('a:contains("Use WebDriver to automate")') self.open( "https://learn.microsoft.com/en-us/" "microsoft-edge/webdriver-chromium/?tabs=python" ) self.wait_for_element("h1") self.create_tour(theme="driverjs") self.add_tour_step( "", "#use-webdriver-to-automate-microsoft-edge", alignment="right" ) self.add_tour_step( "", 'div[data-heading-level="h2"] ~ p', alignment="right" ) self.add_tour_step( "", 'div[data-heading-level="h2"] ~ ul', alignment="right" ) self.add_tour_step( "", 'table[aria-label="Table 1"]', alignment="right" ) self.add_tour_step( "", "#download-microsoft-edge-webdriver", alignment="right" ) self.add_tour_step( "", 'img[src*="microsoft-edge-version"]', alignment="right" ) self.play_tour() self.highlight('img[src*="microsoft-edge-version"]') self.highlight('img[src*="microsoft-edge-driver-install"]', loops=8) self.highlight('p:contains("that matches your version")', loops=8) self.create_tour(theme="driverjs") self.add_tour_step( "", '[href*="microsoft-edge/tools/webdriver"]', alignment="right" ) self.play_tour() self.highlight('[href*="microsoft-edge/tools/webdriver"]') self.get_new_driver(browser="edge", disable_csp=True) self.maximize_window() self.open( "https://developer.microsoft.com/en-us/" "microsoft-edge/tools/webdriver/" ) self.wait_for_element("div.common-heading") self.scroll_to("div.common-heading") zoom_in = 'div.h1{zoom: 1.02;-moz-transform: scale(1.02);}' self.add_css_style(zoom_in) self.highlight("div.common-heading", loops=8) self.create_tour(theme="hopscotch") self.add_tour_step("", "div.common-heading", alignment="right") self.play_tour() self.create_tour(theme="hopscotch") self.add_tour_step( "", "div.block-heading--sixtyforty", alignment="left" ) self.play_tour() self.quit_extra_driver() self.switch_to_default_driver() self.create_tour(theme="hopscotch") self.add_tour_step( "", 'img[src*="microsoft-edge-driver-install"]', alignment="left" ) self.play_tour() self.highlight('p:contains("After the download completes")', loops=10) self.sleep(0.5) self.create_tour(theme="hopscotch") self.add_tour_step( "", "#choose-a-webdriver-testing-framework", alignment="left" ) self.add_tour_step("", "#using-selenium-4", alignment="left") self.add_tour_step( "", "#automate-microsoft-edge-with-webdriver", alignment="left" ) self.add_tour_step("", "#automate-microsoft-edge", alignment="left") self.add_tour_step("", "#tabgroup_1", alignment="left") self.add_tour_step( "", '[id*="configure-the-edge-webdriver-serv"]', alignment="left" ) self.add_tour_step("", "#tabgroup_2", alignment="left") self.add_tour_step( "", "#configure-microsoft-edge-options", alignment="left" ) self.add_tour_step( "", "#choose-specific-browser-binaries", alignment="left" ) self.add_tour_step("", "#tabgroup_3", alignment="left") self.add_tour_step( "", "#pass-extra-command-line-arguments", alignment="left" ) self.add_tour_step("", "#tabgroup_4", alignment="left") self.add_tour_step( "", "#other-webdriver-installation-options", alignment="left" ) self.add_tour_step( "", 'code[data-author-content*="docker run"]', alignment="left" ) self.add_tour_step( "", "#opt-out-of-diagnostic-data-collection", alignment="left" ) self.add_tour_step( "", "#developer-tools-availability-policy", alignment="left" ) self.play_tour() self.sleep(0.25) self.open("data:,") self.create_presentation(theme="sky", transition="fade") self.add_slide( "

How do you get Selenium?

\n" "
\n" "

(for Python)


\n" "

pip install selenium

", image="https://seleniumbase.io/other/selenium_pypi.png", ) self.add_slide( "

What are some building blocks?

\n" "

\n", code=( "from selenium import webdriver\n\n" "driver = webdriver.Edge()\n\n" 'driver.get("http://selenium.dev")\n\n' "element = driver.find_element" '("css selector", "#docsearch span")\n\n' "element.click()\n\n" "elem_2 = driver.find_element" '("css selector", "#docsearch-input")\n\n' 'elem_2.send_keys("Python")\n\n' "driver.quit()\n\n" ), ) self.add_slide( "

Is Selenium really a framework, or just a library?

\n" "

\n" "

Given that Selenium uses WebDriver APIs for interacting with" " websites, but lacks essential features for structuring tests," " (and more...), Selenium is really: JUST A LIBRARY!

\n", image="https://seleniumbase.io/other/selenium_slogan.png" ) self.add_slide( "

JUST A LIBRARY, continued...

\n" "

\n" "

Technically, Selenium consists of multiple language bindings" " for interacting with WebDriver APIs. These bindings include:" " C#, Java, JS, Python, and Ruby.

\n", image="https://seleniumbase.io/other/library_books.jpg" ) self.add_slide( "

Test frameworks wrap Selenium to improve things!


\n" "
" '' '(Where does a framework fit in?)\n', image="https://seleniumbase.io/other/with_a_framework.png", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without extra libraries or frameworks?


" "

\n" "
", image="https://seleniumbase.io/other/sel_and_py_2.png", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without extra libraries or frameworks?


" "

\n" "The default timeout is 0: If an element isn't immediately " "ready to be interacted with, you'll get errors when trying " "to interact with those elements.\n" "

\n", image="https://seleniumbase.io/other/messy_stacktrace.png", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without extra libraries or frameworks?


" "


\n" "The command statements can get a bit too long:

\n" "

" "driver.find_element(By.CSS_SELECTOR, CSS_SELECTOR).click()" "


" "

This is better:

" "

self.click(CSS_SELECTOR)


", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without extra libraries or frameworks?



\n" "No HTML reports, dashboards, screenshots...
" "

A test framework can provide those!


", ) self.add_slide( "
Raw Selenium disadvantages, continued...

" "
No HTML reports, dashboards, screenshots...
\n" "A test framework can provide those!", image="https://seleniumbase.io/cdn/img/dash_report.png", ) self.add_slide( "

Raw Selenium disadvantages, continued...


\n
\n" "

It takes multiple lines of code to do simple tasks:" "

\n
\n"
            'element = driver.find_element("css selector", "#password")\n'
            "element.clear()\n"
            'element.send_keys("secret_sauce")\n'
            'element.submit()\n'
            "
\n
\n" "

But with a framework, do all that in ONE line:" '

\n
self.type("#password", "secret_sauce\\n")
' ) self.add_slide( "

What else can test frameworks provide?


\n" "
    \n" "
  • Driver management.
  • \n" "
  • Advanced methods. Eg. " "
    self.assert_no_broken_links()
  • \n" "
  • Test assertions. Eg. " "
    self.assert_text(TEXT, SELECTOR)
  • \n" "
  • Command-line options. Eg. " "
    pytest --browser=edge --html=report.html
  • \n" "
  • Advanced tools (Eg. test recorders)
  • \n" "
  • Easy to read error messages. Eg. " '
    Element "h2" was not visible after 10s!
  • ' "
\n", ) self.add_slide( "

What about test runners?


\n" "

Python includes powerful test runners, such as pytest." "

\n", image="https://seleniumbase.io/other/invoke_pytest.png", ) self.add_slide( "

What can pytest do?


\n" "
    \n" "
  • Auto-collect tests to run.
  • \n" "
  • Use markers for organizing tests.
  • \n" "
  • Generate test reports.
  • \n" "
  • Provide test assertions.
  • \n" "
  • Multithread your tests.
  • \n" "
  • Use a large number of existing plugins.
  • \n" "
\n", ) self.add_slide( "

What about complete frameworks?


\n" "

SeleniumBase combines the best of both " "Selenium and pytest " "into a super framework.

\n", image="https://seleniumbase.io/cdn/img/sb_logo_10c.png", ) self.add_slide( "

SeleniumBase features. (You already saw this!)" "


\n" "
    \n" "
  • Driver management.
  • \n" "
  • Advanced methods. Eg. " "
    self.assert_no_broken_links()
  • \n" "
  • Test assertions. Eg. " "
    self.assert_text(TEXT, SELECTOR)
  • \n" "
  • Command-line options. Eg. " "
    pytest --browser=edge --html=report.html
  • \n" "
  • Advanced tools (Eg. test recorders)
  • \n" "
  • Easy to read error messages. Eg. " '
    Element "h2" was not visible after 10s!
  • ' "
\n", ) self.add_slide( "

How do you get SeleniumBase?

\n" "

\n" "

pip install seleniumbase

", image="https://seleniumbase.io/other/seleniumbase_github.png", ) code = ( "from seleniumbase import BaseCase\n" "BaseCase.main(__name__, __file__)\n\n" "class MyTestClass(BaseCase):\n" " def test_basics(self):\n" ' self.open("https://www.saucedemo.com")\n' ' self.type("#user-name", "standard_user")\n' ' self.type("#password", "secret_sauce\\n")\n' ' self.assert_element("div.inventory_list")\n' ' self.assert_exact_text("Products", "span.title")\n' " self.click('button[name*=\"backpack\"]')\n" ' self.click("#shopping_cart_container a")\n' ' self.assert_exact_text("Your Cart", "span.title")\n' ' self.assert_text("Backpack", "div.cart_item")\n' ' self.click("button#checkout")\n' ' self.type("#first-name", "SeleniumBase")\n' ' self.type("#last-name", "Automation")\n' ' self.type("#postal-code", "77123")\n' ' self.click("input#continue")\n' ' self.assert_text("Checkout: Overview")' ) self.add_slide( "SeleniumBase example test
", code=code, ) self.add_slide( "SeleniumBase example test (Time to run this!)
", code=code, ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.get_new_driver(browser="edge") self.maximize_window() self.open("data:,") self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Automation") self.type("#postal-code", "77123") self.click("input#continue") self.assert_text("Checkout: Overview") self.quit_extra_driver() self.create_presentation(theme="serif", transition="fade") self.add_slide( '(Now to run that same test in "--demo" mode!)
', code=code, ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.get_new_driver(browser="edge") self.maximize_window() self.open("data:,") self.demo_mode = True self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Automation") self.type("#postal-code", "77123") self.click("input#continue") self.assert_text("Checkout: Overview") self.demo_mode = False self.quit_extra_driver() self.create_presentation(theme="serif", transition="fade") self.add_slide( "

What about Edge tests using a mobile emulator?


" "

pytest --edge --mobile

" "

Another demo...

", ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.get_new_driver(browser="edge", is_mobile=True) self.maximize_window() self.open("https://www.roblox.com/") self.assert_element("#download-the-app-container") self.assert_text("Roblox for Android") self.assert_text("Continue in App", "a.content-action-emphasis") self.highlight('span:contains("Roblox for Android")', loops=8) self.highlight("a.content-action-emphasis", loops=8) self.quit_extra_driver() self.create_presentation(theme="serif", transition="fade") self.add_slide( "

What about 2-Factor Auth?



\n" "

Another demo...

", ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.get_new_driver(browser="edge") self.maximize_window() self.demo_mode = True self.open("https://seleniumbase.io/realworld/login") self.type("#username", "demo_user") self.type("#password", "secret_pass") self.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit self.assert_text("Welcome!", "h1") self.highlight("img#image1") # A fancier assert_element() call self.click('a:contains("This Page")') # Use :contains() on any tag self.save_screenshot_to_logs() # ("./latest_logs" folder for test) self.click_link("Sign out") # Link must be "a" tag. Not "button". self.assert_element('a:contains("Sign in")') self.assert_exact_text("You have been signed out!", "#top_message") self.demo_mode = False self.quit_extra_driver() self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Need some coffee?



\n" "

Another demo...

", ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.get_new_driver(browser="edge") self.maximize_window() self.demo_mode = True self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.click('div[data-sb="Cappuccino"]') self.click('div[data-sb="Flat-White"]') self.click('div[data-sb="Cafe-Latte"]') self.click('a[aria-label="Cart page"]') self.assert_exact_text("Total: $53.00", "button.pay") self.click("button.pay") self.type("input#name", "Selenium Coffee") self.type("input#email", "test@test.test") self.click("button#submit-payment") self.assert_text("Thanks for your purchase.", "#app .success") self.demo_mode = False self.quit_extra_driver() self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Let's have some fun!



\n" "

Another demo...

", ) self.begin_presentation(filename="edge_presentation.html") self.sleep(0.25) self.get_new_driver(browser="edge") self.maximize_window() self.open("https://seleniumbase.io/error_page/") self.highlight('img[alt="500 Error"]') self.highlight("img#parallax_octocat") self.highlight("#parallax_error_text") self.highlight('img[alt*="404"]') self.highlight("img#octobi_wan_catnobi") self.highlight("img#speeder") self.quit_extra_driver() self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Let's learn more...



\n" ) self.add_slide( "

Common SeleniumBase methods:


", code=( "self.open(url) # Navigate the browser window to the URL.\n" "self.type(selector, text) # Update field with the text.\n" "self.click(selector) # Click element with the selector.\n" "self.click_link(link_text) # Click link containing text.\n" "self.check_if_unchecked(selector) # Check checkbox.\n" "self.uncheck_if_checked(selector) # Uncheck checkbox.\n" "self.select_option_by_text(dropdown_selector, option)\n" "self.hover_and_click(hover_selector, click_selector)\n" "self.drag_and_drop(drag_selector, drop_selector)\n" "self.choose_file(selector, file_path) # Upload a file.\n" "self.switch_to_frame(frame) # Switch into the iframe.\n" "self.switch_to_default_content() # Exit all iframes.\n" "self.switch_to_parent_frame() # Exit current iframe.\n" "self.open_new_window() # Open new window in same browser.\n" "self.switch_to_window(window) # Switch to browser window.\n" "self.switch_to_default_window() # Go to original window.\n" "self.assert_element(selector) # Verify element visible.\n" "self.assert_text(text, selector) # Substring assertion.\n" "self.assert_exact_text(text, selector) # String assert." ), ) self.add_slide( "

Common command-line options:


" "
\n"
            '--browser=BROWSER'
            ''
            '  (Choose web browser. Default: "chrome".)'
            '\n'
            '--edge / --firefox / --safari'
            ''
            '  (Browser Shortcut.)'
            '\n'
            '--headless'
            ''
            '  (Run tests headlessly.  Default on Linux OS.)'
            '\n'
            '--demo'
            ''
            '  (Slow down and see test actions as they occur.)'
            '\n'
            '--slow'
            ''
            '  (Slow down the automation. Faster than Demo Mode.)'
            '\n'
            '--rs / --reuse-session'
            ''
            '  (Reuse browser session for tests.)'
            '\n'
            '--rcs / --reuse-class-session'
            ''
            '  (RS, but for class tests.)'
            '\n'
            '--crumbs'
            ''
            '  (Clear cookies between tests reusing a session.)'
            '\n'
            '--maximize'
            ''
            '  (Start tests with the web browser maximized.)'
            '\n'
            '--dashboard'
            ''
            '  (Enable the SB Dashboard at dashboard.html)'
            '\n'
            '--uc'
            ''
            '  (Enable undetected-chromedriver mode.)'
            '\n'
            '--incognito'
            ''
            '  (Enable Incognito mode.)'
            '\n'
            '--guest'
            ''
            '  (Enable Guest mode.)'
            '\n'
            '-m=MARKER'
            ''
            '  (Run tests with the specified pytest marker.)'
            '\n'
            '-n=NUM'
            ''
            '  (Multithread the tests using that many threads.)'
            '\n'
            '-v'
            ''
            '  (Verbose mode. Print the full names of each test run.)'
            '\n'
            '--html=report.html'
            ''
            '  (Create a detailed pytest-html report.)'
            '\n'
            '--co / --collect-only'
            ''
            '  (Only show discovered tests. No run.)'
            '\n'
            '--co -q'
            ''
            '  (Only show full names of discovered tests. No run.)'
            '\n'
            '-x'
            ''
            '  (Stop running tests after the first failure is reached.)'
            '\n'
            "
" ) self.add_slide( "

Common console scripts:


", code=( "sbase get [DRIVER] [OPTIONS] # Eg. chromedriver\n" "sbase methods # List common Python methods\n" "sbase options # List common pytest options\n" "sbase gui # Open the SB GUI for pytest\n" "sbase caseplans # Open the SB Case Plans App\n" "sbase mkdir [DIRECTORY] # Create a test directory\n" "sbase mkfile [FILE.py] # Create a test file\n" "sbase codegen [FILE.py] [OPTIONS] # Record a test\n" "sbase recorder # Open the SB Recorder App\n" "sbase mkpres # Create a Presentation boilerplate\n" "sbase mkchart # Create a Chart boilerplate\n" "sbase print [FILE] # Print file to console\n" "sbase translate [FILE.py] [OPTIONS] # Translate\n" "sbase extract-objects [SB_FILE.py] # Get objects\n" "sbase inject-objects [SB_FILE.py] # Swap selectors\n" "sbase objectify [SB_FILE.py] # Get & swap objects\n" "sbase revert-objects [SB_FILE.py] # Undo objectify\n" "sbase download server # Get Selenium Grid JAR file\n" "sbase grid-hub [start|stop] [OPTIONS] # Start Grid\n" "sbase grid-node [start|stop] --hub=[IP] # Add Node" ), ) self.add_slide( "

Live Demo Time!


" "

(Let's head over to GitHub...)

", image="https://seleniumbase.io/other/sbase_qr_code_s.png", ) self.begin_presentation(filename="edge_presentation.html") ================================================ FILE: examples/presenter/fundamentals.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_presentation(self): self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Python Selenium:

\n" "

Fundamentals to Frameworks

" "

(with SeleniumBase)

\n" "


\n" "

Presented by Michael Mintz

\n" "

SeleniumConf 2023 - Chicago

\n" ) self.add_slide( "

Welcome to Chicago!


\n", image="https://seleniumbase.io/other/chicago_skyline.png", ) self.add_slide( "

A few shout-outs before starting:



\n" "
    \n" "
  • The Selenium Org (made everything possible)

  • " "
  • Conference organizers (made today possible)

  • " "
  • My wife (a major supporter of my work)

  • " "
  • iboss (my employer)
  • \n" "
", ) self.add_slide( "

About me:

\n" "
    \n" "
  • I created the SeleniumBase framework.
  • \n" "
  • I lead the Automation Team at iboss.
  • \n" "
", image="https://seleniumbase.io/other/iboss_booth.png", ) self.add_slide( "

" "This is the ONLY Python session at SeleniumConf!" "



\n" "

\n" "If one Python session is not enough, come see me afterwards.\n" "

\n", image="https://seleniumbase.io/other/python_3d_logo.png", ) self.add_slide( "

By the end of this presentation, you'll learn:

" "

\n" "
    \n" "
  • Python Selenium fundamentals.

  • \n" "
  • How test frameworks can improve on the fundamentals.
  • " "
    \n" "
  • How SeleniumBase makes Python Web Automation easier.
  • " "
    \n" "
", ) self.add_slide( "

The Format:

" "
\n" "
    \n" "
  • Slides.
  • \n" "
  • ReadMe files.
  • \n" "
  • LOTS of live demos!!!
  • \n" "
", image="https://seleniumbase.io/other/presentation_parts.png", ) self.add_slide( "

What does Selenium provide?

\n" "

\n" "

Selenium provides an automation library!

\n" "
(It does NOT provide a test framework.)
\n", image="https://seleniumbase.io/other/selenium_slogan.png", ) self.add_slide( "

How do you get Selenium?

\n" "
\n" "

(for Python)


\n" "

pip install selenium

", image="https://seleniumbase.io/other/selenium_pypi.png", ) self.add_slide( "

What are some building blocks?

\n" "

\n", code=( "from selenium import webdriver\n\n" "driver = webdriver.Edge()\n\n" 'driver.get("http://selenium.dev")\n\n' "element = driver.find_element" '("css selector", "#docsearch span")\n\n' "element.click()\n\n" "elem_2 = driver.find_element" '("css selector", "#docsearch-input")\n\n' 'elem_2.send_keys("Python")\n\n' "driver.quit()\n\n" ), ) self.add_slide( "

Launching a browser can get more complicated:

\n" "

\n", code=( "from selenium import webdriver\n\n" "options = webdriver.ChromeOptions()\n" 'options.add_argument("--disable-notifications")\n' "options.add_experimental_option(\n " '"excludeSwitches", ["enable-automation", "enable-logging"]\n' ')\n' "prefs = {\n" ' "credentials_enable_service": False,\n' ' "profile.password_manager_enabled": False,\n' "}\n" 'options.add_experimental_option("prefs", prefs)\n' "driver = webdriver.Chrome(options=options)\n" ), ) self.add_slide( "

Test frameworks wrap Selenium to improve things!


\n" "
" '' '(Where does a framework fit in?)\n', image="https://seleniumbase.io/other/with_a_framework.png", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without additional libraries or frameworks?


" "

\n" "
", image="https://seleniumbase.io/other/sel_and_py_2.png", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without additional libraries or frameworks?


" "

\n" "The default timeout is 0: If an element isn't immediately " "ready to be interacted with, you'll get errors when trying " "to interact with those elements.\n" "

\n", image="https://seleniumbase.io/other/messy_stacktrace.png", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without extra libraries or frameworks?


" "


\n" "The command statements can get a bit too long:

\n" "

" "driver.find_element(By.CSS_SELECTOR, CSS_SELECTOR).click()" "


" "

This is better:

" "

self.click(CSS_SELECTOR)


", ) self.add_slide( "

What are some disadvantages of using raw Selenium " "without additional libraries or frameworks?


" "


\n" "No HTML reports, dashboards, results, screenshots..." "


A test framework can provide those!
", ) self.add_slide( "

Raw Selenium disadvantages, continued...
\n" "No HTML reports, dashboards, results, screenshots...
" "A test framework can provide those!
", image="https://seleniumbase.io/cdn/img/dash_report.png", ) self.add_slide( "

Raw Selenium disadvantages, continued...


\n" "
\n" "

It takes multiple lines of code to do simple tasks:" "

\n
\n"
            'element = driver.find_element("css selector", "#password")\n'
            "element.clear()\n"
            'element.send_keys("secret_sauce")\n'
            'element.submit()\n'
            "
\n
\n" "

But with a framework, do all that in ONE line:

\n" '
self.type("#password", "secret_sauce\\n")
' ) self.add_slide( "

Raw Selenium disadvantages, continued...


\n" "
\n" "

It takes multiple lines of code to do simple tasks:

\n" "
\n"
            'element = driver.find_element("css selector", "#password")\n'
            "element.clear()\n"
            'element.send_keys("secret_sauce")\n'
            'element.submit()\n'
            "
\n
\n" "

But with a framework, do all that in ONE line:" "

\n" '
self.type("#password", "secret_sauce\\n")
' ) self.add_slide( "

What else can test frameworks provide?


\n" "
    \n" "
  • Driver management.
  • \n" "
  • Advanced methods. Eg. " "
    self.assert_no_broken_links()
  • \n" "
  • Test assertions. Eg. " "
    self.assert_text(TEXT, SELECTOR)
  • \n" "
  • Command-line options. Eg. " "
    pytest --browser=edge --html=report.html
  • \n" "
  • Advanced tools (Eg. test recorders)
  • \n" "
  • Easy to read error messages. Eg. " '
    Element "h2" was not visible after 10s!
  • ' "
", ) self.add_slide( "

What about test runners?


\n" "

Python includes powerful test runners, such as pytest." "

\n", image="https://seleniumbase.io/other/invoke_pytest.png", ) self.add_slide( "

What can pytest do?


\n" "
    \n" "
  • Auto-collect tests to run.
  • \n" "
  • Use markers for organizing tests.
  • \n" "
  • Generate test reports.
  • \n" "
  • Provide test assertions.
  • \n" "
  • Multithread your tests.
  • \n" "
  • Use a large number of existing plugins.
  • \n" "
", ) self.add_slide( "

What about complete frameworks?


\n" "

SeleniumBase combines the best of both " "Selenium and pytest " "into a super framework.

\n", image="https://seleniumbase.io/cdn/img/sb_logo_10c.png", ) self.add_slide( "

SeleniumBase features. (You already saw this slide!)" "


\n" "
    \n" "
  • Driver management.
  • \n" "
  • Advanced methods. Eg. " "
    self.assert_no_broken_links()
  • \n" "
  • Test assertions. Eg. " "
    self.assert_text(TEXT, SELECTOR)
  • \n" "
  • Command-line options. Eg. " "
    pytest --browser=edge --html=report.html
  • \n" "
  • Advanced tools (Eg. test recorders)
  • \n" "
  • Easy to read error messages. Eg. " '
    Element "h2" was not visible after 10s!
  • ' "
", ) self.add_slide( "

How do you get SeleniumBase?

\n" "

\n" "

pip install seleniumbase

", image="https://seleniumbase.io/other/seleniumbase_github.png", ) self.add_slide( "

SeleniumBase example test:


", code=( "from seleniumbase import BaseCase\n" "BaseCase.main(__name__, __file__)\n\n" "class MyTestClass(BaseCase):\n" " def test_basics(self):\n" ' self.open("https://www.saucedemo.com")\n' ' self.type("#user-name", "standard_user")\n' ' self.type("#password", "secret_sauce\\n")\n' ' self.assert_element("div.inventory_list")\n' ' self.assert_exact_text("Products", "span.title")\n' " self.click('button[name*=\"backpack\"]')\n" ' self.click("#shopping_cart_container a")\n' ' self.assert_exact_text("Your Cart", "span.title")\n' ' self.assert_text("Backpack", "div.cart_item")\n' ' self.click("button#checkout")\n' ' self.type("#first-name", "SeleniumBase")\n' ' self.type("#last-name", "Automation")\n' ' self.type("#postal-code", "77123")\n' ' self.click("input#continue")\n' ' self.assert_text("Checkout: Overview")' ), ) self.add_slide( "

Common SeleniumBase methods:


", code=( "self.open(url) # Navigate the browser window to the URL.\n" "self.type(selector, text) # Update field with the text.\n" "self.click(selector) # Click element with the selector.\n" "self.click_link(link_text) # Click link containing text.\n" "self.check_if_unchecked(selector) # Check checkbox.\n" "self.uncheck_if_checked(selector) # Uncheck checkbox.\n" "self.select_option_by_text(dropdown_selector, option)\n" "self.hover_and_click(hover_selector, click_selector)\n" "self.drag_and_drop(drag_selector, drop_selector)\n" "self.choose_file(selector, file_path) # Upload a file.\n" "self.switch_to_frame(frame) # Switch into the iframe.\n" "self.switch_to_default_content() # Exit all iframes.\n" "self.switch_to_parent_frame() # Exit current iframe.\n" "self.open_new_window() # Open new window in same browser.\n" "self.switch_to_window(window) # Switch to browser window.\n" "self.switch_to_default_window() # Go to original window.\n" "self.assert_element(selector) # Verify element visible.\n" "self.assert_text(text, selector) # Substring assertion.\n" "self.assert_exact_text(text, selector) # String assert." ), ) self.add_slide( "

Common command-line options:


" "
\n"
            '--browser=BROWSER'
            ''
            '  (Choose web browser. Default: "chrome".)'
            '\n'
            '--edge / --firefox / --safari'
            ''
            '  (Browser Shortcut.)'
            '\n'
            '--headless'
            ''
            '  (Run tests headlessly.  Default on Linux OS.)'
            '\n'
            '--demo'
            ''
            '  (Slow down and see test actions as they occur.)'
            '\n'
            '--slow'
            ''
            '  (Slow down the automation. Faster than Demo Mode.)'
            '\n'
            '--reuse-session / --rs'
            ''
            '  (Reuse browser session for tests.)'
            '\n'
            '--reuse-class-session / --rcs'
            ''
            '  (RS, but for class tests.)'
            '\n'
            '--crumbs'
            ''
            '  (Clear cookies between tests reusing a session.)'
            '\n'
            '--maximize'
            ''
            '  (Start tests with the web browser maximized.)'
            '\n'
            '--dashboard'
            ''
            '  (Enable the SB Dashboard at dashboard.html)'
            '\n'
            '--uc'
            ''
            '  (Enable undetected-chromedriver mode.)'
            '\n'
            '--incognito'
            ''
            '  (Enable Incognito mode.)'
            '\n'
            '--guest'
            ''
            '  (Enable Guest mode.)'
            '\n'
            '-m=MARKER'
            ''
            '  (Run tests with the specified pytest marker.)'
            '\n'
            '-n=NUM'
            ''
            '  (Multithread the tests using that many threads.)'
            '\n'
            '-v'
            ''
            '  (Verbose mode. Print the full names of each test run.)'
            '\n'
            '--html=report.html'
            ''
            '  (Create a detailed pytest-html report.)'
            '\n'
            '--collect-only / --co'
            ''
            '  (Only show discovered tests. No run.)'
            '\n'
            '--co -q'
            ''
            '  (Only show full names of discovered tests. No run.)'
            '\n'
            '-x'
            ''
            '  (Stop running tests after the first failure is reached.)'
            '\n'
            "
" ) self.add_slide( "

Common console scripts:


", code=( "sbase get [DRIVER] [OPTIONS] # Eg. chromedriver\n" "sbase methods # List common Python methods\n" "sbase options # List common pytest options\n" "sbase gui # Open the SB GUI for pytest\n" "sbase caseplans # Open the SB Case Plans App\n" "sbase mkdir [DIRECTORY] # Create a test directory\n" "sbase mkfile [FILE.py] # Create a test file\n" "sbase codegen [FILE.py] [OPTIONS] # Record a test\n" "sbase recorder # Open the SB Recorder App\n" "sbase mkpres # Create a Presentation boilerplate\n" "sbase mkchart # Create a Chart boilerplate\n" "sbase print [FILE] # Print file to console\n" "sbase translate [FILE.py] [OPTIONS] # Translate\n" "sbase extract-objects [SB_FILE.py] # Get objects\n" "sbase inject-objects [SB_FILE.py] # Swap selectors\n" "sbase objectify [SB_FILE.py] # Get & swap objects\n" "sbase revert-objects [SB_FILE.py] # Undo objectify\n" "sbase download server # Get Selenium Grid JAR file\n" "sbase grid-hub [start|stop] [OPTIONS] # Start Grid\n" "sbase grid-node [start|stop] --hub=[IP] # Add Node" ), ) self.add_slide( "

Live Demo Time!



" "

(Starting with raw Selenium, and evolving that into " "SeleniumBase.)

", image="https://seleniumbase.io/other/python_3d_logo.png", ) self.begin_presentation(filename="fundamentals.html") ================================================ FILE: examples/presenter/hacking_with_cdp.py ================================================ # https://www.youtube.com/watch?v=vt2zsdiNh3U from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class UCPresentationClass(BaseCase): def test_hacking_with_cdp(self): self.open("data:,") self.set_window_position(4, 40) self._output_file_saves = False self.create_presentation(theme="serif", transition="none") self.add_slide("

Press SPACE to begin!

\n") self.add_slide( "

Coming up on the Hacker Show...

\n" "
    \n" '' "
", ) self.add_slide( "

Coming up on the Hacker Show...

\n" "

    \n" "
  • Intercepting requests/responses/XHR with CDP." "

  • \n" "

    \n" "

    \n" "

    \n" "

    \n" "
", ) self.add_slide( "

Coming up on the Hacker Show...

\n" "

    \n" "
  • Intercepting requests/responses/XHR with CDP." "

  • \n" "
  • Modifying requests: CDP.Fetch.continueRequest." "

  • \n" "

    \n" "

    \n" "

    \n" "
", ) self.add_slide( "

Coming up on the Hacker Show...

\n" "

    \n" "
  • Intercepting requests/responses/XHR with CDP." "

  • \n" "
  • Modifying requests: CDP.Fetch.continueRequest." "

  • \n" "
  • Controlling browsers via remote-debugging-port" "

  • \n" "

    \n" "

    \n" "
", ) self.add_slide( "

Coming up on the Hacker Show...

\n" "

    \n" "
  • Intercepting requests/responses/XHR with CDP." "

  • \n" "
  • Modifying requests: CDP.Fetch.continueRequest." "

  • \n" "
  • Controlling browsers via remote-debugging-port" "

  • \n" "
  • Bypassing CAPTCHAs & anti-bot defenses." "

  • \n" "

    \n" "
", ) self.add_slide( "

Coming up on the Hacker Show...

\n" "

    \n" "
  • Intercepting requests/responses/XHR with CDP." "

  • \n" "
  • Modifying requests: CDP.Fetch.continueRequest." "

  • \n" "
  • Controlling browsers via remote-debugging-port" "

  • \n" "
  • Bypassing CAPTCHAs & anti-bot defenses." "

  • \n" "
  • And live demos of all the above... with Python!" "

  • \n" "
", ) self.add_slide( "

Get ready for some
serious hacking!

" ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '

The remote-debugging-port

' '' ) self.add_slide( "

Let's get to the fun part...

" '' ) self.begin_presentation(filename="uc_presentation.html") ================================================ FILE: examples/presenter/multi_uc.py ================================================ """Part of the UC presentation""" import pytest from random import randint from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc", "-n3") @pytest.mark.parametrize("", [[]] * 3) def test_multi_threaded(sb): url = "https://gitlab.com/users/sign_in" sb.driver.uc_open_with_reconnect(url, 3) sb.set_window_rect(randint(0, 755), randint(38, 403), 700, 500) try: sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.post_message("SeleniumBase wasn't detected", duration=4) sb._print("\n Success! Website did not detect Selenium! ") except Exception: sb.driver.uc_open_with_reconnect(url, 3) try: sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.post_message("SeleniumBase wasn't detected", duration=4) sb._print("\n Success! Website did not detect Selenium! ") except Exception: sb.fail('Selenium was detected! Try using: "pytest --uc"') ================================================ FILE: examples/presenter/my_presentation.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyPresenterClass(BaseCase): def test_presenter(self): self.create_presentation(theme="serif", transition="none") self.add_slide( "

Welcome


\n" "

Press the Right Arrow

" ) self.add_slide( "

SeleniumBase Presenter


\n" '' '' '' "

\n

Create presentations with Python

" ) self.add_slide( "

Make slides using HTML:


\n" '' '\n\n' "\n" '' "\n" '\n' "\n" "
Row ABCRow XYZ
Value ONEValue TWO
Value THREEValue FOUR

\n

(HTML table example)

" ) self.add_slide( "

Keyboard Shortcuts:

\n" '\n' "\n" "\n" "" "\n\n" "\n" "\n" "\n" "
KeyAction
=>Next Slide (N also works)
<=Previous Slide (P also works)
FFull Screen Mode
OOverview Mode Toggle
escExit Full Screen / Overview Mode
.Pause/Resume Toggle
spaceNext Slide (alternative)
" ) self.add_slide( "

Add images to slides:

", image="https://seleniumbase.github.io/other/seagulls.jpg", ) self.add_slide( "

Add code to slides:

", code=( "from seleniumbase import BaseCase\n" "BaseCase.main(__name__, __file__)\n\n" "class MyTestClass(BaseCase):\n" " def test_basics(self):\n" ' self.open("https://xkcd.com/353/")\n' ' self.assert_title("xkcd: Python")\n' " self.assert_element('img[alt=\"Python\"]')\n" " self.click('a[rel=\"license\"]')\n" ' self.assert_text("free to copy and reuse")\n' " self.go_back()\n" ' self.click_link("About")\n' ' self.assert_exact_text("xkcd.com", "h2")' ), ) self.add_slide( "

Highlight code in slides:

", code=( "from seleniumbase import BaseCase\n" "BaseCase.main(__name__, __file__)\n\n" "class MyTestClass(BaseCase):\n" " def test_basics(self):\n" ' self.open("https://xkcd.com/353/")\n' ' self.assert_title("xkcd: Python")\n' " self.assert_element('img[alt=\"Python\"]')\n" " self.click('a[rel=\"license\"]')\n" ' self.assert_text("free to copy and reuse")\n' " self.go_back()\n" ' self.click_link("About")\n' ' self.assert_exact_text("xkcd.com", "h2")' ), ) self.add_slide( "

Add iFrames to slides:

", iframe="https://seleniumbase.io/demo_page", ) self.add_slide( "

Getting started is easy:

", code=( "from seleniumbase import BaseCase\n" "BaseCase.main(__name__, __file__)\n\n" "class MyPresenterClass(BaseCase):\n" " def test_presenter(self):\n" ' self.create_presentation(theme="serif")\n' ' self.add_slide("Welcome to Presenter!")\n' " self.add_slide(\n" ' "Add code to slides:",\n' " code=(\n" ' "from seleniumbase import BaseCase\\n\\n"\n' ' "class MyPresenterClass(BaseCase):\\n\\n"\n' ' " def test_presenter(self):\\n"\n' ' " self.create_presentation()\\n"))\n' " self.begin_presentation(\n" ' filename="demo.html", show_notes=True)' ), ) self.add_slide( "

Include notes with slides:


", code=( 'self.add_slide("[Your HTML goes here]",\n' ' code="[Your software code goes here]",\n' ' content2="[Additional HTML goes here]",\n' ' notes="[Attached speaker notes go here]"\n' ' "[Note A! -- Note B! -- Note C! ]")' ), notes="

  • Note A!
  • Note B!
  • Note C!
  • Note D!

", content2="

(Notes can include HTML tags)

", ) self.add_slide( "

Multiple themes available:

", code=( 'self.create_presentation(theme="serif")\n\n' 'self.create_presentation(theme="sky")\n\n' 'self.create_presentation(theme="simple")\n\n' 'self.create_presentation(theme="white")\n\n' 'self.create_presentation(theme="moon")\n\n' 'self.create_presentation(theme="black")\n\n' 'self.create_presentation(theme="night")\n\n' 'self.create_presentation(theme="beige")\n\n' 'self.create_presentation(theme="league")' ), ) self.add_slide( "

The End

", image="https://seleniumbase.github.io/img/sb_logo_10.png", ) self.begin_presentation( filename="presenter.html", show_notes=True, interval=0 ) ================================================ FILE: examples/presenter/py_virtual_envs.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class PythonVirtualEnvs(BaseCase): def test_py_virtual_envs(self): self.create_presentation(theme="serif", transition="slide") self.add_slide( "

Python Virtual Environments:


\n" "

What, Why, and How



\n" "

Presented by Michael Mintz

\n" "

Granite State Code Camp - Sat, Nov 14, 2020

" ) self.add_slide( "

About me:

\n" "
    " "
  • I created the SeleniumBase framework.
  • " "
  • I'm currently the DevOps Lead at iboss.
  • " "
\n", image="https://seleniumbase.io/other/iboss_booth.png", ) self.add_slide( "

Topics & tools covered by this presentation:

" "

\n" "
    " "
  • Overview of Virtual Environments
  • " "
  • Python package management
  • " '
  • Python 3 "venv"
  • ' "
  • virtualenv / virtualenvwrapper
  • " '
  • pip / "pip install"
  • ' "
  • requirements.txt files
  • " "
  • setup.py files
  • " "
" ) self.add_slide( "

Topics & tools that are NOT covered here:


\n" "
    " '
  • "conda"
  • ' '
  • "pipenv"
  • ' '
  • "poetry"
  • ' '
  • "pipx"
  • ' "

" "

(Other Python package management tools)

" ) self.add_slide( "

What is a Python virtual environment?



\n" "

A Python virtual environment is a partitioned directory" " where a Python interpreter, libraries/packages, and scripts" " can be installed and isolated from those installed in other" " virtual environments or the global environment.

" ) self.add_slide( "

Why should we use Python virtual environments?" "



\n" "

We should use Python virtual environments because different" " Python projects can have conflicting Python dependencies that" " cannot coexist in the same env.

" ) self.add_slide( "

Why? - continued



\n" "

Example: Project A and Project B both depend on" " different versions of the same Python library!

" "

Therefore, installing the second project requirements" " would overwrite the first one, causing it to break.

", code=( "# Project A requirement:\n" "urllib3==1.25.3\n\n" "# Project B requirement:\n" "urllib3==1.26.2" ), ) self.add_slide( "

Why? - continued



\n" "

It is also possible that Project A and Project B" " require different versions of Python installed!

", code=( "# Project A requirement:\n" "Python-3.8\n\n" "# Project B requirement:\n" "Python-2.7" ), ) self.add_slide( "

How do we create and use Python virtual envs?" "



\n" "
There are tools/scripts we can use:

" "
    " '
  • The Python 3 "venv" command
  • ' "
  • virtualenv / virtualenvwrapper
  • " "
" ) self.add_slide( '

Python 3 "venv"



\n' '"venv" creates virtual environments in the location where run' " (generally with Python projects).", code=( "# Mac / Linux\n" "python3 -m venv ENV_NAME\n" "source ENV_NAME/bin/activate\n\n" "# Windows\n" "py -m venv ENV_NAME\n" "call ENV_NAME\\Scripts\\activate\n\n" '# (Type "deactivate" to leave a virtual environment.)' ), ) self.add_slide( '

"mkvirtualenv" (from virtualenvwrapper)


\n' '
"mkvirtualenv" creates virtual environments in one place' " (generally in your home directory).", code=( "# Mac / Linux\n" "python3 -m pip install virtualenvwrapper\n" "export WORKON_HOME=$HOME/.virtualenvs\n" "source `which virtualenvwrapper.sh`\n" "mkvirtualenv ENV_NAME\n\n" "# Windows\n" "py -m pip install virtualenvwrapper-win\n" "mkvirtualenv ENV_NAME\n\n" '# (Type "deactivate" to leave a virtual environment.)' ), ) self.add_slide( "

List of commands from virtualenvwrapper

" "

", code=( "# Create a virtual environment:\n" "mkvirtualenv ENV_NAME\n\n" "# Exit your virtual environment:\n" "deactivate\n\n" "# Re-enter a virtual environment:\n" "workon ENV_NAME\n\n" "# List all virtual environments:\n" "workon\n\n" "# Delete a virtual environment:\n" "rmvirtualenv ENV_NAME" ), ) self.add_slide( "

Determining if you are in a virtual env

" "

" "

When activated, the name of your virtual env" " will appear in parentheses on the left side of your" " command prompt.

", code=( "# Example of how it may look on a Windows machine:\n" "C:\\Users\\Michael\\github> mkvirtualenv my_env\n" "(my_env) C:\\Users\\Michael\\github>" ), ) self.add_slide( '

Installing packages with "pip install"



' "

Once you have created a Python virtual environment and are" " inside, it is now safe to install packages from PyPI," " setup.py files, and/or requirements.txt files.

\n", code=( "# Install a package from PyPI:\n" "pip install seleniumbase\n\n" "# Install packages from a folder with setup.py:\n" "pip install . # Normal installation\n" "pip install -e . # Editable install\n\n" "# Install packages from a requirements.txt file:\n" "pip install -r requirements.txt\n" ), ) self.add_slide( '

Other useful "pip" commands



', code=( "# See which Python packages are installed:\n" "pip list\n\n" "# See which installed Python packages are outdated:\n" "pip list --outdated\n\n" "# Create a requirements file from installed packages:\n" "pip freeze > my_requirements.txt" ), ) self.add_slide( "

Live Demo Time!



", image="https://seleniumbase.io/other/python_3d_logo.png", ) self.add_slide( "

The End. Questions?



\n" "

Where to find me:

" "" ) self.begin_presentation( filename="py_virtual_envs.html", show_notes=False, interval=0 ) ================================================ FILE: examples/presenter/uc_presentation.py ================================================ # https://www.youtube.com/watch?v=5dMFI3e85ig import os import subprocess from contextlib import suppress from seleniumbase import BaseCase from seleniumbase import SB BaseCase.main(__name__, __file__) class UCPresentationClass(BaseCase): def test_presentation(self): self.open("data:,") self._output_file_saves = False self.create_presentation(theme="beige", transition="fade") self.add_slide( "

A deep dive into undetectable automation, with:

" "
SeleniumBase UC Mode" " and undetected-chromedriver
", image="https://seleniumbase.io/cdn/img/uc_mode_phases_3.png", ) self.add_slide( '' ) self.add_slide( "

🔹 The Objective 🔹



" "

By the end of this presentation, you'll learn how to" " create bots that appear as humans to websites.


" "

(These bots won't get detected or blocked.)

" "

Here's a live demo of that...

" ) self.begin_presentation(filename="uc_presentation.html") self.get_new_driver(undetectable=True) url = "https://gitlab.com/users/sign_in" try: self.uc_open_with_reconnect(url, reconnect_time=3) try: self.assert_text("Username", '[for="user_login"]', timeout=3) self.post_message("SeleniumBase wasn't detected", duration=4) except Exception: self.uc_open_with_reconnect(url, reconnect_time=4) self.assert_text("Username", '[for="user_login"]', timeout=3) self.post_message("SeleniumBase wasn't detected", duration=4) finally: self.quit_extra_driver() if os.path.exists("multi_uc.py"): self.create_presentation(theme="beige", transition="fade") self.add_slide( "

🔹 There's a lot more to come! 🔹



" "

If one bot isn't enough, how about several?" "



" "

Here's a demo of multithreaded bots in parallel..." "

" ) self.begin_presentation(filename="uc_presentation.html") subprocess.Popen("pytest multi_uc.py --uc -n3", shell=True).wait() self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Not just an army of bots, but an army of bots
" "that look just like humans using web browsers.


" "

(That's how they weren't detected!)

" ) self.begin_presentation(filename="uc_presentation.html") self.create_presentation(theme="serif", transition="fade") self.add_slide( "If this is what you came here for, stick around
to" " learn how to do the things you just saw.
" "

You may find it easier to build a Selenium
" "bot than to navigate an obstacle course.

", image="https://seleniumbase.io/other/yeah_you_passed.png", ) self.add_slide( "

But first, you may be wondering who I am...

" "

Or maybe you're one of over one million people
" "that I've already helped on Stack Overview:

" '' ) self.add_slide( "

About the presenter (Michael Mintz):

\n" "
    \n" "
  • I created SeleniumBase (for Python).
  • \n" "
  • I lead the Automation Team at iboss.
  • \n" "
\n", image="https://seleniumbase.io/other/iboss_booth.png", ) self.add_slide( "

I've been doing video podcasts since 2012!

" "

(That's when I first co-hosted the
Marketing Update" " on HubSpot TV)

", image="https://seleniumbase.io/other/hub_tv.png", ) self.add_slide( "

I spoke at Selenium Conference 2023:

" "

(As the dedicated Python Selenium speaker)

", image="https://seleniumbase.io/other/me_se_conf.jpg", ) self.add_slide( "

Here's me with the creators
" "of Selenium / WebDriver:

", image="https://seleniumbase.io/other/selenium_builders.jpg", ) self.add_slide( "SeleniumBase Fun Fact:" "
The 1st SB GitHub issue was from a Tesla engineer:", image="https://seleniumbase.io/other/first_issue.png", ) self.add_slide( "

Now, let me explain how we got here...

" "

And by here, I mean a time when lots of companies" " have been building services to detect and block bots:

", image="https://seleniumbase.io/other/verify_human.png", ) self.add_slide( "

In the early days, there were few bots,
" "and those bots didn't look human at all.


" "

Those early bots were mostly innocent, and
" "most websites didn't care if they were around.

" ) self.add_slide( "

At some point, the number of bots grew by a lot...


" "

Many bots were programmed with bad intentions...


" "

And sometimes you couldn't tell apart the good bots" " from the bad ones until it was already too late...

", ) self.add_slide( "

Then came Google reCAPTCHA v1...

" "

Although intended as a defense against bots,
" " humans on a web browser also got hit by it.

", image="https://seleniumbase.io/other/recaptcha_v1.png", ) self.add_slide( "

Then Google made improvements:

" "

(reCAPTCHA v2 / reCAPTCHA v3)

", image="https://seleniumbase.io/other/recaptcha_v2a.png", ) self.add_slide( "

And that annoyed a lot of people...

", image="https://seleniumbase.io/other/recaptcha_v2b.png", ) self.add_slide( "

Then came the next iteration of anti-bot security:

" "

Cloudflare Turnstile CAPTCHA-replacement." "

That changed the game in many ways.

", image="https://seleniumbase.io/other/check_if_secure.png", ) self.add_slide( "For awhile, Cloudflare's Turnstile did a decent
" "job filtering out bot traffic from human traffic." "

Then undetected-chromedriver arrived:

", image="https://seleniumbase.io/other/undetected_ch.png", ) self.add_slide( '' ) self.add_slide( "

undetected-chromedriver was found to be
" "incredibly effective at getting past the Turnstile:

", image="https://seleniumbase.io/other/connection_secure.png", ) self.add_slide( "

The maintainer of undetected-chromedriver:

" "

He appears to be very busy with various projects.

" '' ) self.add_slide( "

The biggest challenge for undetected-chromedriver" " has been adapting to breaking changes caused by:

" "
    \n" "
  • New versions of Chrome.
  • \n" "
  • New versions of Selenium.
  • \n" "
  • New versions of Cloudflare.
  • \n" "


\n" "

Those can brake things until updates are released:

" '' ) self.add_slide( "

Thankfully, undetected-chromedriver has
" "supporters helping to figure out and fix things.

" '' ) self.add_slide( "

Sometimes all that help isn't enough...

" "

That's where seleniumbase UC Mode comes in:

" '' ) self.add_slide( "

SeleniumBase UC Mode is a modified fork of
" "undetected-chromedriver with multiple changes.

" "

UC Mode includes bug fixes and additional features, such as" " multithreading support via pytest-xdist.

" '' ) self.add_slide( "

UC Mode is one of many SeleniumBase modes:" "

\n
    \n" "
  • UC Mode (--uc / uc=True)
  • \n" "
  • Slow Mode (--slow)
  • \n" "
  • Demo Mode (--demo)
  • \n" '
  • Proxy Mode' ' (--proxy="h:p"/"u:p@h:p")
  • \n' "
  • Debug Mode " "(--pdb/--trace/--ftrace)
  • \n" "
  • Mobile Mode " "(--mobile / mobile=True)
  • \n" "
  • Recorder Mode " "(--rec / --rec-behave)
  • \n" "
  • Multithreaded Mode " "(pytest -n4 / -n8)
  • \n" "
\n" "

And more! (You can even combine modes!)

\n" ) self.add_slide( "

ℹ️: UC Mode is not enabled by default.
" " It must be activated by switching it on:

" "
    \n" "
  • --uc  " " (pytest command-line option)
  • \n" "
  • uc=True  " " (SB/driver manager formats)
  • \n" "




\n

" "Then websites can no longer detect chromedriver.

" ) self.add_slide( "

Here's an example script that uses UC Mode:

\n" "
(Note that SeleniumBase has driver methods" "
that aren't included with standard Selenium.)
\n" "

\n", code=( "from seleniumbase import Driver\n\n" "driver = Driver(uc=True)\n" "try:\n" ' driver.get("https://gitlab.com/users/sign_in")' '\n' " driver.sleep(4)\n" " # DO MORE STUFF\n" "finally:\n" " driver.quit()\n" ), ) self.add_slide( "

For reference, here's a script in a different
" "SeleniumBase format, with more methods:

" "
(Get ready for another live demo...)
", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' sb.get("seleniumbase.io/simple/login")\n' ' sb.type("#username", "demo_user")\n' ' sb.type("#password", "secret_pass")\n' ' sb.click(\'a:contains("Sign in")\')\n' ' sb.assert_exact_text("Welcome!", "h1")\n' ' sb.assert_element("img#image1")\n' ' sb.highlight("#image1")\n' ' sb.click_link("Sign out")\n' ' sb.assert_text("signed out", "#top_message")' "\n" ), ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True) as sb: sb.get("https://seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Was that too fast for you?

" "

Let's run that again in Demo Mode:

", code=( "from seleniumbase import SB\n\n" "with SB(uc=True, demo=True) as sb:\n" ' sb.get(' '"https://seleniumbase.io/simple/login")\n' ' sb.type("#username", "demo_user")\n' ' sb.type("#password", "secret_pass")\n' ' sb.click(\'a:contains("Sign in")\')\n' ' sb.assert_exact_text("Welcome!", "h1")\n' ' sb.assert_element("img#image1")\n' ' sb.highlight("#image1")\n' ' sb.click_link("Sign out")\n' ' sb.assert_text("signed out", "#top_message")\n' ), ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True, demo=True) as sb: sb.get("https://seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Note the differences between undetected-chromedriver and" " SeleniumBase UC Mode.


SeleniumBase UC Mode:" "

    \n" "
  • Has driver version-detection & management." "
  • \n" "
  • Allows mismatched browser/driver versions." "
  • \n" "
  • Changes the user agent to prevent detection." "
  • \n" "
  • Hides chromedriver from Chrome as needed." "
  • \n" "
  • Allows for multithreaded tests in parallel." "
  • \n" "
  • Adjusts configuration based on the environment." "
  • \n" "
  • Has options for proxy and proxy-with-auth." "
  • \n
\n" ) self.add_slide( "

Here's another UC Mode script:

" "

(Get ready for another live demo...)

" "

", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n\n" " ...\n" ), ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) self.create_presentation(theme="serif", transition="fade") self.add_slide( "

Now let's learn how UC Mode works in general.

" "

First, there are several things that need to happen for" " browsers to remain undetected from anti-bot services.

\n", image="https://seleniumbase.io/other/yeah_you_passed.png", ) self.add_slide( "Requirements for avoiding detection (UC Mode)
" "
    \n" "
  • Modify chromedriver to rename driver variables" " that appear in the Chrome DevTools console.
  • \n" "
  • Launch Chrome before attaching chromedriver.
    " "(Don't launch Chrome with chromedriver)
  • \n" "
  • Don't use Selenium-specific Chrome options." "
  • \n
  • If using headless Chrome, change" " HeadlessChrome to Chrome in the User Agent.
  • \n" "
  • If using a custom user_data_dir, don't let that" " folder be used with non-UC-Mode Chrome.
  • \n" "
  • Disconnect chromedriver briefly from Chrome before" " loading websites with detection services.
  • \n" "
" ) self.add_slide( "Requirements, continued... / Good news:
\n" "

Most of those things are already done automatically" " when using UC Mode with default settings.

\n" "

The part that's your responsibility, (if setting a" " custom user_data_dir), is making sure that" " the u_d_d is only used by UC Mode Chrome instances. If you" ' "cross the streams", UC Mode can be detected.

' "

(UC Mode takes care of the other requirements.)" "

" ) self.add_slide( "

With those things done, your bot can appear human.

\n" "
But if anyone looks too closely at what your bot does,
" 'it may raise suspicion, even if already marked "not a bot".
', image="https://seleniumbase.io/other/other_anti_bots.jpg", ) self.add_slide( "

There are additional methods that you can use" " to have a better experience when using UC Mode:

\n" "

Note that driver.get(url) has" " been modified from the original to
reconnect automatically" " if a web page is using bot-detection software.
" "

", code=( "driver.default_get(url)\n\n" "driver.uc_open(url)\n\n" "driver.uc_open_with_tab(url)\n\n" "driver.uc_open_with_reconnect(url, reconnect_time)\n\n" 'driver.uc_click(selector, by="css selector", timeout=7)\n' ), ) self.add_slide( "

There are additional methods that you can use" " to have a better experience when using UC Mode:

" "

Since driver.get(url) has" " been modified, driver.default_get(url)" " exists to do a regular get(url)," " which may be useful if revisiting a website.
" "

", code=( "driver.default_get(url)\n\n" "driver.uc_open(url)\n\n" "driver.uc_open_with_tab(url)\n\n" "driver.uc_open_with_reconnect(url, reconnect_time)\n\n" 'driver.uc_click(selector, by="css selector", timeout=7)\n' ), ) self.add_slide( "

There are additional methods that you can use" " to have a better experience when using UC Mode:

" "

driver.uc_open(url) will" " open a URL in the same tab with a disconnect.
" "(This might not be enough to bypass detection.)
" "

", code=( "driver.default_get(url)\n\n" "driver.uc_open(url)\n\n" "driver.uc_open_with_tab(url)\n\n" "driver.uc_open_with_reconnect(url, reconnect_time)\n\n" 'driver.uc_click(selector, by="css selector", timeout=7)\n' ), ) self.add_slide( "

There are additional methods that you can use" " to have a better experience when using UC Mode:

\n" "

driver.uc_open_with_tab(url)" " opens a URL in a new tab with a disconnect. Similar to the new" " driver.get(url), but without the pre-check." "


", code=( "driver.default_get(url)\n\n" "driver.uc_open(url)\n\n" "driver.uc_open_with_tab(url)\n\n" "driver.uc_open_with_reconnect(url, reconnect_time)\n\n" 'driver.uc_click(selector, by="css selector", timeout=7)\n' ), ) self.add_slide( "

There are additional methods that you can use" " to have a better experience when using UC Mode:

\n" "

driver.uc_open_with_tab(url) opens" " a URL in a new tab with a disconnect. Similar to the new" " driver.get(url), but without the pre-check." "


\n" "
As a reminder, the driver.get(url)" " pre-check checks to see if a URL has bot-detection software" " on it before opening the URL in a new tab with a disconnect." "
\n" "
This pre-check is done using" " requests.get(URL)
before opening" " a URL in the UC Mode web browser.
" "
If the response code is a" ' "403" (Forbidden),
' "then the URL is opened with a disconnect." "
" ) self.add_slide( "

Customizing the default disconnect/reconnect time

" "
\n" "

Here's a method for a custom reconnect time
" "when opening a page that tries to detect bots:

" "
driver.uc_open_with_reconnect(url, reconnect_time)"
            "
" "
(The default reconnect_time is slightly more than 2 seconds.)" "


", code=( "# Example:\n" "driver.uc_open_with_reconnect(\n" ' "https://steamdb.info/login/", reconnect_time=6\n)' "" "\n\n" "# Short form example:\n" "driver.uc_open_with_reconnect(" '"https://steamdb.info/login/", 6)\n' ), ) self.add_slide( "

Clicking with a disconnect/reconnect


\n" "

If your bot needs to click a button on a website that has" " anti-bot services, you might be able to do it with this special" " method, which forces a short disconnect:

\n" "
driver.uc_click(selector)
" '
(Defaults: by="css selector", timeout=7)
' "

\n", code=( "# Examples:\n" 'driver.uc_click("button")\n\n' 'driver.uc_click("button#id", timeout=10)\n' ), ) self.add_slide( "

Links to UC Mode code



\n", ) self.add_slide( "

Things to keep in mind


    \n" "
  • You may need to adjust default settings
    " "for your bot to remain undetected.

  • \n" "
  • Once your bot enters a website,
    " "it should continue to act accordingly.

  • \n" "
  • Improvise if your bot makes any mistakes.

  • \n" "
  • Your bot should look human to avoid detection.
\n", ) self.add_slide( "

Ethical concerns


    \n" "
  • Don't use bots for evil purposes.

  • \n" "
  • Do use bots with honorable intentions.

  • \n" "
  • Do use bots for automating tedious manual tasks.

  • \n" "
  • Do take the time to train & configure your bots.
\n", ) self.add_slide( "

🔹 Final remarks 🔹



    \n" "
  • Not all bots are created equal.

  • \n" "
  • SeleniumBase UC Mode lets bots appear human.

  • \n" "
  • Visit SeleniumBase on GitHub for more info:\n" "https://github.com/seleniumbase/SeleniumBase

\n", ) self.add_slide( "

❓ Questions? ❓

" "
https://github.com/seleniumbase/SeleniumBase/discussions
" "

📌 Found a bug? 🐞

" "https://github.com/seleniumbase/SeleniumBase/issues

" "

🔰 Perfection takes practice. Keep iterating! 🔰

" ) self.add_slide( "

The End

", image="https://seleniumbase.io/other/sb_github.png" ) self.begin_presentation(filename="uc_presentation.html") ================================================ FILE: examples/presenter/uc_presentation_3.py ================================================ # https://www.youtube.com/watch?v=-EpZlhGWo9k import sys from contextlib import suppress from seleniumbase import BaseCase from seleniumbase import SB BaseCase.main(__name__, __file__) class UCPresentationClass(BaseCase): def test_presentation_3(self): self.open("data:,") self.set_window_position(4, 40) self._output_file_saves = False self.open("https://seleniumbase.io/other/uc3_title.jpg") self.create_presentation(theme="serif", transition="fade") self.add_slide( '' ) self.begin_presentation(filename="uc_presentation.html") self.open("https://seleniumbase.io/other/uc3_title.jpg") self.sleep(2.5) self.create_presentation(theme="serif", transition="fade") self.add_slide( '' '

(with SeleniumBase UC Mode)

' ) self.add_slide( "

This is the follow-up to my previous video:

" '' ) self.add_slide( '' ) self.add_slide( "

Which was the follow-up to an earlier video:

" '' ) self.add_slide( '' ) self.add_slide( "
There, you learned the basics of bypassing CAPTCHAs:
" '' ) self.add_slide( "

Here's a LIVE DEMO of bypassing a CAPTCHA:

" ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) self.create_presentation(theme="serif", transition="none") self.add_slide( "

The code for the previous live demo:

" "

", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n\n" " ...\n" ), ) self.add_slide( "

The code for the previous live demo:

" "

", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n\n" ' sb.assert_text("Username", \'[for="user_login"]\',' ' timeout=3)\n' ' sb.assert_element(\'[for="user_login"]\')\n' ' sb.highlight(\'button:contains("Sign in")\')' '\n' ' sb.highlight(\'h1:contains("GitLab")\')' '\n' ' sb.post_message("SeleniumBase wasn\'t detected",' ' duration=4)\n' ), ) self.add_slide( "

Even if using UC Mode, you may still need to" " click the CAPTCHA checkbox in order to bypass it.

" "
" "

(That's not a problem because there are special
" "UC Mode methods for handling that situation.)
" ) self.add_slide( "

Special UC Mode methods for clicking CAPTCHAs:" "


" "

    \n" "
  • sb.uc_gui_handle_captcha()" "
  • \n" "PyAutoGUI uses the TAB key with SPACEBAR.

    \n\n" "
  • sb.uc_gui_click_captcha()" "
  • \n\n" "PyAutoGUI clicks CAPTCHA with the mouse.
    \n" "(Note that you'll need to use this one on Linux!)\n" "
\n\n\n\n" "


" ) self.add_slide( "

When is clicking the CAPTCHA checkbox required?" "


 
" "

  • They've seen your IP Address too many times." "

  • " "
  • They don't accept your User-Agent string." "
    (UC Mode gives you a good one by default)
  • " "
    " "
  • You're using Linux. (Likely a server)
  • " "
    " "
  • You're using a VPN. (If detected)
  • " "
    " ) self.add_slide( "

    For testing purposes...


    " "I'll use a bad User-Agent for some Live Demos..." "

    " "This will force me to click the CAPTCHA to bypass it." "" ) self.add_slide( "

    On the topic of live demos,

    " "

    I'll run some of them now:

    " "

    " "

    Get ready for a live demo of:

    " "

    Bypassing Cloudflare with TAB + SPACEBAR...

    " ) self.begin_presentation(filename="uc_presentation.html") agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0.0.0" if "linux" in sys.platform or "win32" in sys.platform: agent = None # Use the default UserAgent with suppress(Exception): with SB(uc=True, test=True, agent=agent) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.uc_gui_handle_captcha() # Only if needed sb.assert_element('label[for="user_login"]') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") self.create_presentation(theme="serif", transition="none") self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n" " sb.uc_gui_handle_captcha()\n\n" " ...\n\n\n\n\n\n" ), ) self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n" " sb.uc_gui_handle_captcha()\n\n" ' sb.assert_text("Username", \'[for="user_login"]\',' ' timeout=3)\n' ' sb.assert_element(\'[for="user_login"]\')\n' ' sb.set_messenger_theme(location="bottom_center")' '\n' ' sb.post_message("SeleniumBase wasn\'t detected!")' '' ), ) self.add_slide( "

    Live demos, continued...

    " "

    " "

    Get ready for a live demo of:

    " "

    Bypassing Cloudflare with a mouse click...

    " ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True, test=True, agent=agent) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.uc_gui_click_captcha() # Only if needed sb.assert_element('label[for="user_login"]') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") self.create_presentation(theme="serif", transition="none") self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n" " sb.uc_gui_click_captcha()\n\n" " ...\n\n\n\n\n" ), ) self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.uc_open_with_reconnect(url, 4)\n" " sb.uc_gui_click_captcha()\n\n" ' sb.assert_text("Username", \'[for="user_login"]\',' ' timeout=3)\n' ' sb.assert_element(\'[for="user_login"]\')\n' ' sb.set_messenger_theme(location="bottom_center")' '\n' ' sb.post_message("SeleniumBase wasn\'t detected!")' '\n' ), ) self.add_slide( "

    Quick recap of what you just learned:" "


    " "
      \n" "
    • Activate UC Mode with SB(uc=True)" "

    • \n" "
    • Navigate with stealth by calling " "sb.uc_open_with_reconnect(url)" "

    • \n" "
    • Use sb.uc_gui_handle_captcha()" " or sb.uc_gui_click_captcha()" " to bypass CAPTCHAs as needed.
    • \n" "
    \n" "


    (It's that easy!)


    \n" ) self.add_slide( "

    Things can get more complicated

    " "

    " "
      \n" "
    • Previous tutorials mentioned this method:
      " "sb.uc_click(selector)" "

    • \n" "Although this method can no longer click a
      " "CAPTCHA directly, it should be used when
      " "clicking on something else that causes a
      " "CAPTCHA to appear after that.
      \n" "

      " "
    • Here's a live demo of that...

    • \n" "
    \n" "



    \n" ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True, incognito=True, locale="en") as sb: url = "https://ahrefs.com/website-authority-checker" input_field = 'input[placeholder="Enter domain"]' submit_button = 'span:contains("Check Authority")' sb.uc_open_with_reconnect(url) # The bot-check is later sb.type(input_field, "github.com/seleniumbase/SeleniumBase") sb.reconnect(0.1) sb.uc_click(submit_button, reconnect_time=4) sb.uc_gui_click_captcha() sb.wait_for_text_not_visible("Checking", timeout=10) sb.highlight('p:contains(".com/seleniumbase/SeleniumBase")') sb.highlight('a:contains("Top 100 backlinks")') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") self.create_presentation(theme="serif", transition="none") self.add_slide( "

    The code for the previous live demo can be
    " "found in the SeleniumBase GitHub repo:

    " "github.com/seleniumbase/SeleniumBase
    " "



    " '(See the "examples" folder for all examples)

    \n' ) self.add_slide( "

    Live demos, continued...

    " "

    " "

    Get ready for another live demo of" " using the uc_click(selector) method" " to bypass a Cloudflare CAPTCHA on" " steamdb.info ...

    " ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True, test=True, disable_csp=True) as sb: url = "https://steamdb.info/" sb.uc_open_with_reconnect(url, 3) sb.uc_click("a.header-login span", 3) sb.uc_gui_click_captcha() sb.assert_text("Sign in", "button#js-sign-in", timeout=3) sb.uc_click("button#js-sign-in", 2) sb.highlight("div.page_content form") sb.highlight('button:contains("Sign in")', scroll=False) sb.set_messenger_theme(location="top_center") sb.post_message("SeleniumBase wasn't detected", duration=4) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True, disable_csp=True) as sb:\n" ' url = "https://steamdb.info/"\n' " sb.uc_open_with_reconnect(url, 3)\n" ' sb.uc_click("a.header-login span", 3)\n\n' " ...\n\n\n\n\n\n\n\n" ), ) self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://steamdb.info/"\n' " sb.uc_open_with_reconnect(url, 3)\n" ' sb.uc_click("a.header-login span", 3)\n\n' " sb.uc_gui_click_captcha()\n" ' sb.assert_text("Sign in", "button#js-sign-in",' ' timeout=3)\n' ' sb.uc_click("button#js-sign-in", 2)\n' ' sb.highlight("div.page_content form")\n' ' sb.highlight(\'button:contains("Sign in")\',' ' scroll=False)\n' ' sb.set_messenger_theme(location="top_center")' '\n' ' sb.post_message("SeleniumBase wasn\'t detected!")' '\n' ), ) self.add_slide( "

    👤 Important information 👤

    " "
    " "
      \n" "
    • UC Mode now requires PyAutoGUI for all" " features to work.
    • \n" "
    • PyAutoGUI may require enabling admin-level permissions" " for controlling the mouse and the keyboard.
    • \n" "

      " "
    • PyAutoGUI doesn't support Headless Mode." "
    • \n

      " "
    • UC Mode now includes a special virtual display" " on Linux so that you no longer need to use Headless Mode" " in GUI-less environments.
    • \n" "
    \n" "

    \n" ) self.add_slide( "

    👤 General information 👤

    " "

    " "

    Don't assume that all CAPTCHA services" " are secure, even if they say they are...

    " "



    " ) self.add_slide( "

    👤 General information 👤

    " "

    " "

    Don't assume that all CAPTCHA services" " are secure, even if they say they are...


    " "

    (Looking at you, Cloudflare!)


    " "

    \n" ) self.add_slide( "

    👤 General information 👤

    " "

    " "

    " "On the other hand,
    " "some CAPTCHA services are quite good..." "

    " "



    " ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/apps/recaptcha" sb.uc_open_with_reconnect(url) sb.uc_gui_click_captcha() # Try with PyAutoGUI Click sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected") self.create_presentation(theme="serif", transition="none") self.add_slide( "

    👤 General information 👤

    " "

    " "

    On the other hand,
    " "some CAPTCHA services are quite good..." "


    " "

    (Well done, Google reCAPTCHA!)


    " "

    \n" ) self.add_slide( "

    👤 General information 👤

    " "

    " "

    " "However, the real reason UC Mode is popular," " which you saw earlier," " is because of the Cloudflare-bypass capabilities." " That's where the reputation comes from..." "

    " "



    " ) self.add_slide( "

    👤 Catching up 👤

    " "

    " "

    " "If this is your first tutorial on UC Mode or SeleniumBase," " then here are some important
    things to know to" " understand things better..." "

    " "



    " ) self.add_slide( "

    👤 What is SeleniumBase? 👤

    " "

    " "

    " "SeleniumBase is a complete framework for web automation" " and testing with Python and Selenium." "

    " "Although there are many different features,
    " "the most popular one today is UC Mode,
    " "which enables Selenium browsers to appear
    " "as human-controlled browsers to websites." "

    " "
    " ) self.add_slide( "

    👤 Structuring Scripts / Tests 👤

    " "

    " "

    " "There are different ways of stucturing SeleniumBase scripts." ' (Internally called: "The 25 Syntax Formats")' "

    " 'Most examples use Syntax Format 1: "BaseCase direct class' ' inheritance", which uses the "pytest" test runner.' "

    " 'The next one in popularity is Syntax Format 21: "SeleniumBase SB"' ' (Python context manager)",
    which is ideal and recommended' " for UC Mode." "

    " "
    " ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "
    📊 The SeleniumBase GitHub Page 📊
    " '' ) self.add_slide( "

    About me: (Michael Mintz)

    \n" "
      \n" "
    • I created the SeleniumBase framework." "
    • \n" "
    • I lead the Automation Team at iboss." "
    • \n" "
    ", image="https://seleniumbase.io/other/iboss_booth.png", ) self.add_slide( "

    About me: (Michael Mintz)

    \n" "
      \n" "
    • I've reached over 2 million developers
      " " on Stack Overflow.
    • \n" "
    " '' ) self.add_slide( "

    👤 The Great CAPTCHA Duel: 👤

    " "

    " "

    " "Throughout the past few years, Cloudflare has pushed a lot" " of changes to their Turnstile CAPTCHA." "

    " "In order to keep UC Mode working, I had to push a lot of" " updates to counter those changes." "

    " "It has been an epic duel..." "

    " "
    " ) self.add_slide( "

    👤 The Great CAPTCHA Duel: 👤

    " "

    " "

    " "Sometimes Cloudflare pushed multiple
    " "changes at the same time..." "


    " "That's when I had to make multiple
    " "UC Mode updates to counter those changes..." "


    " "Sometimes I received a little assistance from GitHub..." "

    " "
    " ) self.add_slide( "

    👤 Timeline of major Cloudflare updates 👤

    " "

    " "

    " "March 20, 2024: Cloudflare pushed a major update" " where they could detect UC Mode Selenium clicks." "

    " "Outcome: UC Mode's uc_click(selector) method" " was updated to click on CAPTCHAs
    via JavaScript using" " window.setTimeout()." "


    " "
    " ) self.add_slide( "

    👤 Timeline of major Cloudflare updates 👤

    " "

    " "

    " "May 10, 2024: Cloudflare pushed a major update" " where CSS Selectors of CAPTCHAs were updated." "

    " "Outcome: I had to update all the UC Mode
    " 'examples to change "span.mark" to just "span".' "


    " "
    " ) self.add_slide( "

    👤 Timeline of major Cloudflare updates 👤

    " "

    " "

    " "June 7, 2024: Cloudflare pushed an update where CAPTCHAs" " could detect JavaScript clicks.
    (This was a major setback!)" "


    " "Outcome: I had to add new UC Mode methods for clicking on" " CAPTCHAs with PyAutoGUI." "

    " "
    " ) self.add_slide( "

    👤 Timeline of major Cloudflare updates 👤

    " "

    " "

    " "July 8, 2024: Cloudflare made an update where CAPTCHAs" " were hidden behind Shadow-DOM.
    " "(They went for a killing blow!)" "


    " "Outcome: I updated existing UC Mode methods so they could" " determine the CAPTCHA coordinates
    for" " PyAutoGUI." " (Same-day delivery, thanks to an advanced warning" " on Discord a few days earlier.)" "


    " "
    " ) self.add_slide( "

    👤 Timeline of major Cloudflare updates 👤

    " "

    " "

    " "July 25, 2024: Cloudflare made updates to the
    " "CSS Selectors that come before Shadow-DOM." "


    " "Outcome: I updated existing UC Mode methods." "

    " 'Note: You can use "uc_gui_handle_captcha()" or
    ' '"uc_gui_click_captcha()" for any CAPTCHA now.
    ' '(On Linux, only "uc_gui_click_captcha" works.)' "

    " "
    " ) self.add_slide( "

    👤 Timeline of major Cloudflare updates 👤

    " "

    " "

    " "Only minor changes from Cloudflare
    " "have been shipped since then so far..." "


    " "Remember: Give me space to work
    " "on UC Mode updates as needed..." "


    " "...because you never know when they'll strike next..." "

    " "
    " ) self.add_slide( "

    👤 Theories on how Cloudflare detects JS 👤

    " "

    " "

    " "A month before Cloudflare added JS-detection,
    " "a GitHub repo named Brotector was released." "


    " "Brotector is capable of detecting both Selenium & JS." "

    " "Based on experiements, Brotector's detection mechanisms" " appear to get the same results as Cloudflare's detection" " mechanisms.
    (It appears that Cloudflare learned from them.)" "

    " "
    " ) self.add_slide( "
    Brotector info
    " '' ) self.add_slide( "
    Brotector info
    " '' ) self.add_slide( "

    👤 Using Brotector to make UC Mode better 👤

    " "

    " "

    " "In order to make UC Mode better, I decided to build my own" " open-source CAPTCHA using Brotector:
    " '"The Brotector CAPTCHA"' "


    " "Unlike Cloudflare's detection system, which only scans for" " bots on page loads and CAPTCHA-clicks, the Brotector CAPTCHA" " continuously scans for bots." "

    " "This makes it a more powerful anti-bot system." "

    " "
    " ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "

    Here's a LIVE DEMO of Brotector CAPTCHA:

    " ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(test=True) as sb: url = "https://seleniumbase.io/hobbit/login" sb.open(url) sb.click_if_visible("button") sb.assert_text("Gandalf blocked you!", "h1") sb.click("img") sb.highlight("h1") sb.sleep(3) # Gandalf: "You Shall Not Pass!" self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Here's a LIVE DEMO of UC Mode bypassing" " Brotector CAPTCHA:

    " ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/hobbit/login" sb.uc_open_with_disconnect(url, 2.2) sb.uc_gui_press_keys("\t ") sb.reconnect(1.5) sb.assert_text("Welcome to Middle Earth!", "h1") sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") sb.click("img") sb.sleep(5.888) # Cool animation happening now! self.create_presentation(theme="serif", transition="none") self.add_slide( "

    👤 What happens when Cloudflare adds real-time
    " "bot-detection, like Brotector already has?
    👤

    " "

    " "

    " "Currently, UC Mode uses Selenium to locate the CAPTCHA checkbox" " before the PyAutoGUI click.
    (This is fine" " for now because CF only scans
    " "during page loads and CAPTCHA clicks.)" "


    " "There's already a plan in place for the day
    " "Cloudflare adds real-time bot-scanning..." "

    " "
    " ) self.add_slide( "

    👤 The plan to handle real-time bot-scanning 👤

    " "

    " "

    " '

    sb.uc_gui_click_captcha(frame="iframe", retry=False,'
                ' blind=True)

    ' 'Set the third arg, `blind`, to `True` to force a retry' ' (if the first click failed) by clicking at the last known' ' coordinates of the CAPTCHA checkbox without confirming first' ' with Selenium that a CAPTCHA is still on the page.' ' (The page will need to reload first.)' "

    " "
    " ) self.add_slide( "

    👤 Field trip to the UC Mode help docs 👤

    " "

    " "

    " "Let's take a look at the UC Mode docs
    " "from the SeleniumBase GitHub repo..." "

    " '' '' ) self.add_slide( "

    👤 Study, study, study! 👤

    " "

    " "

    " "There's lots of important information in the UC Mode docs," " so study well to avoid falling into traps..." "

    " "Sometimes you might still be able to
    " "get out of a trap you fell into..." "


    " "Once you bypass a CAPTCHA, be ready for anything!" "

    " "
    " ) self.add_slide( "

    👤 There's more to come 👤

    " "

    " "

    " "As usual, export more UC Mode updates,
    " "but new projects are classified until released." "

    " "
    " ) self.add_slide( "

    Questions?

    " "https://github.com/seleniumbase/SeleniumBase/discussions" "

    " "

    📌 Found a bug? 🐞

    " "https://github.com/seleniumbase/SeleniumBase/issues" "
    " ) self.add_slide( "

    📊 Final remarks 📣



    " "

    " "🛠️ SeleniumBase gives you 🛠️
    " "the tools you need to succeed!" "

    " "


    " "And tools to build lots of bots..." "


    " ) self.add_slide( "
    🏁 The End 🏁
    " '' ) self.begin_presentation(filename="uc_presentation.html") ================================================ FILE: examples/presenter/uc_presentation_4.py ================================================ # https://www.youtube.com/watch?v=Mr90iQmNsKM from contextlib import suppress from seleniumbase import BaseCase from seleniumbase import SB BaseCase.main(__name__, __file__) class UCPresentationClass(BaseCase): def test_presentation_4(self): self.open("data:,") self.set_window_position(4, 40) self._output_file_saves = False self.create_presentation(theme="serif", transition="fade") self.add_slide( '' ) self.add_slide( "

    This continues my Undetectable Automation series:

    " '' ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): self.open("https://www.bostoncodecamp.com/CC37/info") self.create_tour(theme="hopscotch") self.add_tour_step( "

    Good Afternoon and Welcome!

    ", 'h1.wow' ) self.add_tour_step( "

    PSA: Visit our sponsors later.

    ", '[href*="/Sponsors"]', ) self.add_tour_step( "

    Let's check out the schedule...

    ", '[href*="/Schedule/SessionGrid"]' ) self.play_tour() with suppress(Exception): self.open( "https://www.bostoncodecamp.com/CC37/Schedule/SessionGrid" ) self.highlight("h2", loops=8) if self.is_element_visible('[data-sessionid="765448"]'): self.highlight('div[data-sessionid="765448"]', loops=10) self.create_tour(theme="driverjs") self.add_tour_step( "

    Here we are

    ", '[data-sessionid="765448"]' ) self.play_tour() self.click('a[onclick*="765448"]') self.create_tour(theme="hopscotch") self.add_tour_step( "

    What to expect

    ", "div.sz-modal-session", alignment="left", ) self.play_tour() self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Last time...

    " ) self.add_slide( "

    Last time...

    " '' ) self.add_slide( "

    This time...

    " '' ) self.add_slide( "Note: There are different kinds of reCAPTCHA,
    " "and not all of them are created equal.
    " '' ) self.add_slide( "

    This is what happens when you fail reCAPTCHA:

    " '' ) self.add_slide( "

    This is what happens when you fail hCAPTCHA:

    " '' ) self.add_slide( "

    If you like puppies, hCAPTCHA has you covered:

    " '' ) self.add_slide( "

    This is what happens when some anti-bots detect you:

    " '' ) self.add_slide( "

    And this is what happens when Gandalf blocks you:

    " '' ) self.add_slide( "

    No joke... There's a Hobbit CAPTCHA

    " '' ) self.add_slide( "

    Important Notice:

    " "(Know the laws and legal implications!)" '' ) self.add_slide( "

    🔹 By the end of this presentation... 🔹



    " "✅ You'll learn which anti-bot systems work,
    " "and which ones don't. (Hint: Most don't work.)

    " "✅ There will be multiple live demos." "

    " "✅ You'll learn how to bypass weak defenses." "

    " "✅ You'll learn powerful web-scraping techniques." "

    " ) self.add_slide( "

    But first, a little about me...

    " '' ) self.add_slide( "

    About me: (Michael Mintz)

    \n" "
      \n" "
    • I created the SeleniumBase framework." "
    • \n" "
    • I lead the Automation Team at iboss." "
    • \n" "
    ", image="https://seleniumbase.io/other/iboss_booth.png", ) self.add_slide( "

    In my spare time,

    " "

    I may be found...

    " ) self.add_slide( "

    Spending time with entrepreneurs...

    " '' ) self.add_slide( "

    Spending time with celebrities...

    " '' ) self.add_slide( "

    Spending time with politicians...

    " '' ) self.add_slide( "

    Spending time with philanthropists...

    " '' ) self.add_slide( "

    Speaking at conferences...

    " '' ) self.add_slide( "

    Attending conferences as a guest...

    " '' ) self.add_slide( "

    Jet-skiing in Key West...

    " '' ) self.add_slide( "

    And working on SeleniumBase...

    " '' ) self.add_slide( "

    Enough about me...


    " "

    Let's begin the presentation!



    " ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' '' "
    " ) self.add_slide( '' ) self.add_slide( "Playwright using CDP" '' ) self.add_slide( "Selenium using CDP" '' ) self.add_slide( "

    Microsoft still supports Selenium,
    " "even though they have Playwright.

    " '' ) self.add_slide( '' ) self.add_slide( "As a birthday gift, BrightData invested a lot of money into " "Selenium (making them an official sponsor)." '' ) self.add_slide( "That's great news for the Selenium community!" '' ) self.add_slide( "Now, let's get back to CDP..." '' ) self.add_slide( "There are lots of GitHub repos using CDP.
    " "(This repo tracks some of them)" '' ) self.add_slide( "The first major Python implementation of CDP:" '' ) self.add_slide( '' ) self.add_slide( "PyCDP was the key ingredient to stealthy automation." '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "In addition to using CDP for controlling Chrome in a" " stealthy way, you can also achieve stealth by using" " tools that can control the mouse and keyboard.

    " "PyAutoGUI is one such tool:" '' ) self.add_slide( '' ) self.add_slide( "PyAutoGUI requires a headed browser to work.

    " " Since most Linux machines have headless displays that" " don't support headed browsers, an external tool called" " Xvfb must be used in order to simulate a headed browser" " in a headless Linux environment..." ) self.add_slide( '' ) self.add_slide( "

    To have a completely stealthy framework" " for clicking CAPTCHAs & bypassing anti-bot systems," " you need:


    " "

      \n" '
    • A framework that uses a "regular" browser
      ' '(to hide evidence of automation activity)' '
      ' "

    • \n" "
    • CDP capabilities for performing stealthy actions" "

    • \n" "
    • PyAutoGUI for performing tricky actions
      " "(eg. clicking Shadow-root CAPTCHAs)
      " "

    • \n" "
    • Xvfb integration for headless Linux systems" "
    • \n

    \n" ) self.add_slide( "SeleniumBase CDP Mode simplifies all that for you:

    " '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "List of sites with their invisible anti-bot services:" '' ) self.add_slide( "

    Let's get started with live demos of bypassing" " physical CAPTCHAs:

    " ) self.begin_presentation(filename="uc_presentation.html") self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up first...




    " "planetminecraft.com/account/sign_in/" "





    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True) as sb: url = "www.planetminecraft.com/account/sign_in/" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() sb.wait_for_element_absent("input[disabled]") sb.sleep(2) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "cloudflare.com/login" "





    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en") as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.sleep(3.5) sb.solve_captcha() sb.sleep(2.5) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "gitlab.com/users/sign_in" "





    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en") as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.activate_cdp_mode(url)\n" " sb.sleep(2)\n" " sb.solve_captcha()\n\n" " ...\n\n\n\n\n" ), ) self.add_slide( "

    The code for the previous live demo:

    " "

    ", code=( "from seleniumbase import SB\n\n" "with SB(uc=True) as sb:\n" ' url = "https://gitlab.com/users/sign_in"\n' " sb.activate_cdp_mode(url)\n" " sb.sleep(2)\n" " sb.solve_captcha()\n\n" ' sb.assert_text("Username", \'[for="user_login"]\',' ' timeout=3)\n' ' sb.assert_element(\'[for="user_login"]\')\n' ' sb.set_messenger_theme(location="bottom_center")' '\n' ' sb.post_message("SeleniumBase wasn\'t detected!")' '\n' ), ) self.add_slide( "

    Up next...




    " "bing.com/turing/captcha/challenge" "





    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True) as sb: url = "https://www.bing.com/turing/captcha/challenge" sb.activate_cdp_mode(url) sb.sleep(1) sb.solve_captcha() sb.sleep(2) self.create_presentation(theme="serif", transition="none") self.add_slide("

    Having fun yet?!?

    ") self.add_slide( "

    If you're not yet concerned about online security,
    " " then you probably need to see more live demos...

    ") self.add_slide( "

    Time for live demos of bypassing
    " "some invisible anti-bot services:

    " ) self.add_slide( "

    Up next...




    " "pokemon.com/us" "


    " "(Protected by Imperva / Incapsula)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.pokemon.com/us" sb.activate_cdp_mode(url) sb.sleep(1.5) sb.click_if_visible("button#onetrust-accept-btn-handler") sb.sleep(1.2) sb.click("a span.icon_pokeball") sb.sleep(2.5) sb.click('b:contains("Show Advanced Search")') sb.sleep(2.5) sb.click('span[data-type="type"][data-value="electric"]') sb.sleep(0.7) sb.scroll_into_view("a#advSearch") sb.sleep(0.7) sb.click("a#advSearch") sb.sleep(0.5) sb.cdp.click("a#advSearch") sb.sleep(1.2) sb.cdp.click('img[src*="img/pokedex/detail/025.png"]') sb.cdp.assert_text("Pikachu", 'div[class*="title"]') sb.cdp.assert_element('img[alt="Pikachu"]') sb.cdp.scroll_into_view("div.pokemon-ability-info") sb.sleep(1.2) sb.cdp.flash('div[class*="title"]') sb.cdp.flash('img[alt="Pikachu"]') sb.cdp.flash("div.pokemon-ability-info") name = sb.cdp.get_text("label.styled-select") info = sb.cdp.get_text("div.version-descriptions p.active") print("*** %s: ***\n* %s" % (name, info)) sb.sleep(2) sb.cdp.highlight_overlay("div.pokemon-ability-info") sb.sleep(2) sb.cdp.open("https://events.pokemon.com/EventLocator/") sb.sleep(2) sb.cdp.click('span:contains("Championship")') sb.sleep(2) events = sb.cdp.select_all("div.event-info__title") print("*** Pokémon Championship Events: ***") for event in events: print("* " + event.text) sb.sleep(2) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "walmart.com" "


    " "(Protected by Akamai + PerimeterX)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.walmart.com/" sb.activate_cdp_mode(url) sb.sleep(1.8) continue_button = 'button:contains("Continue shopping")' if sb.is_element_visible(continue_button): sb.cdp.gui_click_element(continue_button) sb.sleep(0.6) sb.click('input[aria-label="Search"]') sb.sleep(1.2) search = "Settlers of Catan Board Game" required_text = "Catan" sb.press_keys('input[aria-label="Search"]', search + "\n") sb.sleep(3.8) if sb.is_element_visible("#px-captcha"): sb.cdp.gui_click_and_hold("#px-captcha", 7.2) sb.sleep(3.2) if sb.is_element_visible("#px-captcha"): sb.cdp.gui_click_and_hold("#px-captcha", 4.2) sb.sleep(3.2) sb.remove_elements('[data-testid="skyline-ad"]') sb.remove_elements('[data-testid="sba-container"]') print('*** Walmart Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] sb.click_if_visible('[data-automation-id="sb-btn-close-mark"]') items = sb.find_elements('[data-item-id]') for item in items: if required_text in item.text: description = item.querySelector( '[data-automation-id="product-title"]' ) if ( description and description.text not in unique_item_text ): unique_item_text.append(description.text) print("* " + description.text) price = item.querySelector( '[data-automation-id="product-price"]' ) if price: price_text = price.text price_text = ( price_text.split("current price Now ")[-1] ) price_text = price_text.split("current price ")[-1] price_text = price_text.split(" ")[0] print(" (" + price_text + ")") self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "albertsons.com/recipes/" "


    " "(Protected by Imperva / Incapsula)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en") as sb: url = "https://www.albertsons.com/recipes/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.remove_element("div > div > article") sb.scroll_into_view('input[type="search"]') close_btn = ".notification-alert-wrapper__close-button" sb.click_if_visible(close_btn) sb.click("input#search-suggestion-input") sb.sleep(0.2) search = "Avocado Smoked Salmon" required_text = "Salmon" sb.press_keys("input#search-suggestion-input", search) sb.sleep(0.8) sb.click("#suggestion-0 a span") sb.sleep(0.8) sb.click_if_visible(close_btn) sb.sleep(3.2) print('*** Albertsons Search for "%s":' % search) print(' (Results must contain "%s".)' % required_text) unique_item_text = [] item_selector = 'a[href*="/meal-plans-recipes/shop/"]' items = sb.find_elements(item_selector) for item in items: sb.sleep(0.06) if required_text in item.text: item.flash(color="44CC88") sb.sleep(0.025) if item.text not in unique_item_text: unique_item_text.append(item.text) print("* " + item.text) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "easyjet.com/en/" "


    " "(Protected by Akamai)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.easyjet.com/en/" sb.activate_cdp_mode(url) sb.sleep(2) sb.click_if_visible("button#ensCloseBanner") sb.sleep(1.2) sb.click('input[name="from"]') sb.sleep(1.2) sb.type('input[name="from"]', "London Gatwick") sb.sleep(0.6) sb.click_if_visible("button#ensCloseBanner") sb.sleep(0.6) sb.click('span[data-testid="airport-name"]') sb.sleep(1.2) sb.type('input[name="to"]', "Paris") sb.sleep(1.2) sb.click('span[data-testid="airport-name"]') sb.sleep(1.2) sb.click('input[name="when"]') sb.sleep(1.2) sb.cdp.click( '[data-testid="month"]:last-of-type' ' [aria-disabled="false"]' ) sb.sleep(1.2) sb.click( '[data-testid="month"]:last-of-type' ' [aria-disabled="false"]' ) sb.sleep(1.2) sb.click('button[data-testid="submit"]') sb.sleep(3.5) sb.connect() sb.sleep(4.2) for window in sb.driver.window_handles: sb.switch_to_window(window) if "/buy/flights" in sb.get_current_url(): break sb.click_if_visible("button#ensCloseBanner") days = sb.find_elements('div[class*="FlightGridLayout_column"]') for day in days: if not day.text.strip(): continue print( "\n\n**** " + " ".join(day.text.split("\n")[0:2]) + " ****" ) fares = day.find_elements( "css selector", 'button[class*="flightDet"]' ) if not fares: print("No flights today!") for fare in fares: info = fare.text info = info.replace("LOWEST FARE\n", "") info = info.replace("\n", " ") print(info) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "hyatt.com" "


    " "(Protected by Kasada)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en", ad_block=True) as sb: url = "https://www.hyatt.com/" sb.activate_cdp_mode(url) sb.sleep(3.2) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(0.1) sb.click_if_visible("#onetrust-reject-all-handler") sb.sleep(1.2) location = "Anaheim, CA, USA" sb.type('input[id="search-term"]', location) sb.sleep(1.2) sb.click('li[data-js="suggestion"]') sb.sleep(1.2) sb.click("button.be-button-shop") sb.sleep(6) card_info = ( 'div[data-booking-status="BOOKABLE"] [class*="HotelCard_info"]' ) hotels = sb.select_all(card_info) destination_selector = 'span[class*="summary_destination"]' print("Hyatt Hotels in %s:" % location) print("(" + sb.get_text(destination_selector) + ")") if len(hotels) == 0: print("No availability over the selected dates!") for hotel in hotels: info = hotel.text.strip() if "Avg/Night" in info and not info.startswith("Rates from"): name = info.split(" (")[0] name = name.split(" + ")[0].split(" Award Cat")[0] name = name.split(" Rates from :")[0] price = "?" if "Rates from : " in info: price = info.split("Rates from : ")[1] price = price.split(" Avg/Night")[0] print("* %s => %s" % (name, price)) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "bestwestern.com/en_US.html" "


    " "(Protected by DataDome)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en", guest=True) as sb: url = "https://www.bestwestern.com/en_US.html" sb.activate_cdp_mode(url) sb.sleep(3) sb.click_if_visible(".onetrust-close-btn-handler") sb.sleep(1) sb.click("input#destination-input") sb.sleep(2) location = "Palm Springs, CA, USA" sb.press_keys("input#destination-input", location) sb.sleep(1) sb.click("ul#google-suggestions li") sb.sleep(1) sb.click("button#btn-modify-stay-update") sb.sleep(4) sb.click("label#available-label") sb.sleep(2.5) print("Best Western Hotels in %s:" % location) summary_details = sb.get_text("#summary-details-column") dates = summary_details.split("DESTINATION")[-1] dates = dates.split(" CHECK-OUT")[0].strip() + " CHECK-OUT" dates = dates.replace(" ", " ") print("(Dates: %s)" % dates) flip_cards = sb.select_all(".flipCard") for i, flip_card in enumerate(flip_cards): hotel = flip_card.query_selector(".hotelName") price = flip_card.query_selector(".priceSection") if hotel and price: print("* %s: %s => %s" % ( i + 1, hotel.text.strip(), price.text.strip()) ) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "priceline.com" "


    " "(Protected by DataDome)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en", guest=True, pls="none") as sb: url = "https://www.priceline.com" sb.activate_cdp_mode(url) sb.sleep(2.6) input_selector = '[name="endLocation"]' if not sb.is_element_present(input_selector): input_selector = "div.location-input input" sb.click(input_selector) sb.sleep(1.2) location = "Portland, OR" selection = "Oregon, United States" # (Dropdown option) sb.press_keys(input_selector, location) sb.sleep(0.6) sb.click(selection) sb.sleep(0.4) sb.scroll_down(25) sb.sleep(0.4) calendar_close = 'button[aria-label="Dismiss calendar"]' if not sb.is_element_visible(calendar_close): calendar_close = '[data-mode="range"] span.px-1' sb.click(calendar_close) sb.sleep(0.6) sb.click('form button[type="submit"]') sb.sleep(4.8) if len(sb.cdp.get_tabs()) > 1: sb.cdp.close_active_tab() sb.cdp.switch_to_newest_tab() sb.sleep(0.6) sb.sleep(0.8) for y in range(1, 9): sb.scroll_to_y(y * 400) sb.sleep(0.5) hotel_names = sb.find_elements('h3 div[class*="TitleName"]') if not hotel_names: hotel_names = sb.find_elements( 'a[data-autobot-element-id*="HOTEL_NAME"]' ) price_selector = '[class*="PriceWrap"] .relative > .items-center' if sb.is_element_visible(price_selector): hotel_prices = sb.find_elements(price_selector) elif sb.is_element_present( '[font-size="12px"] + [font-size="20px"]' ): hotel_prices = sb.find_elements( '[font-size="12px"] + [font-size="20px"]' ) else: hotel_prices = sb.find_elements( 'span.text-priceSuper-heading4 + div > span' ) print("Priceline Hotels in %s:" % location) print(sb.get_text('[data-testid="POPOVER-DATE-PICKER"]')) if len(hotel_names) == 0: print("No availability over the selected dates!") count = 0 for i, hotel in enumerate(hotel_names): if hotel_prices[i] and hotel_prices[i].text: count += 1 hotel_price = "$" + hotel_prices[i].text if hotel_price.startswith("$$ "): hotel_price = hotel_price.replace("$$ ", "$") print("* %s: %s => %s" % (count, hotel.text, hotel_price)) self.create_presentation(theme="serif", transition="none") self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "

    Up next...




    " "nike.com" "


    " "(Protected by Shape Security)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en", pls="none") as sb: url = "https://www.nike.com/" sb.activate_cdp_mode(url) sb.sleep(2.5) sb.click('[data-testid="user-tools-container"] search') sb.sleep(1.5) search = "Nike Air Force 1" sb.press_keys('input[type="search"]', search) sb.sleep(4) details = 'ul[data-testid*="products"] figure .details' elements = sb.select_all(details) if elements: print('**** Found results for "%s": ****' % search) for element in elements: print("* " + element.text) sb.sleep(2) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    Up next...




    " "nordstrom.com" "


    " "(Protected by Shape Security)" "


    " ) self.begin_presentation(filename="uc_presentation.html") with SB(uc=True, test=True, locale="en") as sb: url = "https://www.nordstrom.com/" sb.activate_cdp_mode(url) sb.sleep(2.2) sb.click("input#keyword-search-input") sb.sleep(0.8) search = "cocktail dresses for women teal" sb.press_keys("input#keyword-search-input", search + "\n") sb.sleep(2.2) for i in range(17): sb.scroll_down(16) sb.sleep(0.14) print('*** Nordstrom Search for "%s":' % search) unique_item_text = [] items = sb.find_elements("article") for item in items: description = item.querySelector("article h3") if description and description.text not in unique_item_text: unique_item_text.append(description.text) price_text = "" price = item.querySelector( 'div div span[aria-hidden="true"]' ) if price: price_text = price.text print("* %s (%s)" % (description.text, price_text)) self.create_presentation(theme="serif", transition="none") self.add_slide( "

    CDP is powerful, as you can see.

    " "

    (Especially when used for stealth!)

    " '' ) self.add_slide( "

    Out of the following 9 anti-bot defense systems...

    " '' ) self.add_slide( "

    These are weak: (Can't detect stealthy CDP)

    " '' ) self.add_slide( "

    And these are strong: (CDP is detected)

    " '' ) self.add_slide( "

    What is Microsoft's stance
    on stealthy CDP?
    " "



    Officially...

    " ) self.add_slide( '' ) self.add_slide( "

    What is Microsoft's stance
    on stealthy CDP?

    " "

    Unofficially...

    " ) self.add_slide( "

    There are external repos
    using Playwright for stealth." "



    " "And Microsoft employees are
    endorsing them via GitHub Stars." "

    " ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "And steathy CDP works well in GitHub Actions." '' ) self.add_slide( "Why does stealthy CDP work in GitHub Actions,
    " "but not in other kinds of services like AWS?" ) self.add_slide( "

    Answer:

    " 'GitHub Actions runs in a
    ' '"residential IP address" space!' "

    " ) self.add_slide( '' ) self.add_slide( "

    People can use residential proxies
    " "to get a residential IP address.

    " ) self.add_slide( "

    Legal info:

    " '' ) self.add_slide("

    To summarize that...

    ") self.add_slide( "

    Scraping public data is probably legal.
    " "(Think Light Side)

    " '' ) self.add_slide( "

    Scraping private data is probably NOT legal.
    " "(Think Dark Side)

    " '' ) self.add_slide( "

    If you break local and/or international laws,
    " "then bounty hunters may come after you.

    " '' ) self.add_slide( "

    Let's get back to SeleniumBase

    " '' ) self.add_slide( "

    SeleniumBase includes a special Chrome extension:

    " '

    The "Recorder"

    ' "

    (You can generate complete scripts with it.)

    " "

    sbase recorder --uc

    " '' ) self.add_slide( '' ) self.begin_presentation(filename="uc_presentation.html") # import sys # from seleniumbase.console_scripts import sb_recorder # sys.argv.append("--uc") # sb_recorder.main() self.create_presentation(theme="serif", transition="none") self.add_slide( "

    How does one make an automation Recorder?

    " '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "And that's the secret to building a test recorder!" '' ) self.add_slide( "Also note that there are more stealth CDP repos
    " "other than the ones that you have already seen." '' ) self.add_slide( "

    👤 Field trip to the CDP Mode help docs 👤

    " "

    " "

    Let's take a look at the CDP Mode docs
    " "from the SeleniumBase GitHub repo...

    " '' '' ) self.add_slide( "

    Questions?

    " "https://github.com/seleniumbase/SeleniumBase/discussions" "

    " "

    📌 Found a bug? 🐞

    " "https://github.com/seleniumbase/SeleniumBase/issues" "
    " ) self.add_slide( "

    📊 Final remarks 📣



    " "

    🛠️ SeleniumBase gives you 🛠️
    " "the tools you need to succeed!" "


    " "And tools to build lots of bots..." "


    " ) self.add_slide( "
    🏁 The End 🏁
    " '' ) self.begin_presentation(filename="uc_presentation.html") ================================================ FILE: examples/presenter/web_scraping_on_gha.py ================================================ from contextlib import suppress from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class UCPresentationClass(BaseCase): def test_hacking_with_cdp(self): self.open("data:,") self.set_window_position(4, 40) self._output_file_saves = False self.create_presentation(theme="serif", transition="none") self.add_slide("

    Press SPACE to continue!

    \n") self.add_slide( "

    Before we begin


    " "

    (Here's the GitHub page)

    ", image="https://seleniumbase.io/other/sbase_qr_code.png", ) self.begin_presentation(filename="uc_presentation.html") with suppress(Exception): self.open("https://www.bostoncodecamp.com/CC38/info") self.create_tour(theme="hopscotch") self.add_tour_step( "

    Good Afternoon and Welcome!

    ", 'h1.wow' ) self.add_tour_step( "

    PSA: Visit our sponsors later.

    ", '[href*="/Sponsors"]', ) self.add_tour_step( "

    Let's check out the schedule...

    ", '[href*="/Schedule/SessionGrid"]' ) self.play_tour() with suppress(Exception): self.open( "https://www.bostoncodecamp.com/CC38/Schedule/SessionGrid" ) self.highlight("h2", loops=8) if self.is_element_visible('[data-sessionid="869465"]'): self.highlight( 'div[data-sessionid="869465"]', loops=10, scroll=False ) self.create_tour(theme="driverjs") self.add_tour_step( "

    Here we are

    ", '[data-sessionid="869465"]' ) self.play_tour() self.click('a[onclick*="869465"]') self.create_tour(theme="hopscotch") self.add_tour_step( "

    What to expect

    ", "div.sz-modal-session", alignment="left", ) self.play_tour() self.create_presentation(theme="serif", transition="none") self.add_slide("

    Press SPACE to begin!

    \n") self.add_slide( "

    Coming up... on the Hacker Show:

    \n" "
      \n" '
    \n' "

    \n" "

    \n", ) self.add_slide( "

    Coming up... on the Hacker Show:

    \n" "

      \n" "
    • Unlimited free web-scraping w/ GitHub Actions \n" "
    • \n" "
    " '', ) self.add_slide( "

    Coming up... on the Hacker Show:

    \n" "

      \n" "
    • Unlimited free web-scraping w/ GitHub Actions " "

    • \n" "
    • Using GitHub Secrets to hide within open-source" "
    \n" '\n' "

    \n" "

    \n", ) self.add_slide( "

    Coming up... on the Hacker Show:

    \n" "

      \n" "
    • Unlimited free web-scraping w/ GitHub Actions " "

    • \n" "
    • Using GitHub Secrets to hide within open-source" "

    • \n" "
    • Launching your own, free, local proxy server" "
    \n" '' "

    \n" ) self.add_slide( "

    Coming up... on the Hacker Show:

    \n" "

      \n" "
    • Unlimited free web-scraping w/ GitHub Actions " "

    • \n" "
    • Using GitHub Secrets to hide within open-source" "

    • \n" "
    • Launching your own, free, local proxy server" "

    • \n" '
    • Using "iptables" to make a proxy server public' "
    \n" '' "

    \n", ) self.add_slide( "

    Coming up... on the Hacker Show:

    \n" "

      \n" "
    • Unlimited free web-scraping w/ GitHub Actions " "

    • \n" "
    • Using GitHub Secrets to hide within open-source" "

    • \n" "
    • Launching your own, free, local proxy server" "

    • \n" '
    • Using "iptables" to make a proxy server public' "

    • \n" "
    • And multiple live demos after the previews" "

    • \n" "
    ", ) self.add_slide( "

    Get ready for some serious hacking!

    " '' ) self.add_slide( '' ) self.add_slide( "

    And YES, that means bypassing bot-detection!" '' ) self.add_slide( "

    But first, a little bit about me...

    " '' ) self.add_slide( "

    About me: (Michael Mintz)

    \n" "
      \n" "
    • I created the SeleniumBase framework." "
    • \n" "
    • And I lead the Automation Team at iboss." "
    • \n" "
    " '' ) self.add_slide( "

    Fun Fact


    \n" "

    I once showed SeleniumBase to Sam Altman at MIT.
    " "(Sam Altman cofounded OpenAI with Elon Musk.)" '' ) self.add_slide( "

    Recently, SeleniumBase was trending on GitHub:" "

    \n" '' ) self.add_slide( "

    The recent popularity can be attributed to CDP Mode," "
    which provides advanced stealth during automation.

    " '' ) self.add_slide( "

    That stealth is enough to bypass bot-detection
    " "while web-scraping from GitHub Actions:

    " '' ) self.add_slide( "

    GitHub Actions is free for public repositories:

    " '' ) self.add_slide( '' ) self.add_slide( "

    To hide sensitive information while using" "
    GitHub Actions for open-source projects," "
    there's a feature called: GitHub Secrets.

    " "
    " "

    That removes the limitation" r"
    of being 100% open-source," r"
    while still being 100% free.

    " ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( '' ) self.add_slide( "

    And that's the secret
    to GitHub Secrets!

    " ) self.add_slide( "

    Up next:

    " "
    " "

    Instant proxy server

    " "
    " "

    (Faster to launch than making Instant Coffee!)" ) self.add_slide( '

    "sbase proxy"

    ' "
    " "

    (That's it!)

    " "
    " '' ) self.add_slide( '

    More configuration options for "sbase proxy":

    ' '' ) self.add_slide( "

    The proxy server code comes from proxy.py:

    " '' ) self.add_slide( "

    " "Here's how to configure a proxy with SeleniumBase:" "



      \n" '
    • Proxy Mode via pytest:' '
      pytest --proxy="host:port"' '
      pytest --proxy="user:pass@host:port"' '

    • \n' '
    • Proxy Mode via SB() manager:' '
      SB(proxy="host:port")' '
      SB(proxy="user:pass@host:port")' '
    • \n' ) self.add_slide( "

      That's the secret to
      instant proxy servers!

      " "

      (And how to use them with SeleniumBase)

      " ) self.add_slide( "

      How about opening up a" "
      proxy server to the world?


      " ) self.add_slide( "

      For that, there's iptables


      " '' ) self.add_slide( '' ) self.add_slide( "

      And that's the secret to
      making a server public!

      " '' ) self.add_slide( "

      Let's move on to
      some live demos

      " '' ) self.add_slide( "

      Live Demo Time!


      " "

      (Let's head over to GitHub...)

      ", image="https://seleniumbase.io/other/sbase_qr_code.png", ) self.begin_presentation(filename="uc_presentation.html") ================================================ FILE: examples/proxy_test.py ================================================ from seleniumbase.config import settings from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ProxyTests(BaseCase): def test_proxy(self): if self.headless or self.recorder_mode or self.browser == "safari": self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") settings.SKIP_JS_WAITS = True self.open("https://api.ipify.org/") ip_address = self.get_text("body") if "ERR" in ip_address: raise Exception("Failed to determine IP Address!") print("\n\nMy IP Address = %s\n" % ip_address) self.open("https://ipinfo.io/%s" % ip_address) self.sleep(2) self.wait_for_text(ip_address, "h1", timeout=20) self.wait_for_element_present('[href="/signup"]') self.wait_for_text("Hosted domains", timeout=20) self.highlight("h1") pop_up = '[role="dialog"] span.cursor-pointer' self.click_if_visible(pop_up) self.highlight("#block-summary") self.click_if_visible(pop_up) self.highlight("#block-geolocation") self.click_if_visible(pop_up) self.sleep(2) print("Displaying Host Info:") text = self.get_text("#block-summary").split("Hosted domains")[0] rows = text.split("\n") data = [] for row in rows: if row.strip() != "": data.append(row.strip()) print("\n".join(data).replace('\n"', ' "')) print("\nDisplaying GeoLocation Info:") text = self.get_text("#block-geolocation") text = text.split("IP Geolocation data")[0] rows = text.split("\n") data = [] for row in rows: if row.strip() != "": data.append(row.strip()) print("\n".join(data).replace('\n"', ' "')) self.click_if_visible(pop_up) self.sleep(3) ================================================ FILE: examples/pytest.ini ================================================ [pytest] # Display console output. Disable cacheprovider: addopts = --capture=tee-sys -p no:cacheprovider # Skip these directories during test collection: norecursedirs = .* build dist recordings temp assets # Ignore DeprecationWarning, PytestUnknownMarkWarning filterwarnings = ignore::pytest.PytestWarning ignore:.*U.*mode is deprecated:DeprecationWarning # Configure the junit_family option explicitly: junit_family = legacy # Set pytest discovery rules: # (Most of the rules here are similar to the default rules.) # (Inheriting unittest.TestCase could override these rules.) python_files = test_*.py *_test.py *_tests.py *_suite.py python_classes = Test* *Test* *Test *Tests *Suite python_functions = test_* # Common pytest markers used in examples: # (pytest may require marker registration to prevent warnings.) # (Future versions may turn those marker warnings into errors.) markers = marker1: custom marker marker2: custom marker marker3: custom marker marker_test_suite: custom marker expected_failure: custom marker local: custom marker remote: custom marker offline: custom marker develop: custom marker qa: custom marker ci: custom marker e2e: custom marker ready: custom marker smoke: custom marker deploy: custom marker active: custom marker master: custom marker release: custom marker staging: custom marker production: custom marker ================================================ FILE: examples/rate_limiting_test.py ================================================ """This test demonstrates the use of the "rate_limited" decorator. You can use this decorator on any method to rate-limit it.""" from seleniumbase import BaseCase from seleniumbase import decorators class RateLimitingTests(BaseCase): @decorators.rate_limited(4.2) # The arg is max calls per second def print_item(self, item): print(item) def test_rate_limited_printing(self): if self._multithreaded or self.recorder_mode: self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") message = "Running rate-limited print() on the command line" self.open("data:text/html,

      %s

      " % message) print("\n%s:" % message) for item in range(1, 11): self.print_item(item) ================================================ FILE: examples/raw_ahrefs.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True, locale="en") as sb: url = "https://ahrefs.com/website-authority-checker" input_field = 'input[placeholder="Enter domain"]' submit_button = 'span:contains("Check Authority")' sb.activate_cdp_mode(url) sb.type(input_field, "github.com/seleniumbase/SeleniumBase") sb.click(submit_button) sb.sleep(2) sb.solve_captcha() sb.wait_for_text_not_visible("Checking", timeout=15) sb.click_if_visible('button[data-cky-tag="close-button"]') sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")') sb.highlight('a:contains("Top 100 backlinks")') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") ================================================ FILE: examples/raw_antibot_login.py ================================================ """UC Mode has PyAutoGUI methods for CAPTCHA-bypass.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/antibot/login" sb.uc_open_with_disconnect(url, 2.15) sb.uc_gui_write("\t" + "demo_user") sb.uc_gui_write("\t" + "secret_pass") sb.uc_gui_press_keys("\t" + " ") # For Single-char keys sb.sleep(1.5) sb.uc_gui_press_keys(["\t", "ENTER"]) # Multi-char keys sb.reconnect(1.8) sb.assert_text("Welcome!", "h1") sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") ================================================ FILE: examples/raw_bing_captcha.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://www.bing.com/turing/captcha/challenge" sb.activate_cdp_mode(url) sb.sleep(1) sb.solve_captcha() sb.sleep(2) ================================================ FILE: examples/raw_block.py ================================================ """If Brotector catches you, Gandalf blocks you!""" from seleniumbase import SB with SB(test=True) as sb: url = "https://seleniumbase.io/hobbit/login" sb.open(url) sb.click_if_visible("button") sb.assert_text("Gandalf blocked you!", "h1") sb.click("img") sb.highlight("h1") sb.sleep(3) # Gandalf: "You Shall Not Pass!" ================================================ FILE: examples/raw_brotector_captcha.py ================================================ """UC Mode has PyAutoGUI methods for CAPTCHA-bypass.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/apps/brotector" sb.uc_open_with_disconnect(url, 2.2) sb.uc_gui_press_key("\t") sb.uc_gui_press_key(" ") sb.reconnect(2.2) ================================================ FILE: examples/raw_call.py ================================================ """Can run with "python" instead of using "pytest" directly. Usage: "python raw_call.py". Two examples: pytest.main() and subprocess.call().""" import pytest import subprocess if __name__ == "__main__": pytest.main(["test_coffee_cart.py", "--chrome", "-v"]) subprocess.call(["pytest", "test_mfa_login.py", "--chrome", "-v"]) ================================================ FILE: examples/raw_cdp_drivers.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url1 = "https://seleniumbase.io/demo_page" sb.activate_cdp_mode(url1) driver1 = sb.driver url2 = "https://seleniumbase.io/coffee/" driver2 = sb.get_new_driver(undetectable=True) sb.activate_cdp_mode(url2) print(driver1.get_current_url()) print(driver2.get_current_url()) sb.switch_to_default_driver() sb.assert_url_contains("demo_page") print(sb.get_current_url()) sb.switch_to_driver(driver2) sb.assert_url_contains("coffee") print(sb.get_current_url()) ================================================ FILE: examples/raw_cdp_logging.py ================================================ from rich.pretty import pprint from seleniumbase import Driver driver = Driver(uc=True, log_cdp=True) try: url = "seleniumbase.io/apps/turnstile" driver.uc_open_with_reconnect(url, 2) driver.uc_gui_handle_captcha() driver.sleep(2) pprint(driver.get_log("performance")) finally: driver.quit() ================================================ FILE: examples/raw_cf.py ================================================ """SB Manager using CDP Mode for bypassing CAPTCHAs.""" from seleniumbase import SB with SB(uc=True, test=True, guest=True) as sb: url = "https://www.cloudflare.com/login" sb.activate_cdp_mode(url) sb.wait_for_element('div[data-testid*="challenge-widget"]') sb.sleep(1.5) sb.solve_captcha() sb.sleep(3) ================================================ FILE: examples/raw_cookies.py ================================================ """A SeleniumBase test that loads cookies to bypass login.""" from seleniumbase import SB # Log in to Swag Labs and save cookies with SB(test=True) as sb: sb.open("https://www.saucedemo.com") sb.wait_for_element("div.login_logo") sb.type("#user-name", "standard_user") sb.type("#password", "secret_sauce") sb.click('input[type="submit"]') sb.highlight("div.inventory_list", loops=6) sb.save_cookies(name="cookies.txt") # Load previously saved cookies to bypass login with SB(test=True) as sb: sb.open("https://www.saucedemo.com") sb.load_cookies(name="cookies.txt") sb.open("https://www.saucedemo.com/inventory.html") sb.highlight("div.inventory_list", loops=12) ================================================ FILE: examples/raw_detection.py ================================================ """The Brotector CAPTCHA in action.""" from seleniumbase import SB with SB(test=True) as sb: sb.open("https://seleniumbase.io/antibot/login") sb.highlight("h4", loops=6) sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click_if_visible("button span") sb.highlight("label#pText") sb.highlight("table#detections") sb.sleep(4.4) # Add time to read the table ================================================ FILE: examples/raw_driver_context.py ================================================ """DriverContext() example. (Runs with "python").""" from seleniumbase import DriverContext with DriverContext() as driver: driver.open("seleniumbase.io/") driver.highlight('img[alt="SeleniumBase"]', loops=6) with DriverContext(browser="chrome", incognito=True) as driver: driver.open("seleniumbase.io/apps/calculator") driver.click('[id="4"]') driver.click('[id="2"]') driver.assert_text("42", "#output") driver.highlight("#output", loops=6) with DriverContext() as driver: driver.open("seleniumbase.io/demo_page") driver.highlight("h2") driver.type("#myTextInput", "Automation") driver.click("#checkBox1") driver.highlight("img", loops=6) ================================================ FILE: examples/raw_driver_manager.py ================================================ """Driver() manager example. (Runs with "python").""" from seleniumbase import Driver driver = Driver() try: driver.open("seleniumbase.io/demo_page") driver.highlight("h2") driver.type("#myTextInput", "Automation") driver.click("#checkBox1") driver.highlight("img", loops=6) finally: driver.quit() driver = Driver(browser="chrome", headless=False) try: driver.open("seleniumbase.io/apps/calculator") driver.click('[id="4"]') driver.click('[id="2"]') driver.assert_text("42", "#output") driver.highlight("#output", loops=6) finally: driver.quit() ================================================ FILE: examples/raw_file_call.py ================================================ """Call a file with "python" instead of "pytest" directly. Added pytest args from the command-line won't be included. To run, use: "python raw_file_call.py".""" from seleniumbase import BaseCase if __name__ == "__main__": from pytest import main main([__file__, "-s"]) class TinyMceTest(BaseCase): def test_tinymce(self): self.open("https://seleniumbase.io/tinymce/") self.wait_for_element("div.mce-container-body") self.click('span:contains("File")') self.click('span:contains("New document")') self.click('span:contains("Paragraph")') self.click('span:contains("Heading 1")') with self.frame_switch("iframe"): self.add_text("#tinymce", "SeleniumBase!") self.highlight("#tinymce") self.post_message("SeleniumBase is fast!", duration=1.5) self.post_message("And SeleniumBase is fun!", duration=1.5) ================================================ FILE: examples/raw_form_turnstile.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "seleniumbase.io/apps/form_turnstile" sb.uc_open_with_reconnect(url, 1.1) sb.press_keys("#name", "SeleniumBase") sb.press_keys("#email", "test@test.test") sb.press_keys("#phone", "1-555-555-5555") sb.click('[for="date"]') sb.click("td.is-today button") sb.click('div[class="select-wrapper"] input') sb.click('span:contains("9:00 PM")') sb.highlight_click('input[value="AR"] + span') sb.click('input[value="cc"] + span') sb.scroll_to('div[class*="cf-turnstile"]') sb.uc_gui_handle_captcha() sb.highlight("img#captcha-success", timeout=3) sb.highlight_click('button:contains("Request & Pay")') sb.highlight("img#submit-success") sb.highlight('button:contains("Success!")') ================================================ FILE: examples/raw_games.py ================================================ """SB Manager using CDP Mode for evading bot-detection.""" from seleniumbase import SB with SB(uc=True, test=True, disable_csp=True) as sb: url = "https://steamdb.info/" sb.activate_cdp_mode(url) sb.sleep(1) sb.click("a.header-login span") sb.sleep(2) sb.solve_captcha() sb.assert_text("Sign in", "button#js-sign-in", timeout=4) sb.sleep(0.5) sb.click("button#js-sign-in") sb.sleep(0.5) sb.highlight("div.page_content form") sb.highlight('button:contains("Sign in")', scroll=False) sb.set_messenger_theme(location="top_center") sb.post_message("SeleniumBase wasn't detected", duration=4) ================================================ FILE: examples/raw_gitlab.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, locale="en") as sb: url = "https://gitlab.com/users/sign_in" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() # (The rest is for testing and demo purposes) sb.assert_element('label[for="user_login"]') sb.assert_element('input[data-testid*="username"]') sb.assert_element('input[data-testid*="password"]') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") ================================================ FILE: examples/raw_google.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://google.com/ncr" sb.activate_cdp_mode(url) sb.type('[name="q"]', "SeleniumBase GitHub page") sb.click('[value="Google Search"]') sb.sleep(4) # The "AI Overview" sometimes loads print(sb.get_page_title()) sb.save_as_pdf_to_logs() sb.save_page_source_to_logs() sb.save_screenshot_to_logs() print("Logs have been saved to: ./latest_logs/") ================================================ FILE: examples/raw_gui_click.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "seleniumbase.io/apps/form_turnstile" sb.activate_cdp_mode(url) sb.press_keys("#name", "SeleniumBase") sb.press_keys("#email", "test@test.test") sb.press_keys("#phone", "1-555-555-5555") sb.click('[for="date"]') sb.click("td.is-today button") sb.click('div[class="select-wrapper"] input') sb.click('span:contains("9:00 PM")') sb.highlight_click('input[value="AR"] + span') sb.click('input[value="cc"] + span') sb.scroll_to('div[class*="cf-turnstile"]') sb.scroll_down(40) sb.uc_gui_click_captcha() sb.highlight("img#captcha-success", timeout=3) sb.highlight_click('button:contains("Request & Pay")') sb.highlight("img#submit-success") sb.highlight('button:contains("Success!")') ================================================ FILE: examples/raw_hobbit.py ================================================ """UC Mode has PyAutoGUI methods for CAPTCHA-bypass.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/hobbit/login" sb.uc_open_with_disconnect(url, 2.2) sb.uc_gui_press_keys("\t ") sb.reconnect(1.5) sb.assert_text("Welcome to Middle Earth!", "h1") sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") sb.click("img") sb.sleep(5.888) # Cool animation happening now! ================================================ FILE: examples/raw_invisible_captcha.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True) as sb: url = "https://seleniumbase.io/apps/invisible_recaptcha" sb.activate_cdp_mode(url) sb.sleep(1) sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ================================================ FILE: examples/raw_login_context.py ================================================ from seleniumbase import DriverContext with DriverContext() as driver: driver.open("seleniumbase.io/simple/login") driver.type("#username", "demo_user") driver.type("#password", "secret_pass") driver.click('a:contains("Sign in")') driver.assert_exact_text("Welcome!", "h1") driver.assert_element("img#image1") driver.highlight("#image1") driver.click_link("Sign out") driver.assert_text("signed out", "#top_message") ================================================ FILE: examples/raw_login_driver.py ================================================ from seleniumbase import Driver driver = Driver() try: driver.open("seleniumbase.io/simple/login") driver.type("#username", "demo_user") driver.type("#password", "secret_pass") driver.click('a:contains("Sign in")') driver.assert_exact_text("Welcome!", "h1") driver.assert_element("img#image1") driver.highlight("#image1") driver.click_link("Sign out") driver.assert_text("signed out", "#top_message") finally: driver.quit() ================================================ FILE: examples/raw_login_sb.py ================================================ from seleniumbase import SB with SB() as sb: sb.open("seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") ================================================ FILE: examples/raw_main_call.py ================================================ """Can use "python" instead of using "pytest". Added pytest args will be included in the run. Example usage: "python raw_file_call.py".""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TinyMceTest(BaseCase): def test_tinymce(self): self.open("https://seleniumbase.io/tinymce/") self.wait_for_element("div.mce-container-body") self.click('span:contains("File")') self.click('span:contains("New document")') self.click('span:contains("Paragraph")') self.click('span:contains("Heading 1")') with self.frame_switch("iframe"): self.add_text("#tinymce", "SeleniumBase!") self.highlight("#tinymce") self.post_message("SeleniumBase is fast!", duration=1.5) self.post_message("And SeleniumBase is fun!", duration=1.5) ================================================ FILE: examples/raw_mobile.py ================================================ from seleniumbase import Driver driver = Driver(mobile=True) try: driver.open("https://www.roblox.com/") driver.assert_element("#download-the-app-container") driver.assert_text("Roblox for Android") driver.highlight('span:contains("Roblox for Android")', loops=8) driver.highlight('span:contains("Continue in App")', loops=8) finally: driver.quit() ================================================ FILE: examples/raw_multi_drivers.py ================================================ import sys import threading from concurrent.futures import ThreadPoolExecutor from random import randint, seed from seleniumbase import Driver sys.argv.append("-n") # Tell SeleniumBase to do thread-locking as needed def launch_driver(url): seed(len(threading.enumerate())) # Random seed for browser placement driver = Driver() try: driver.set_window_rect(randint(4, 720), randint(8, 410), 700, 500) driver.get(url=url) if driver.is_element_visible("h1"): driver.highlight("h1", loops=9) else: driver.sleep(2.2) finally: driver.quit() if __name__ == "__main__": urls = ['https://seleniumbase.io/demo_page' for i in range(4)] with ThreadPoolExecutor(max_workers=len(urls)) as executor: for url in urls: executor.submit(launch_driver, url) ================================================ FILE: examples/raw_multi_sb.py ================================================ import sys import threading from concurrent.futures import ThreadPoolExecutor from random import randint, seed from seleniumbase import SB sys.argv.append("-n") # Tell SeleniumBase to do thread-locking as needed def launch_driver(url): seed(len(threading.enumerate())) # Random seed for browser placement with SB() as sb: sb.set_window_rect(randint(4, 720), randint(8, 410), 700, 500) sb.open(url=url) if sb.is_element_visible("h1"): sb.highlight("h1", loops=9) else: sb.sleep(2.2) if __name__ == "__main__": urls = ['https://seleniumbase.io/demo_page' for i in range(4)] with ThreadPoolExecutor(max_workers=len(urls)) as executor: for url in urls: executor.submit(launch_driver, url) ================================================ FILE: examples/raw_no_context_mgr.py ================================================ """SB() without the context manager `with` block.""" from seleniumbase import SB sb_context = SB() sb = sb_context.__enter__() sb.open("data:text/html,

      Test Page

      ") sb.highlight("h1", loops=8) sb_context.__exit__(None, None, None) """Same example using `with`: from seleniumbase import SB with SB() as sb: sb.open("data:text/html,

      Test Page

      ") sb.highlight("h1", loops=8) """ ================================================ FILE: examples/raw_order_tickets.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, ad_block=True) as sb: url = "https://www.ticketmaster.com" sb.activate_cdp_mode(url) input_field = 'input[name="q"]' sb.wait_for_element(input_field) sb.sleep(1.6) query = "Jerry Seinfeld" sb.press_keys(input_field, query) sb.sleep(1.6) sb.click('a:contains("%s")' % query) sb.sleep(4.2) print('*** TicketMaster Search for "%s":' % query) item_selector = '[data-testid="eventList"] li' for item in sb.find_elements(item_selector): print("* " + item.text) ================================================ FILE: examples/raw_parameter_script.py ================================================ """ The main purpose of this file is to demonstrate running SeleniumBase scripts without the use of Pytest by calling the script directly with Python or from a Python interactive interpreter. Based on whether relative imports work or don't work, this script can autodetect how this file was run. With pure Python, it initializes all the variables that would've been automatically initialized by the Pytest plugin. The setUp() and tearDown() methods are also now called from the script itself. (Note: The SB() and Driver() formats make this example obsolete.) One big advantage to running tests with Pytest is that most of this is done for you automatically, with the option to update any of the parameters through command-line parsing. Pytest also provides you with other plugins, such as ones for generating test reports, handling multithreading, and parametrized tests. Depending on your specific needs, you may need to call SeleniumBase commands without using Pytest, and this example shows you one way of doing that. """ pure_python = False try: # Running with Pytest / (Finds test methods to run using autodiscovery) # Example run command: "pytest raw_parameter_script.py" from .my_first_test import MyTestClass # (relative imports work: ".~") except (ImportError, ValueError): # Running with pure Python OR from a Python interactive interpreter # Example run command: "python raw_parameter_script.py" from my_first_test import MyTestClass # (relative imports do not work) pure_python = True if pure_python: sb = MyTestClass("test_swag_labs") sb.browser = "chrome" sb.is_behave = False sb.headless = False sb.headless1 = False sb.headless2 = False sb.headed = False sb.xvfb = False sb.xvfb_metrics = None sb.start_page = None sb.locale_code = None sb.protocol = "http" sb.servername = "localhost" sb.port = 4444 sb.data = None sb.var1 = None sb.var2 = None sb.var3 = None sb.variables = {} sb.account = None sb.environment = "test" sb.env = "test" # should match sb.environment sb.user_agent = None sb.incognito = False sb.guest_mode = False sb.dark_mode = False sb.devtools = False sb.mobile_emulator = False sb.device_metrics = None sb.extension_zip = None sb.extension_dir = None sb.database_env = "test" sb.archive_logs = False sb.disable_csp = False sb.disable_ws = False sb.enable_ws = False sb.enable_sync = False sb.use_auto_ext = False sb.undetectable = False sb.uc_cdp_events = False sb.uc_subprocess = False sb.no_sandbox = False sb.disable_js = False sb.disable_gpu = False sb.log_cdp_events = False sb._multithreaded = False sb._reuse_session = False sb._crumbs = False sb._final_debug = False sb.esc_end = False sb.use_wire = False sb.enable_3d_apis = False sb.window_position = None sb.window_size = None sb.maximize_option = False sb.visual_baseline = False sb.disable_cookies = False sb.disable_features = None sb._disable_beforeunload = False sb.save_screenshot_after_test = False sb.no_screenshot_after_test = False sb.host_resolver_rules = None sb.page_load_strategy = None sb.timeout_multiplier = None sb.pytest_html_report = None sb.with_db_reporting = False sb.with_s3_logging = False sb.js_checking_on = False sb.recorder_mode = False sb.recorder_ext = False sb.record_sleep = False sb.rec_behave = False sb.rec_print = False sb.report_on = False sb.is_pytest = False sb.slow_mode = False sb.demo_mode = False sb.time_limit = None sb.demo_sleep = None sb.dashboard = False sb._dash_initialized = False sb.message_duration = None sb.binary_location = None sb.driver_version = None sb.block_images = False sb.do_not_track = False sb.external_pdf = False sb.remote_debug = False sb.settings_file = None sb.user_data_dir = None sb.chromium_arg = None sb.firefox_arg = None sb.firefox_pref = None sb.proxy_string = None sb.proxy_bypass_list = None sb.proxy_pac_url = None sb._swiftshader = False sb.multi_proxy = False sb.ad_block_on = False sb.highlights = None sb.interval = None sb.cap_file = None sb.cap_string = None sb.setUp() try: sb.test_swag_labs() finally: sb.tearDown() del sb ================================================ FILE: examples/raw_performance_logs.py ================================================ from rich.pretty import pprint from seleniumbase import SB with SB(log_cdp=True) as sb: sb.open("seleniumbase.io/demo_page") sb.sleep(1) pprint(sb.driver.get_log("performance")) ================================================ FILE: examples/raw_pixelscan.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True) as sb: url = "https://pixelscan.net/fingerprint-check" sb.activate_cdp_mode(url) sb.remove_element("#headerBanner") sb.wait_for_element("pxlscn-dynamic-ad") sb.sleep(0.5) sb.remove_elements("pxlscn-dynamic-ad") sb.sleep(2) sb.assert_text("No masking detected", "pxlscn-fingerprint-masking") sb.assert_text("No automated behavior", "pxlscn-bot-detection") sb.cdp.highlight('span:contains("is consistent")') sb.sleep(1) sb.cdp.highlight("pxlscn-fingerprint-masking p") sb.sleep(1) sb.cdp.highlight("pxlscn-bot-detection p") sb.sleep(2) ================================================ FILE: examples/raw_pyautogui.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/apps/turnstile" sb.activate_cdp_mode(url) sb.uc_gui_handle_captcha() # Cycle with TAB, then SPACEBAR sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ================================================ FILE: examples/raw_recaptcha.py ================================================ from seleniumbase import SB with SB(uc=True, test=True, incognito=True) as sb: url = "https://seleniumbase.io/apps/recaptcha" sb.activate_cdp_mode(url) sb.uc_gui_click_captcha('iframe[src*="/recaptcha/"]') sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ================================================ FILE: examples/raw_robot.py ================================================ from seleniumbase import SB with SB(enable_3d_apis=True, test=True) as sb: sb.open("threejs.org/examples/#webgl_animation_skinning_morph") sb.switch_to_frame("iframe#viewer") sb.set_text_content("#info p", "Hi, I'm Michael Mintz") sb.add_css_style("#info p{zoom: 2.54}") sb.sleep(0.8) sb.click('button:contains("Wave")') sb.highlight("#info p") sb.select_option_by_text("select", "Idle") sb.click('button:contains("ThumbsUp")') sb.set_text_content("#info p", "I created SeleniumBase") sb.highlight("#info p") sb.sleep(0.8) sb.click('button:contains("Jump")') sb.sleep(1.5) ================================================ FILE: examples/raw_sb.py ================================================ """SB() context manager example. (Runs with "python").""" from seleniumbase import SB with SB() as sb: # By default, browser="chrome" if not set. sb.open("https://seleniumbase.io/realworld/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit sb.assert_text("Welcome!", "h1") sb.highlight("img#image1") # A fancier assert_element() call sb.click('a:contains("This Page")') # Use :contains() on any tag sb.click_link("Sign out") # Link must be "a" tag. Not "button". sb.assert_element('a:contains("Sign in")') sb.assert_exact_text("You have been signed out!", "#top_message") ================================================ FILE: examples/raw_test_scripts.py ================================================ """Context Manager Test. Runs with "python". (pytest not needed)""" from seleniumbase import SB with SB(uc=True, test=True) as sb: sb.open("https://google.com/ncr") sb.type('[name="q"]', "SeleniumBase on GitHub") sb.click('[value="Google Search"]') sb.highlight('a[href*="github.com/seleniumbase"]') sb.sleep(0.5) with SB(test=True, rtf=True, demo=True) as sb: sb.open("seleniumbase.github.io/demo_page") sb.type("#myTextInput", "This is Automated") sb.assert_text("This is Automated", "#myTextInput") sb.assert_text("This Text is Green", "#pText") sb.click('button:contains("Click Me")') sb.assert_text("This Text is Purple", "#pText") sb.click("#checkBox1") sb.assert_element_not_visible("div#drop2 img#logo") sb.drag_and_drop("img#logo", "div#drop2") sb.assert_element("div#drop2 img#logo") ================================================ FILE: examples/raw_turnstile.py ================================================ from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/apps/turnstile" sb.uc_open_with_reconnect(url) sb.uc_gui_click_captcha() sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ================================================ FILE: examples/raw_uc_events.py ================================================ from rich.pretty import pprint from seleniumbase import SB def add_cdp_listener(sb): # (To print everything, use "*". Otherwise select specific headers.) # self.driver.add_cdp_listener("*", lambda data: print(pformat(data))) sb.driver.add_cdp_listener( "Network.requestWillBeSentExtraInfo", lambda data: pprint(data) ) def click_turnstile_and_verify(sb): sb.uc_gui_handle_captcha() sb.assert_element("img#captcha-success", timeout=3) sb.highlight("img#captcha-success", loops=8) with SB(uc_cdp_events=True, test=True) as sb: url = "seleniumbase.io/apps/turnstile" sb.uc_open_with_reconnect(url, 2) add_cdp_listener(sb) click_turnstile_and_verify(sb) sb.sleep(1) sb.refresh() sb.sleep(1.2) ================================================ FILE: examples/raw_uc_mode.py ================================================ """SB Manager using UC Mode for evading bot-detection.""" from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.uc_gui_click_captcha() sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) ================================================ FILE: examples/sb_fixture_tests.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) # "sb" pytest fixture test in a method with no class def test_sb_fixture_with_no_class(sb: BaseCase): sb.open("seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") # "sb" pytest fixture test in a method inside a class class Test_SB_Fixture: def test_sb_fixture_inside_class(self, sb: BaseCase): sb.open("seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") ================================================ FILE: examples/setup.cfg ================================================ [flake8] # W503 (line break before binary operator) can be ignored. exclude=recordings,temp ignore=W503 [nosetests] # nocapture=1 (Display print statements from output) # (Undo this by using: "--nologcapture") # logging-level=INFO (Shorter logs than using DEBUG) nocapture=1 logging-level=INFO [behave] show_skipped=false show_timings=false ================================================ FILE: examples/shadow_root_test.py ================================================ """Piercing through shadow-root elements with the "::shadow" selector. To confirm that "::shadow" works, print text and assert exact text.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ShadowRootTest(BaseCase): def test_shadow_root(self): if self.recorder_mode or not self.is_chromium(): self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") self.open("https://seleniumbase.io/other/shadow_dom") print("") self.click("button.tab_1") print(self.get_text("fancy-tabs::shadow #panels")) self.assert_exact_text("Content Panel 1", "fancy-tabs::shadow #panels") self.click("button.tab_2") print(self.get_text("fancy-tabs::shadow #panels")) self.assert_exact_text("Content Panel 2", "fancy-tabs::shadow #panels") self.click("button.tab_3") print(self.get_text("fancy-tabs::shadow #panels")) self.assert_exact_text("Content Panel 3", "fancy-tabs::shadow #panels") ================================================ FILE: examples/swag_labs_user_tests.py ================================================ from parameterized import parameterized from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class SwagLabsTests(BaseCase): def login_to_swag_labs(self, username="standard_user"): """Login to Swag Labs and verify success.""" url = "https://www.saucedemo.com" self.open(url) self.wait_for_element("div.login_logo") if username not in self.get_text("#login_credentials"): self.fail("Invalid user for login: %s" % username) self.type("#user-name", username) self.type("#password", "secret_sauce") self.click('input[type="submit"]') self.assert_element("div.inventory_list") self.assert_element('div:contains("Sauce Labs Backpack")') @parameterized.expand( [ ["standard_user"], ["problem_user"], ] ) def test_swag_labs_user_flows(self, username): """This parameterized test checks basic user actions on the website. It also shows you how SeleniumBase can help you catch bugs.""" self.login_to_swag_labs(username=username) if username == "problem_user": print("\n(This test should fail)") # Verify that the "Test.allTheThings() T-Shirt" appears on the page item_name = "Test.allTheThings() T-Shirt" self.assert_text(item_name) # Verify that a reverse-alphabetical sort works as expected self.select_option_by_value("select.product_sort_container", "za") if item_name not in self.get_text("div.inventory_item"): self.fail('Sort Failed! Expecting "%s" on top!' % item_name) # Add the "Test.allTheThings() T-Shirt" to the cart self.assert_exact_text("Add to cart", "button.btn_inventory") item_price = self.get_text("div.inventory_item_price") self.click("button.btn_inventory") self.assert_exact_text("Remove", "button.btn_inventory") self.assert_exact_text("1", "span.shopping_cart_badge") # Verify your cart self.click("#shopping_cart_container a") self.assert_element('span:contains("Your Cart")') self.assert_text(item_name, "div.inventory_item_name") self.assert_exact_text("1", "div.cart_quantity") self.assert_exact_text("Remove", "button.cart_button") self.assert_element("button#continue-shopping") # Checkout - Add info self.click("button#checkout") self.assert_element('span:contains("Checkout: Your Information")') self.assert_element("button#cancel") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Rocks") self.type("#postal-code", "01720") # Checkout - Overview self.click("input#continue") self.assert_element('span:contains("Checkout: Overview")') self.assert_element("button#cancel") self.assert_text(item_name, "div.inventory_item_name") self.assert_text(item_price, "div.inventory_item_price") self.assert_exact_text("1", "div.cart_quantity") # Finish Checkout and verify that the cart is now empty self.click("button#finish") self.assert_exact_text("Thank you for your order!", "h2") self.assert_element("img.pony_express") self.click("#shopping_cart_container a") self.assert_element_absent("div.inventory_item_name") self.click("button#continue-shopping") self.assert_element_absent("span.shopping_cart_badge") def tearDown(self): self.save_teardown_screenshot() # Only if a test fails # Reset App State and Logout if the controls are present try: self.wait_for_ready_state_complete() if self.is_element_visible("#react-burger-menu-btn"): self.click("#react-burger-menu-btn") self.wait_for_element("a#reset_sidebar_link") self.js_click_if_present("a#reset_sidebar_link") self.js_click_if_present("a#logout_sidebar_link") except Exception: pass super().tearDown() ================================================ FILE: examples/test_3d_apis.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--enable-3d-apis") class ThreeJSTests(BaseCase): def test_animation(self): if self.headless: self.open_if_not_url("about:blank") self.skip("Skip this test in headless mode!") if self.is_chromium() and not self.enable_3d_apis: self.get_new_driver(enable_3d_apis=True) # --enable-3d-apis url = "https://threejs.org/examples/#webgl_animation_skinning_morph" self.open(url) self.switch_to_frame("iframe#viewer") self.sleep(0.8) self.click('button:contains("Wave")') self.sleep(3) ================================================ FILE: examples/test_apple_site.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class AppleTests(BaseCase): def test_apple_developer_site_webdriver_instructions(self): if self.headed: self.demo_mode = True self.demo_sleep = 0.5 self.message_duration = 2.0 if self.is_chromium() and not self.disable_csp: self.get_new_driver(browser=self.browser, disable_csp=True) if self.headless: if self._multithreaded or self.undetectable or self.recorder_mode: self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") elif self.is_chromium(): self.get_new_driver(browser=self.browser, headless2=True) self.open("https://developer.apple.com/search/") title = "Testing with WebDriver in Safari" self.type('[placeholder*="developer.apple.com"]', title + "\n") self.click("link=%s" % title) self.assert_element("nav.documentation-nav") self.assert_text(title, "h1") self.assert_text("Enable WebDriver and run a test.", "div.abstract") if self.demo_mode: self.highlight("div.content h2") else: self.assert_element("div.content h2") h3 = "div.content h3:nth-of-type(%s)" self.assert_text("Make Sure You Have Safari’s WebDriver", h3 % "1") self.assert_text("Get the Correct Selenium Library", h3 % "2") self.assert_text("Configure Safari to Enable WebDriver", h3 % "3") self.assert_text("Write a WebDriver Testing Suite", h3 % "4") self.assert_text("Run Your Test", h3 % "5") ================================================ FILE: examples/test_assert_elements.py ================================================ """Assert that multiple elements are present or visible: HTML Presence: assert_elements_present() HTML Visibility: assert_elements() <> assert_elements_visible()""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ListAssertTests(BaseCase): def test_assert_list_of_elements(self): self.open("https://seleniumbase.io/demo_page") self.assert_elements_present("head", "style", "script") self.assert_elements("h1", "h2", "h3") my_list = ["#myDropdown", "#myButton", "#mySlider"] self.assert_elements(my_list) ================================================ FILE: examples/test_calculator.py ================================================ """Test the SeleniumBase Calculator App""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CalculatorTests(BaseCase): def test_6_times_7_plus_12_equals_54(self): self.open("seleniumbase.io/apps/calculator") self.click('button[id="6"]') self.click("button#multiply") self.click('button[id="7"]') self.click("button#add") self.click('button[id="1"]') self.click('button[id="2"]') self.click("button#equal") self.assert_exact_text("54", "input#output") ================================================ FILE: examples/test_canvas.py ================================================ """Use SeleniumBase methods to interact with "canvas" elements.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CanvasTests(BaseCase): def get_canvas_pixel_colors_at_top_left(self): # Return the RGB colors of the canvas's top left pixel x = 0 y = 0 if self.browser == "safari": x = 1 y = 1 color = self.execute_script( "return document.querySelector('canvas').getContext('2d')" ".getImageData(%s,%s,1,1).data;" % (x, y) ) if self.is_chromium(): return [color[0], color[1], color[2]] else: return [color["0"], color["1"], color["2"]] def test_canvas_click_from_center(self): self.open("https://seleniumbase.io/other/canvas") self.assert_title_contains("Canvas") self.click_with_offset("canvas", 0, 0, mark=True, center=True) self.sleep(0.55) # Not needed (Lets you see the alert pop up) alert = self.switch_to_alert() self.assert_equal(alert.text, "You clicked on the square!") self.accept_alert() self.sleep(0.55) # Not needed (Lets you see the alert go away) if self.browser == "safari" and self._reuse_session: # Alerts can freeze Safari if reusing the browser session self.driver.quit() def test_click_with_offset(self): self.open("https://seleniumbase.io/canvas/") if self.undetectable: self.open_if_not_url("about:blank") print("\n Skip this test in undetectable mode.") self.skip("Skip this test in undetectable mode.") self.assert_title_contains("Canvas") self.highlight("canvas") rgb = self.get_canvas_pixel_colors_at_top_left() self.assert_equal(rgb, [221, 242, 231]) # Looks greenish self.click_with_offset("canvas", 500, 350) self.highlight("canvas", loops=5) rgb = self.get_canvas_pixel_colors_at_top_left() self.assert_equal(rgb, [39, 42, 56]) # Blue by hamburger ================================================ FILE: examples/test_cdp_ad_blocking.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CDPNetworkBlockingTests(BaseCase): def test_cdp_network_blocking(self): self.open("about:blank") if self._reuse_session or not self.is_chromium(): message = "Skipping test if reusing session or not Chromium!" print(message) self.skip(message) self.execute_cdp_cmd("Network.enable", {}) self.execute_cdp_cmd( "Network.setBlockedURLs", {"urls": [ "*.googlesyndication.com*", "*.googletagmanager.com*", "*.google-analytics.com*", "*.amazon-adsystem.com*", "*.adsafeprotected.com*", "*.doubleclick.net*", "*.fastclick.net*", "*.snigelweb.com*", "*.2mdn.net*", ]}) self.open("https://www.w3schools.com/jquery/default.asp") source = self.get_page_source() self.assert_false("doubleclick.net" in source) self.assert_false("google-analytics.com" in source) self.post_message("Blocking was successful!") ================================================ FILE: examples/test_checkboxes.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CheckboxTests(BaseCase): def test_checkboxes_and_radio_buttons(self): self.open("https://seleniumbase.io/w3schools/checkboxes") self.click("button#runbtn") self.switch_to_frame("iframeResult") checkbox = "input#vehicle2" self.assert_false(self.is_selected(checkbox)) self.click(checkbox) self.assert_true(self.is_selected(checkbox)) self.open("https://seleniumbase.io/w3schools/radio_buttons") self.click("button#runbtn") self.switch_to_frame("iframeResult") option_button = "input#css" self.assert_false(self.is_selected(option_button)) self.click(option_button) self.assert_true(self.is_selected(option_button)) ================================================ FILE: examples/test_chinese_pdf.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ChinesePdfTests(BaseCase): def test_chinese_pdf(self): self.open("data:,") pdf = "https://seleniumbase.io/cdn/pdf/unittest_zh.pdf" # Get and print PDF text pdf_text = self.get_pdf_text(pdf, page=2) print("\n" + pdf_text) # Assert PDF contains the expected text on Page 2 self.assert_pdf_text(pdf, "个测试类", page=2) # Assert PDF contains the expected text on any of the pages self.assert_pdf_text(pdf, "运行单元测试") self.assert_pdf_text(pdf, "等待测试结束后显示所有结果") self.assert_pdf_text(pdf, "测试的执行跟方法的顺序没有关系") ================================================ FILE: examples/test_chromedriver.py ================================================ """This test is only for Chrome! (Verify that your chromedriver is compatible with your version of Chrome.)""" import colorama from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ChromedriverTests(BaseCase): def test_chromedriver_matches_chrome(self): self.open("about:blank") if self.browser != "chrome": print("\n This test is only for Chrome!") self.skip("This test is only for Chrome!") chrome_version = self.get_chrome_version() major_chrome_version = chrome_version.split(".")[0] chromedriver_version = self.get_chromedriver_version() major_chromedriver_version = chromedriver_version.split(".")[0] c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL pr_chromedriver_version = c3 + chromedriver_version + cr pr_chrome_version = c2 + chrome_version + cr message = ( "\n" "* Your version of chromedriver is: %s\n" "*\n* And your version of Chrome is: %s" % (pr_chromedriver_version, pr_chrome_version) ) print(message) if major_chromedriver_version < major_chrome_version: install_sb = ( "seleniumbase get chromedriver %s" % major_chrome_version ) pr_install_sb = c1 + install_sb + cr up_msg = "You may want to upgrade your version of chromedriver:" up_msg = c4 + up_msg + cr message = "*\n* %s\n*\n* >>> %s" % (up_msg, pr_install_sb) print(message) elif major_chromedriver_version > major_chrome_version: up_msg = "You may want to upgrade your version of Chrome:" up_msg = c5 + up_msg + cr up_url = c1 + "chrome://settings/help" + cr message = "*\n* %s\n*\n* See: %s" % (up_msg, up_url) print(message) else: up_msg = ( "Success! Your chromedriver is compatible with your Chrome!" ) up_msg = c1 + up_msg + cr message = "*\n* %s\n" % up_msg print(message) ================================================ FILE: examples/test_coffee_cart.py ================================================ """Use SeleniumBase to test the Coffee Cart App.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CoffeeCartTest(BaseCase): def test_coffee_cart(self): self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.assert_element('button:contains("Total: $0.00")') self.click('div[data-sb="Cappuccino"]') self.assert_exact_text("cart (1)", 'a[aria-label="Cart page"]') self.click('div[data-sb="Flat-White"]') self.assert_exact_text("cart (2)", 'a[aria-label="Cart page"]') self.click('div[data-sb="Cafe-Latte"]') self.assert_exact_text("cart (3)", 'a[aria-label="Cart page"]') self.click('a[aria-label="Cart page"]') self.assert_exact_text("Total: $53.00", "button.pay") self.click("button.pay") self.type("input#name", "Selenium Coffee") self.type("input#email", "test@test.test") self.click("button#submit-payment") self.assert_text("Thanks for your purchase.", "#app .success") ================================================ FILE: examples/test_console_logging.py ================================================ """Use SeleniumBase methods to interact with console logs.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TestConsoleLogging(BaseCase): def test_console_logging(self): self.open("https://seleniumbase.io/demo_page") self.wait_for_element_visible("h2") self.start_recording_console_logs() self.console_log_string("Hello World!") self.console_log_script('document.querySelector("h2").textContent') console_logs = [log[0] for log in self.get_recorded_console_logs()] self.assert_in("Hello World!", console_logs) self.assert_in("SeleniumBase", console_logs) ================================================ FILE: examples/test_contains_selector.py ================================================ """TAG:contains("TEXT") is a special, non-standard CSS Selector that gets converted to XPath: '//TAG[contains(., "TEXT")]' before it's used by Selenium calls. Also part of jQuery.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ContainsSelectorTests(BaseCase): def test_contains_selector(self): self.open("https://xkcd.com/2207/") self.assert_element('div.box div:contains("Math Work")') self.click('a:contains("Next")') self.assert_element('div div:contains("Drone Fishing")') ================================================ FILE: examples/test_cycle_elements.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class CycleTests(BaseCase): def test_cycle_elements_with_tab_and_press_enter(self): """ Test pressing the tab key to cycle through elements. Then click on the active element and verify actions. This can all be performed by using a single command. The "\t" is the tab key. The "\n" is the RETURN key. """ self.open("seleniumbase.io/demo_page") self.assert_text("This Text is Green", "#pText") self.send_keys("html", "\t\t\t\t\n") self.assert_text("This Text is Purple", "#pText") ================================================ FILE: examples/test_decryption.py ================================================ """This test demonstrates the use of password encryption/decryption. (Technically considered to be obfuscation/unobfuscation.)""" from seleniumbase import BaseCase from seleniumbase import encryption BaseCase.main(__name__, __file__) class DecryptionTests(BaseCase): def test_decrypt_password(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") encrypted_password = "$^*ENCRYPT=S3BDTAdCWzMmKEY8Gjg=?&#$" print("\nEncrypted Password = %s" % encrypted_password) password = encryption.decrypt(encrypted_password) print("Decrypted Password = %s" % password) self.type("#password", password) self.click('input[type="submit"]') self.assert_element("#inventory_container") self.assert_element('div:contains("Sauce Labs Backpack")') ================================================ FILE: examples/test_deferred_asserts.py ================================================ """This test shows the use of SeleniumBase deferred asserts. Deferred asserts won't raise exceptions from failures until process_deferred_asserts() is called, or the test completes.""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DeferredAssertTests(BaseCase): @pytest.mark.expected_failure def test_deferred_asserts(self): self.open("https://xkcd.com/993/") self.wait_for_element("#comic") print("\n(This test should fail)") self.deferred_assert_element('img[alt="Brand Identity"]') self.deferred_assert_element('img[alt="Rocket Ship"]') # Will Fail self.deferred_assert_element("#comicmap") self.deferred_assert_text("Fake Item", "ul.comicNav") # Will Fail self.deferred_assert_text("Random", "ul.comicNav") self.deferred_assert_element('a[name="Super Fake !!!"]') # Will Fail self.deferred_assert_exact_text("Brand Identity", "#ctitle") self.deferred_assert_exact_text("Fake Food", "#comic") # Will Fail self.process_deferred_asserts() ================================================ FILE: examples/test_demo_site.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DemoSiteTests(BaseCase): def test_demo_site(self): # Open a web page in the active browser window self.open("https://seleniumbase.io/demo_page") # Assert the title of the current web page self.assert_title("Web Testing Page") # Assert that an element is visible on the page self.assert_element("tbody#tbodyId") # Assert that a text substring appears in an element self.assert_text("Demo Page", "h1") # Type text into various text fields and then assert self.type("#myTextInput", "This is Automated") self.type("textarea.area1", "Testing Time!\n") self.type('[name="preText2"]', "Typing Text!") self.assert_text("This is Automated", "#myTextInput") self.assert_text("Testing Time!\n", "textarea.area1") self.assert_text("Typing Text!", '[name="preText2"]') # Hover & click a dropdown element and assert results self.assert_text("Automation Practice", "h3") try: self.hover_and_click("#myDropdown", "#dropOption2", timeout=1) except Exception: # Someone probably moved the mouse while the test ran self.hover_and_js_click("#myDropdown", "#dropOption2") self.assert_text("Link Two Selected", "h3") # Click a button and then verify the expected results self.assert_text("This Text is Green", "#pText") self.click('button:contains("Click Me")') self.assert_text("This Text is Purple", "#pText") # Assert that the given SVG is visible on the page self.assert_element('svg[name="svgName"]') # Verify that a slider control updates a progress bar self.assert_element('progress[value="50"]') self.set_value("input#mySlider", "100") self.assert_element('progress[value="100"]') # Verify that a "select" option updates a meter bar self.assert_element('meter[value="0.25"]') self.select_option_by_text("#mySelect", "Set to 75%") self.assert_element('meter[value="0.75"]') # Assert an element located inside an iframe self.assert_false(self.is_element_visible("img")) self.switch_to_frame("#myFrame1") self.assert_true(self.is_element_visible("img")) self.switch_to_default_content() # Assert text located inside an iframe self.assert_false(self.is_text_visible("iFrame Text")) self.switch_to_frame("#myFrame2") self.assert_true(self.is_text_visible("iFrame Text")) self.switch_to_default_content() # Verify that clicking a radio button selects it self.assert_false(self.is_selected("#radioButton2")) self.click("#radioButton2") self.assert_true(self.is_selected("#radioButton2")) # Verify that clicking a checkbox makes it selected self.assert_element_not_visible("img#logo") self.assert_false(self.is_selected("#checkBox1")) self.click("#checkBox1") self.assert_true(self.is_selected("#checkBox1")) self.assert_element("img#logo") # Verify clicking on multiple elements with one call self.assert_false(self.is_selected("#checkBox2")) self.assert_false(self.is_selected("#checkBox3")) self.assert_false(self.is_selected("#checkBox4")) self.click_visible_elements("input.checkBoxClassB") self.assert_true(self.is_selected("#checkBox2")) self.assert_true(self.is_selected("#checkBox3")) self.assert_true(self.is_selected("#checkBox4")) # Verify that clicking an iframe checkbox selects it self.assert_false(self.is_element_visible(".fBox")) self.switch_to_frame("#myFrame3") self.assert_true(self.is_element_visible(".fBox")) self.assert_false(self.is_selected(".fBox")) self.click(".fBox") self.assert_true(self.is_selected(".fBox")) self.switch_to_default_content() # Verify Drag and Drop self.assert_element_not_visible("div#drop2 img#logo") self.drag_and_drop("img#logo", "div#drop2") self.assert_element("div#drop2 img#logo") # Assert link text self.assert_link_text("seleniumbase.com") self.assert_link_text("SeleniumBase on GitHub") self.assert_link_text("seleniumbase.io") # Click link text self.click_link("SeleniumBase Demo Page") # Assert exact text self.assert_exact_text("Demo Page", "h1") # Highlight a page element (Also asserts visibility) self.highlight("h2") # Actions with Demo Mode enabled if self.headed: self.activate_demo_mode() self.type("input", "Have a Nice Day!") self.assert_text("SeleniumBase", "h2") ================================================ FILE: examples/test_detect_404s.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class BrokenLinkTests(BaseCase): def test_link_checking(self): self.open("https://seleniumbase.io/other/broken_page.html") print("\n(This test should fail)") self.assert_no_404_errors() ================================================ FILE: examples/test_docs_site.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DocsSiteTests(BaseCase): def test_docs(self): self.open("https://seleniumbase.io/help_docs/customizing_test_runs/") self.assert_text("Command Line Options", "h1") self.js_click('a[href$="/examples/example_logs/ReadMe/"]') self.assert_text("Dashboard / Reports", "h1") self.js_click('a[href$="/seleniumbase/console_scripts/ReadMe/"]') self.assert_text("Console Scripts", "h1") self.js_click('a[href$="/help_docs/syntax_formats/"]') self.assert_text("Syntax Formats", "h1") self.js_click('a[href$="/recorder_mode/"]') self.assert_text("Recorder Mode", "h1") self.js_click('a[href$="/method_summary/"]') self.assert_text("API Reference", "h1") self.js_click('a[href$="/uc_mode/"]') self.assert_text("UC Mode", "h1") self.js_click('img[alt="logo"]') self.assert_text("SeleniumBase", "h1") ================================================ FILE: examples/test_double_click.py ================================================ """Test double_click() after switching into iframes.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DoubleClickTests(BaseCase): def test_switch_to_frame_and_double_click(self): self.open("https://seleniumbase.io/w3schools/double_click") self.assert_title("Double Click Testing") self.click("button#runbtn") self.switch_to_frame("iframe#iframeResult") self.double_click('[ondblclick="myFunction()"]') self.assert_text("Hello World", "#demo") def test_switch_to_frame_of_element_and_double_click(self): self.open("https://seleniumbase.io/w3schools/double_click") self.assert_title("Double Click Testing") self.click("button#runbtn") self.switch_to_frame_of_element('[ondblclick="myFunction()"]') self.double_click('[ondblclick="myFunction()"]') self.assert_text("Hello World", "#demo") ================================================ FILE: examples/test_download_files.py ================================================ """Use SeleniumBase to download files and verify.""" import math from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DownloadTests(BaseCase): def test_download_chromedriver_notes(self): if self._multithreaded: self.open_if_not_url("about:blank") self.skip("Skipping test in multi-threaded mode.") self.open("https://chromedriver.chromium.org/downloads") notes_file = "notes.txt" notes_link = ( "https://chromedriver.storage.googleapis.com" "/101.0.4951.41/%s" % notes_file ) self.download_file(notes_link) self.assert_downloaded_file(notes_file) notes_path = self.get_path_of_downloaded_file(notes_file) with open(notes_path, "r") as f: notes_data = f.read() self.assert_true(len(notes_data) > 100) # Verify file not empty text = "Switching to nested frame fails with chrome/chromedriver 100" self.assert_true(text in notes_data) # Verify file has expected data def test_download_files_from_pypi(self): self.open("https://pypi.org/project/sbvirtualdisplay/#files") self.assert_element("span#pip-command") self.assert_text("Download files", "div#files h2.page-title") self.assert_text("Download files", "a#files-tab") pkg_header = self.get_text("h1.package-header__name").strip() pkg_name = pkg_header.replace(" ", "-") whl_file = pkg_name + "-py3-none-any.whl" tar_gz_file = pkg_name + ".tar.gz" # Click the links to download the files into: "./downloaded_files/" # (If using Safari, IE, or Chromium Guest Mode: download directly.) # (The default Downloads Folder can't be changed when using those.) # (Use self.get_browser_downloads_folder() to get the folder used.) whl_selector = 'div#files a[href$="%s"]' % whl_file tar_selector = 'div#files a[href$="%s"]' % tar_gz_file if ( self.browser == "safari" or self.browser == "ie" or self.browser == "edge" or (self.is_chromium() and self.guest_mode) or (self.is_chromium() and (self.headless or self.headless2)) ): whl_href = self.get_attribute(whl_selector, "href") tar_href = self.get_attribute(tar_selector, "href") self.download_file(whl_href) self.download_file(tar_href) else: self.js_click(whl_selector) # Download the "whl" file self.sleep(0.1) self.js_click(tar_selector) # Download the "tar" file # Verify that the downloaded files appear in the [Downloads Folder] # (This only guarantees that the exact file name is in the folder.) # (This does not guarantee that the downloaded files are complete.) # (Later, we'll check that the files were downloaded successfully.) self.assert_downloaded_file(whl_file) self.assert_downloaded_file(tar_gz_file) self.sleep(1) # Add more time to make sure downloads have completed # Get the actual size of the downloaded files (in bytes) whl_path = self.get_path_of_downloaded_file(whl_file) with open(whl_path, "rb") as f: whl_file_bytes = len(f.read()) print("\n%s | Download = %s bytes." % (whl_file, whl_file_bytes)) tar_gz_path = self.get_path_of_downloaded_file(tar_gz_file) with open(tar_gz_path, "rb") as f: tar_gz_file_bytes = len(f.read()) print("%s | Download = %s bytes." % (tar_gz_file, tar_gz_file_bytes)) # Check to make sure the downloaded files are not empty or too small self.assert_true(whl_file_bytes > 5000) self.assert_true(tar_gz_file_bytes > 5000) # Get file sizes in kB to compare actual values with displayed values whl_file_kb = whl_file_bytes / 1000.0 whl_line_fi = self.get_text('a[href$=".whl"]').strip() whl_line = self.get_text('div.file:contains("%s")' % whl_line_fi) whl_display_kb = float(whl_line.split("(")[1].split(" ")[0]) tar_gz_file_kb = tar_gz_file_bytes / 1000.0 tar_gz_line_fi = self.get_text('a[href$=".tar.gz"]').strip() tar_gz_line = self.get_text('div.file:contains("%s")' % tar_gz_line_fi) tar_gz_display_kb = float(tar_gz_line.split("(")[1].split(" ")[0]) # Verify downloaded files are the correct size (account for rounding) self.assert_true( abs(math.floor(whl_file_kb) - math.floor(whl_display_kb)) < 2 ) self.assert_true( abs(math.floor(tar_gz_file_kb) - math.floor(tar_gz_display_kb)) < 2 ) # Delete the downloaded files from the [Downloads Folder] self.delete_downloaded_file_if_present(whl_file) self.delete_downloaded_file_if_present(tar_gz_file) # Verify that the downloaded files have been successfully deleted self.assert_false(self.is_downloaded_file_present(whl_file)) self.assert_false(self.is_downloaded_file_present(tar_gz_file)) ================================================ FILE: examples/test_download_images.py ================================================ """Use SeleniumBase to download images and verify.""" import os from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DownloadImages(BaseCase): def test_download_images_directly(self): if self._multithreaded: self.open_if_not_url("about:blank") self.skip("Skipping test in multi-threaded mode.") self.open("seleniumbase.io/examples/chart_maker/ReadMe") img_elements_with_src = self.find_elements("img[src]") unique_src_values = [] for img in img_elements_with_src: src = img.get_attribute("src") if src not in unique_src_values: unique_src_values.append(src) print() for src in unique_src_values: if src.split(".")[-1] not in ["png", "jpg", "jpeg"]: continue self.download_file(src) # Goes to downloaded_files/ filename = src.split("/")[-1] self.assert_downloaded_file(filename) folder = "downloaded_files" file_path = os.path.join(folder, filename) print(file_path) def test_download_images_via_screenshot(self): if self.recorder_mode: self.open("about:blank") self.skip("Skipping test in Recorder Mode.") self.open("seleniumbase.io/error_page/") img_elements_with_src = self.find_elements("img[src]") unique_src_values = [] for img in img_elements_with_src: src = img.get_attribute("src") if src not in unique_src_values: unique_src_values.append(src) print() count = 0 for src in unique_src_values: self.open(src) if not self.headless and not self.headless2: self.sleep(0.3) image = self.find_element("img") if src.startswith("data:") or ";base64" in src: # Special Cases: SVGs, etc. Convert to PNG. count += 1 filename = "svg_image_%s.png" % count else: filename = src.split("/")[-1] folder = "downloaded_files" file_path = os.path.join(folder, filename) image.screenshot(file_path) self.assert_downloaded_file(filename) print(file_path) ================================================ FILE: examples/test_drag_and_drop.py ================================================ """Test drag_and_drop() on different pages.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DragAndDropTests(BaseCase): def test_drag_and_drop(self): self.open("https://seleniumbase.io/other/drag_and_drop") self.assert_element_not_visible("#div1 img#drag1") self.drag_and_drop("#drag1", "#div1") self.assert_element("#div1 img#drag1") self.sleep(0.8) def test_w3schools_drag_and_drop(self): self.open("https://seleniumbase.io/w3schools/drag_drop") self.assert_url_contains("drag_drop") self.click("button#runbtn") self.switch_to_frame("iframeResult") self.assert_element_not_visible("#div1 img#drag1") self.drag_and_drop("#drag1", "#div1") self.assert_element("#div1 img#drag1") self.sleep(0.8) ================================================ FILE: examples/test_error_page.py ================================================ """Test an error page with the "highlight() command, which uses a JavaScript animation to point out page objects that are found. If an element isn't visible, the test fails with an exception.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ErrorPageTests(BaseCase): def test_error_page(self): self.open("https://seleniumbase.io/error_page/") self.highlight('img[alt="500 Error"]') self.highlight("img#parallax_octocat") self.highlight("#parallax_error_text") self.highlight('img[alt*="404"]') self.highlight("img#octobi_wan_catnobi") self.highlight("img#speeder") self.save_screenshot_after_test = True ================================================ FILE: examples/test_event_firing.py ================================================ """Test EventFiringWebDriver with AbstractEventListener""" from selenium.webdriver.support.events import EventFiringWebDriver from selenium.webdriver.support.events import AbstractEventListener from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyListener(AbstractEventListener): def before_navigate_to(self, url, driver): print("Before navigating to: %s" % url) def after_navigate_to(self, url, driver): print("After navigating to: %s" % url) def before_find(self, by, value, driver): print('Before find "%s" (by = %s)' % (value, by)) def after_find(self, by, value, driver): print('After find "%s" (by = %s)' % (value, by)) def before_click(self, element, driver): print('Before clicking on element with text: "%s"' % element.text) def after_click(self, element, driver): print("Click complete!") class EventFiringTests(BaseCase): def test_event_firing_webdriver(self): self.driver = EventFiringWebDriver(self.driver, MyListener()) print("\n* EventFiringWebDriver example *") self.open("https://xkcd.com/1862/") self.click("link=About") self.open("https://xkcd.com/1820/") self.assert_text("Security Advice", "#ctitle") self.click('a:contains("Next >")') self.assert_text("Incinerator", "#ctitle") self.click('a[rel="next"]') self.assert_text("Existential Bug Reports", "#ctitle") ================================================ FILE: examples/test_fail.py ================================================ """ This test fails on purpose to demonstrate the logging capabilities of SeleniumBase. >>> pytest test_fail.py --html=report.html This creates ``report.html`` with details. (Also find log files in ``latest_logs/``)""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class FailingTests(BaseCase): @pytest.mark.expected_failure def test_find_army_of_robots_on_xkcd_desert_island(self): self.open("https://xkcd.com/731/") print("\n(This test should fail)") self.assert_element("div#ARMY_OF_ROBOTS", timeout=1) ================================================ FILE: examples/test_geolocation.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TestGeolocation(BaseCase): def tearDown(self): self.save_teardown_screenshot() # If test fails, or if "--screenshot" if self.is_chromium() and not self._multithreaded: # Reset Permissions and GeolocationOverride try: self.open("about:blank") self.execute_cdp_cmd("Emulation.setGeolocationOverride", {}) self.execute_cdp_cmd("Browser.resetPermissions", {}) except Exception: pass super().tearDown() def test_geolocation(self): self.open("about:blank") if self._multithreaded: self.skip("Skipping test in multi-threaded mode.") if not self.is_chromium(): print("\n* execute_cdp_cmd() is only for Chromium browsers") self.skip("execute_cdp_cmd() is only for Chromium browsers") self.execute_cdp_cmd( "Browser.grantPermissions", { "origin": "https://www.randymajors.org/", "permissions": ["geolocation"], }, ) self.execute_cdp_cmd( "Emulation.setGeolocationOverride", { "latitude": 48.87645, "longitude": 2.26340, "accuracy": 100, }, ) self.open("https://www.randymajors.org/what-time-zone-am-i-in") self.ad_block() self.assert_text("Paris, France", "#statecountrylabel") self.assert_text("Central European Standard Time", "#currentlabel") self.save_screenshot_to_logs() if self.headed: self.sleep(4) ================================================ FILE: examples/test_get_coffee.py ================================================ """Use SeleniumBase to get coffee""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class GetCoffeeTest(BaseCase): def test_get_coffee(self): self.open("https://seleniumbase.io/coffee/") self.assert_title("Coffee Cart") self.assert_exact_text("cart (0)", 'a[aria-label="Cart page"]') self.assert_element('div[data-sb="Mocha"]') self.click('div[data-sb="Mocha"]') self.assert_link_text("cart (1)") self.click_link_text("cart (1)") self.assert_exact_text("Total: $8.00", "button.pay") self.click("button.pay") self.type("input#name", "Selenium Coffee") self.type("input#email", "test@test.test") self.click("button#submit-payment") self.assert_text("Thanks", "#app div.success") ================================================ FILE: examples/test_get_locale_code.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class LocaleTests(BaseCase): def test_get_locale_code(self): self.open("about:blank") locale_code = self.get_locale_code() message = '\nLocale Code = "%s"' % locale_code print(message) self.set_messenger_theme(theme="flat", location="top_center") self.post_message(message, duration=4) ================================================ FILE: examples/test_get_pdf_text.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class PdfTests(BaseCase): def test_get_pdf_text(self): self.open("data:,") pdf = ( "https://nostarch.com/download/" "Automate_the_Boring_Stuff_sample_ch17.pdf" ) pdf_text = self.get_pdf_text(pdf, page=1) print("\n" + pdf_text) ================================================ FILE: examples/test_get_swag.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_swag_labs(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("div.inventory_list") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("input#first-name", "SeleniumBase") self.type("input#last-name", "Automation") self.type("input#postal-code", "77123") self.click("input#continue") self.click("button#finish") self.assert_text("Thank you for your order!") ================================================ FILE: examples/test_get_user_agent.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class UserAgentTests(BaseCase): def test_get_user_agent(self): self.open("data:,") user_agent = self.get_user_agent() print('\nUser Agent = "%s"' % user_agent) self.set_messenger_theme(theme="flat", location="top_center") self.post_message(user_agent, duration=4) ================================================ FILE: examples/test_hack_search.py ================================================ """ Testing the "self.set_attribute()" and "self.set_attributes()" methods to modify a Google search into becoming a Bing search. set_attribute() -> Modifies the attribute of the first matching element. set_attributes() -> Modifies the attribute of all matching elements. """ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class HackingTests(BaseCase): def test_hack_search(self): if self.headless or self.browser != "chrome": self.open_if_not_url("about:blank") self.skip('Skip test if headless or not chrome.') if not self.undetectable: self.get_new_driver(undetectable=True) self.open("https://google.com/ncr") self.hide_elements("iframe") self.assert_element('[title="Search"]') self.sleep(0.5) self.set_attribute('[action="/search"]', "action", "//bing.com/search") self.set_attributes('[value="Google Search"]', "value", "Bing Search") self.type('[title="Search"]', "SeleniumBase GitHub Page URL") self.sleep(0.5) self.js_click('[value="Bing Search"]') self.highlight("h1.b_logo", loops=8) source = self.get_page_source() self.assert_true("github.com/seleniumbase/SeleniumBase" in source) self.click('a:contains("seleniumbase/SeleniumBase")') self.js_click('a[title="examples"]') self.highlight('#repo-content-turbo-frame') self.js_click('a[title="test_hack_search.py"]') self.assert_text("test_hack_search.py", "#file-name-id-wide") self.highlight("#file-name-id-wide") ================================================ FILE: examples/test_highlight_elements.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class HighlightTest(BaseCase): def test_highlight_inputs(self): self.open("https://seleniumbase.io/demo_page") if self.headed: self.highlight_elements("input", loops=2) # Default: 4 else: self.highlight_elements("input", loops=1, limit=3) ================================================ FILE: examples/test_image_saving.py ================================================ """ Image-saving with "save_element_as_image_file()". Also shown are ways of ordering tests. (Currently commented out) For ordering tests, add the marker "@pytest.mark.run(order=NUM)" before a test definition or class definition. This changes the global test order when running "pytest". Eg: If you want a test to always run first before any test from all discovered files, add "@pytest.mark.run(order=0)". For local class/module test-ordering, name your tests using alphabetical order to set the order desired. Eg: "def test_AAAAA" will run before "def test_ZZZZZ". You can also add in numbers to force a specific order. Eg: "def test_1_ZZZ" will run before "def test_2_AAA". """ import os # import pytest # For ordering tests globally with @pytest.mark.run() from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ImageTests(BaseCase): # @pytest.mark.run(order=1) def test_1_save_element_as_image_file(self): """Pull an image from a website and save it as a PNG file.""" self.open("https://xkcd.com/1117/") selector = "#comic" file_name = "comic.png" folder = "images_exported" self.save_element_as_image_file(selector, file_name, folder) file_path = os.path.join(folder, file_name) self.assert_true(os.path.exists(file_path)) print('\n"%s" was saved!' % file_path) # @pytest.mark.run(order=2) def test_2_add_text_overlay_to_image(self): """Add a text overlay to an image.""" self.open("https://xkcd.com/1117/") selector = "#comic" file_name = "image_overlay.png" folder = "images_exported" overlay_text = 'This is an XKCD comic!\nTitle: "My Sky"' self.save_element_as_image_file( selector, file_name, folder, overlay_text ) file_path = os.path.join(folder, file_name) self.assert_true(os.path.exists(file_path)) print('\n"%s" was saved!' % file_path) # @pytest.mark.run(order=3) def test_3_add_text_overlay_to_page_section(self): """Add a text overlay to a section of a page.""" self.open("https://xkcd.com/2200/") selector = "#middleContainer" file_name = "section_overlay.png" folder = "images_exported" overlay_text = ( "Welcome to %s\n" "This is a comment added to the image.\n" "Unreachable states come from logic errors." % self.get_current_url() ) self.save_element_as_image_file( selector, file_name, folder, overlay_text ) file_path = os.path.join(folder, file_name) self.assert_true(os.path.exists(file_path)) print('\n"%s" was saved!' % file_path) # @pytest.mark.run(order=4) def test_4_add_text_overlay_to_full_page(self): """Add a text overlay to a full page.""" self.open("https://xkcd.com/1922/") self.remove_element("#bottom") selector = "body" file_name = "page_overlay.png" folder = "images_exported" overlay_text = "A text overlay on %s" % self.get_current_url() self.save_element_as_image_file( selector, file_name, folder, overlay_text ) file_path = os.path.join(folder, file_name) self.assert_true(os.path.exists(file_path)) print('\n"%s" was saved!' % file_path) ================================================ FILE: examples/test_inspect_html.py ================================================ """Uses the SeleniumBase implementation of HTML-Inspector to inspect the HTML. See https://github.com/philipwalton/html-inspector for more details. (Only works on Chrome and Chromium-based browsers.)""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class HtmlInspectorTests(BaseCase): def test_html_inspector(self): self.open("https://xkcd.com/1144/") self.inspect_html() ================================================ FILE: examples/test_login.py ================================================ """A SeleniumBase test for verifying Login functionality on Swag Labs.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class SwagLabsLoginTests(BaseCase): def login_to_swag_labs(self): self.open("https://www.saucedemo.com") self.wait_for_element("div.login_logo") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce") self.click('input[type="submit"]') def test_swag_labs_login(self): self.login_to_swag_labs() self.assert_element("div.inventory_list") self.assert_element('.inventory_item:contains("Backpack")') self.js_click("a#logout_sidebar_link") self.assert_element("div#login_button_container") ================================================ FILE: examples/test_markers.py ================================================ """ These tests demonstrate pytest marker use for finding and running tests. Usage examples from this file: pytest -v -m marker_test_suite # Runs A, B, C, D pytest -v -m marker1 # Runs A pytest -v -m marker2 # Runs B, C pytest -v -m marker3 # Runs C pytest test_markers.py -v -m "not marker2" # Runs A, D (The "-v" will display the names of tests as they run.) (Add "--collect-only" to display names of tests without running them.)""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.marker_test_suite class MarkerTestSuite(BaseCase): @pytest.mark.marker1 def test_A(self): self.open("https://xkcd.com/1319/") self.assert_text("Automation", "div#ctitle") @pytest.mark.marker2 def test_B(self): self.open("https://www.xkcd.com/1700/") self.assert_text("New Bug", "div#ctitle") @pytest.mark.marker2 @pytest.mark.marker3 # Tests can have multiple markers def test_C(self): self.open("https://xkcd.com/844/") self.assert_text("Good Code", "div#ctitle") def test_D(self): self.open("https://xkcd.com/2021/") self.assert_text("Software Development", "div#ctitle") ================================================ FILE: examples/test_mfa_login.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TestMFALogin(BaseCase): def test_mfa_login(self): self.open("https://seleniumbase.io/realworld/login") self.type("#username", "demo_user") self.type("#password", "secret_pass") self.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit self.assert_text("Welcome!", "h1") self.highlight("img#image1") # A fancier assert_element() call self.click('a:contains("This Page")') # Use :contains() on any tag self.save_screenshot_to_logs() # ("./latest_logs" folder for test) self.click_link("Sign out") # Link must be "a" tag. Not "button". self.assert_element('a:contains("Sign in")') self.assert_exact_text("You have been signed out!", "#top_message") ================================================ FILE: examples/test_multiple_drivers.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MultipleDriversTest(BaseCase): def test_multiple_drivers(self): if self.browser == "safari": self.open_if_not_url("about:blank") print("\n Safari doesn't support multiple drivers.") self.skip("Safari doesn't support multiple drivers.") self.open("data:text/html,

      Driver 1

      ") driver2 = self.get_new_driver() self.open("data:text/html,

      Driver 2

      ") self.switch_to_default_driver() # Driver 1 self.highlight("h1") self.assert_text("Driver 1", "h1") self.switch_to_driver(driver2) # Driver 2 self.highlight("h1") self.assert_text("Driver 2", "h1") ================================================ FILE: examples/test_null.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class NullTests(BaseCase): def test_null(self): pass ================================================ FILE: examples/test_override_driver.py ================================================ from selenium import webdriver from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class OverrideDriverTest(BaseCase): def get_new_driver(self, *args, **kwargs): """This method overrides get_new_driver() from BaseCase.""" options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") if self.headless: options.add_argument("--headless=new") options.add_argument("--disable-gpu") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) return webdriver.Chrome(options=options) def test_driver_override(self): self.open("https://seleniumbase.io/demo_page") self.type("#myTextInput", "This is Automated") self.set_value("input#mySlider", "100") self.select_option_by_text("#mySelect", "Set to 100%") self.click("#checkBox1") self.drag_and_drop("img#logo", "div#drop2") self.click('button:contains("Click Me")') ================================================ FILE: examples/test_override_sb_fixture.py ================================================ """Overriding the "sb" fixture to override the driver.""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.fixture() def sb(request): from selenium import webdriver from seleniumbase import BaseCase from seleniumbase import config as sb_config from seleniumbase.core import session_helper class BaseClass(BaseCase): def get_new_driver(self, *args, **kwargs): """This method overrides get_new_driver() from BaseCase.""" options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") if self.headless: options.add_argument("--headless=new") options.add_argument("--disable-gpu") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) return webdriver.Chrome(options=options) def setUp(self): super().setUp() def base_method(self): pass def tearDown(self): self.save_teardown_screenshot() super().tearDown() if request.cls: if sb_config.reuse_class_session: the_class = str(request.cls).split(".")[-1].split("'")[0] if the_class != sb_config._sb_class: session_helper.end_reused_class_session_as_needed() sb_config._sb_class = the_class request.cls.sb = BaseClass("base_method") request.cls.sb.setUp() request.cls.sb._needs_tearDown = True request.cls.sb._using_sb_fixture = True request.cls.sb._using_sb_fixture_class = True sb_config._sb_node[request.node.nodeid] = request.cls.sb yield request.cls.sb if request.cls.sb._needs_tearDown: request.cls.sb.tearDown() request.cls.sb._needs_tearDown = False else: sb = BaseClass("base_method") sb.setUp() sb._needs_tearDown = True sb._using_sb_fixture = True sb._using_sb_fixture_no_class = True sb_config._sb_node[request.node.nodeid] = sb yield sb if sb._needs_tearDown: sb.tearDown() sb._needs_tearDown = False def test_override_fixture_no_class(sb: BaseCase): sb.open("https://seleniumbase.io/demo_page") sb.type("#myTextInput", "This is Automated") sb.set_value("input#mySlider", "100") sb.select_option_by_text("#mySelect", "Set to 100%") sb.click("#checkBox1") sb.drag_and_drop("img#logo", "div#drop2") sb.click('button:contains("Click Me")') class TestOverride: def test_override_fixture_inside_class(self, sb: BaseCase): sb.open("https://seleniumbase.io/demo_page") sb.type("#myTextInput", "This is Automated") sb.set_value("input#mySlider", "100") sb.select_option_by_text("#mySelect", "Set to 100%") sb.click("#checkBox1") sb.drag_and_drop("img#logo", "div#drop2") sb.click('button:contains("Click Me")') ================================================ FILE: examples/test_parse_soup.py ================================================ import re from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class SoupParsingTests(BaseCase): def click_menu_item(self, text): # Use BeautifulSoup to parse the selector ID from element text. # Then click on the element with the ID. # (This is useful when the selector ID is auto-generated.) pattern = re.compile(text) soup = self.get_beautiful_soup() the_id = soup.find(string=pattern).parent.parent.attrs["id"] self.click("#%s" % the_id) def test_beautiful_soup_parsing(self): if self.headless: self.open_if_not_url("about:blank") self.skip("Skip this test in headless mode!") self.open("https://seleniumbase.io/tinymce/") self.wait_for_element("div.mce-container-body") self.click_menu_item("File") self.click_menu_item("New document") self.click_menu_item("Paragraph") self.click_menu_item("Heading 2") self.switch_to_frame("iframe") self.add_text("#tinymce", "Automate anything with SeleniumBase!\n") self.switch_to_default_content() self.click("button i.mce-i-image") self.type('input[aria-label="Width"].mce-textbox', "300") image_url = "https://seleniumbase.github.io/img/sb_logo_10.png" self.type("input.mce-textbox", image_url + "\n") with self.frame_switch("iframe"): self.click("h2") self.post_message("Automate anything with SeleniumBase!") self.click_menu_item("File") self.click_menu_item("Preview") with self.frame_switch('iframe[sandbox="allow-scripts"]'): self.post_message("Learn SeleniumBase Today!") ================================================ FILE: examples/test_pdf_asserts.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class PdfAssertTests(BaseCase): def test_assert_pdf_text(self): self.open("data:,") # Assert PDF contains the expected text on Page 1 self.assert_pdf_text( "https://nostarch.com/download/Automate_the_Boring_Stuff_dTOC.pdf", "Programming Is a Creative Activity", page=1, ) # Assert PDF contains the expected text on any of the pages self.assert_pdf_text( "https://nostarch.com/download/Automate_the_Boring_Stuff_dTOC.pdf", "Extracting Text from PDFs", ) ================================================ FILE: examples/test_pytest_parametrize.py ================================================ import pytest @pytest.mark.parametrize( "value", ["List of Features", "Command Line Options"] ) def test_sb_fixture_with_no_class(sb, value): sb.open("seleniumbase.io/help_docs/install/") sb.type('input[aria-label="Search"]', value) sb.click("nav h1 mark") sb.assert_title_contains(value) sb.assert_text(value, "div.md-content") class Test_SB_Fixture: @pytest.mark.parametrize( "value", ["Console Scripts", "API Reference"] ) def test_sb_fixture_inside_class(self, sb, value): sb.open("seleniumbase.io/help_docs/install/") sb.type('input[aria-label="Search"]', value) sb.click("nav h1 mark") sb.assert_title_contains(value) sb.assert_text(value, "div.md-content") ================================================ FILE: examples/test_repeat_tests.py ================================================ """ Tests to demonstrate how to repeat the same test multiple times. The 1st example uses the "parameterized" library. The 2nd example uses "pytest.mark.parametrize()". (NO class) The 3rd example uses "pytest.mark.parametrize()". (in class) """ import pytest from parameterized import parameterized from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "-n6") url = "data:text/html,

      Hello

       

      " class RepeatTests(BaseCase): @parameterized.expand([[]] * 2) def test_repeat_this_test_with_parameterized(self): self.open(url) self.type("input", "SeleniumBase is fun") self.click('button:contains("OK!")') self.assert_text("Hello", "h2") @pytest.mark.parametrize("", [[]] * 2) def test_repeat_this_test_with_pytest_parametrize(sb): sb.open(url) sb.type("input", "SeleniumBase is fun") sb.click('button:contains("OK!")') sb.assert_text("Hello", "h2") class RepeatTestsWithPytest: @pytest.mark.parametrize("", [[]] * 2) def test_repeat_test_with_pytest_parametrize(self, sb): sb.open(url) sb.type("input", "SeleniumBase is fun") sb.click('button:contains("OK!")') sb.assert_text("Hello", "h2") ================================================ FILE: examples/test_request_sb_fixture.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) # Use the pytest "request" fixture to get the "sb" fixture (no class) def test_request_sb_fixture(request): sb: BaseCase = request.getfixturevalue("sb") sb.open("https://seleniumbase.io/demo_page") sb.assert_text("SeleniumBase", "#myForm h2") sb.assert_element("input#myTextInput") sb.type("#myTextarea", "This is me") sb.click("#myButton") sb.tearDown() # Use the pytest "request" fixture to get the "sb" fixture (in class) class Test_Request_Fixture: def test_request_sb_fixture_in_class(self, request): sb: BaseCase = request.getfixturevalue("sb") sb.open("https://seleniumbase.io/demo_page") sb.assert_element("input#myTextInput") sb.type("#myTextarea", "Automated") sb.assert_text("This Text is Green", "#pText") sb.click("#myButton") sb.assert_text("This Text is Purple", "#pText") sb.tearDown() ================================================ FILE: examples/test_roblox_mobile.py ================================================ """Mobile device test for Chromium-based browsers Example: "pytest test_roblox_mobile.py --mobile" """ from seleniumbase import BaseCase if __name__ == "__main__": from pytest import main main([__file__, "--mobile", "-s"]) class RobloxTests(BaseCase): def test_roblox_mobile_site(self): if not self.mobile_emulator: self.open_if_not_url("about:blank") print("\n This test is only for mobile-device web browsers!") print(' (Use "--mobile" to run this test in Mobile Mode!)') self.skip('Use "--mobile" to run this test in Mobile Mode!') self.open("https://www.roblox.com/") self.assert_element("#download-the-app-container") self.assert_text("Roblox for Android") self.assert_text("Continue in App", "a.content-action-emphasis") self.highlight('span:contains("Roblox for Android")', loops=8) self.highlight("a.content-action-emphasis", loops=8) ================================================ FILE: examples/test_save_screenshots.py ================================================ import os from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ScreenshotTests(BaseCase): def test_save_screenshot(self): self.open("https://seleniumbase.io/demo_page") # "./downloaded_files" is a special SeleniumBase folder for downloads self.save_screenshot("demo_page.png", folder="./downloaded_files") self.assert_downloaded_file("demo_page.png") print('\n"%s/%s" was saved!' % ("downloaded_files", "demo_page.png")) def test_save_screenshot_to_logs(self): self.open("https://seleniumbase.io/demo_page") self.save_screenshot_to_logs() # "self.log_path" is the absolute path to the "./latest_logs" folder. # Each test that generates log files will create a subfolder in there test_logpath = os.path.join(self.log_path, self.test_id) expected_screenshot = os.path.join(test_logpath, "_1_screenshot.png") self.assert_true(os.path.exists(expected_screenshot)) print('\n"%s" was saved!' % (expected_screenshot)) self.open("https://seleniumbase.io/tinymce/") self.save_screenshot_to_logs() expected_screenshot = os.path.join(test_logpath, "_2_screenshot.png") self.assert_true(os.path.exists(expected_screenshot)) print('"%s" was saved!' % (expected_screenshot)) self.open("https://seleniumbase.io/error_page/") self.save_screenshot_to_logs("error_page") expected_screenshot = os.path.join(test_logpath, "_3_error_page.png") self.assert_true(os.path.exists(expected_screenshot)) print('"%s" was saved!' % (expected_screenshot)) self.open("https://seleniumbase.io/devices/") self.save_screenshot_to_logs("devices") expected_screenshot = os.path.join(test_logpath, "_4_devices.png") self.assert_true(os.path.exists(expected_screenshot)) print('"%s" was saved!' % (expected_screenshot)) ================================================ FILE: examples/test_sb_fixture.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) # "sb" pytest fixture test in a method with no class def test_sb_fixture_with_no_class(sb: BaseCase): sb.open("seleniumbase.io/help_docs/install/") sb.type('input[aria-label="Search"]', "GUI Commander") sb.click('mark:contains("Commander")') sb.assert_title_contains("GUI / Commander") # "sb" pytest fixture test in a method inside a class class Test_SB_Fixture: def test_sb_fixture_inside_class(self, sb: BaseCase): sb.open("seleniumbase.io/help_docs/install/") sb.type('input[aria-label="Search"]', "GUI Commander") sb.click('mark:contains("Commander")') sb.assert_title_contains("GUI / Commander") ================================================ FILE: examples/test_scrape_bing.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ScrapeBingTests(BaseCase): def test_scrape_bing(self): if self._multithreaded: self.open_if_not_url("about:blank") self.skip("Skipping test in multi-threaded mode.") self.open("www.bing.com/search?q=SeleniumBase+GitHub&qs=n&form=QBRE") self.wait_for_element("main h2 a") self.sleep(0.5) soup = self.get_beautiful_soup() titles = [item.text for item in soup.select("main h2 a")] print("\nSearch Result Headers:") for title in titles: if ( "seleniumbase/" in title.lower() or "SeleniumBase Docs" in title ): print(" " + title) links = [item["href"] for item in soup.select("main h2 a")] print("Search Result Links:") for link in links: if ( "github.com/seleniumbase" in link.lower() or "https://seleniumbase.io/" in link.lower() ): print(" " + link) self.click_if_visible('a[href="https://github.com/seleniumbase"]') print("Last Page = " + self.get_current_url()) ================================================ FILE: examples/test_select_options.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class SelectTestClass(BaseCase): def test_base(self): self.open("https://seleniumbase.io/demo_page") expected_option_texts = [ "Set to 25%", "Set to 50%", "Set to 75%", "Set to 100%" ] option_texts = self.get_select_options("select#mySelect") self.assert_equal(option_texts, expected_option_texts) expected_option_indexes = ["0", "1", "2", "3"] option_indexes = self.get_select_options( "select#mySelect", attribute="index" ) self.assert_equal(option_indexes, expected_option_indexes) expected_option_values = ["25%", "50%", "75%", "100%"] option_values = self.get_select_options( "select#mySelect", attribute="value" ) self.assert_equal(option_values, expected_option_values) for index, option_text in enumerate(option_texts): self.select_option_by_text("#mySelect", option_text) selected_value = self.get_attribute("#mySelect", "value") self.assert_equal(selected_value, option_values[index]) for index, option_value in enumerate(option_values): self.select_option_by_value("#mySelect", option_value) selected_value = self.get_attribute("#mySelect", "value") self.assert_equal(selected_value, option_values[index]) for index, option_index in enumerate(option_indexes): self.select_option_by_index("#mySelect", option_index) # assert_attribute() combines get_attribute() and assert_equal() # It also highlights the element when Demo Mode is enabled. self.assert_attribute("#mySelect", "value", option_values[index]) ================================================ FILE: examples/test_shadow_dom.py ================================================ """Shadow DOM test. First download files from PyPI. Then search for them on a multi-layered Shadow DOM page. This uses the "::shadow" selector for piercing shadow-root elements. Here's the URL that contains Shadow DOM: chrome://downloads/ """ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ShadowDomTests(BaseCase): def download_tar_file_from_pypi(self, package): self.open("https://pypi.org/project/%s/#files" % package) pkg_header = self.get_text("h1.package-header__name").strip() pkg_name = pkg_header.replace(" ", "-") tar_file = pkg_name + ".tar.gz" tar_selector = 'div#files a[href$="%s"]' % tar_file self.delete_downloaded_file_if_present(tar_file, browser=True) self.click(tar_selector) return tar_file def test_shadow_dom(self): if not self.browser == "chrome" or self.headless or self.recorder_mode: self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") # Download Python package files from PyPI file_name_1 = self.download_tar_file_from_pypi("sbase") file_name_2 = self.download_tar_file_from_pypi("seleniumbase") self.assert_downloaded_file(file_name_1, browser=True) self.assert_downloaded_file(file_name_2, browser=True) # Navigate to the Chrome downloads page. self.open("chrome://downloads/") # Shadow DOM selectors search_icon = ( "downloads-manager::shadow downloads-toolbar::shadow" " cr-toolbar::shadow cr-toolbar-search-field::shadow" " cr-icon-button" ) search_input = ( "downloads-manager::shadow downloads-toolbar::shadow" " cr-toolbar::shadow cr-toolbar-search-field::shadow" " #searchInput" ) clear_search_icon = ( "downloads-manager::shadow downloads-toolbar::shadow" " cr-toolbar::shadow cr-toolbar-search-field::shadow" " #clearSearch" ) file_link = ( "downloads-manager::shadow #downloadsList" " downloads-item::shadow #file-link" ) remove_button = ( "downloads-manager::shadow #downloadsList" " downloads-item::shadow #quick-remove" ) no_downloads_area = "downloads-manager::shadow #no-downloads" self.assert_element(search_icon) self.type(search_input, "sbase") self.assert_text(file_name_1, file_link) print("\n Download 1: %s" % self.get_text(file_link)) self.type(search_input, "seleniumbase") self.assert_text(file_name_2, file_link) print(" Download 2: %s" % self.get_text(file_link)) self.click(clear_search_icon) self.type(search_input, "fake-file.zzz") self.assert_text("No search results found", no_downloads_area) self.click(clear_search_icon) self.assert_element(remove_button) # Delete the downloaded files from the [Downloads Folder] self.delete_downloaded_file_if_present(file_name_1, browser=True) self.delete_downloaded_file_if_present(file_name_2, browser=True) ================================================ FILE: examples/test_show_file_choosers.py ================================================ """self.show_file_choosers() is used to show hidden file-upload fields. Verify that one can choose a file after the hidden input is visible.""" import os from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class FileUpload(BaseCase): def test_show_file_choosers(self): self.open("https://seleniumbase.io/apps/img_upload") self.wait_for_element('img[alt="ImgBB"]') choose_file_selector = 'input[type="file"]' uploaded_image = "#anywhere-upload-queue li.queue-item" self.assert_element_not_visible(choose_file_selector) self.show_file_choosers() self.highlight(choose_file_selector) self.assert_element(choose_file_selector) self.assert_attribute(choose_file_selector, "value", "") self.assert_element_not_visible(uploaded_image) dir_name = os.path.dirname(os.path.abspath(__file__)) my_file = "screenshot.png" file_path = os.path.join(dir_name, "example_logs/%s" % my_file) self.choose_file(choose_file_selector, file_path) if self.browser != "safari": seen_path = "%s\\%s" % ("C:\\fakepath", my_file) self.assert_attribute(choose_file_selector, "value", seen_path) self.demo_mode = True self.assert_element(uploaded_image) ================================================ FILE: examples/test_simple_login.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TestSimpleLogin(BaseCase): def test_simple_login(self): self.open("seleniumbase.io/simple/login") self.type("#username", "demo_user") self.type("#password", "secret_pass") self.click('a:contains("Sign in")') self.assert_exact_text("Welcome!", "h1") self.assert_element("img#image1") self.highlight("#image1") self.click_link("Sign out") self.assert_text("signed out", "#top_message") ================================================ FILE: examples/test_suite.py ================================================ """This test suite contains 2 passing tests and 2 failing tests.""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestSuite(BaseCase): def test_1(self): self.open("https://xkcd.com/1722/") self.assert_text("Debugging", "div#ctitle", timeout=4) for p in range(3): self.click('a[rel="next"]') self.assert_text("Linear Regression", "div#ctitle", timeout=4) @pytest.mark.expected_failure def test_2(self): print("\n(This test should fail)") self.open("https://xkcd.com/1373/") self.assert_text("FakeText", "div#ctitle", timeout=0.4) def test_3(self): self.open("https://xkcd.com/2224/") self.assert_text("Software Updates", "div#ctitle", timeout=4) self.open("https://xkcd.com/608/") self.assert_exact_text("Form", "div#ctitle", timeout=4) @pytest.mark.expected_failure def test_4(self): print("\n(This test should fail)") self.open("https://xkcd.com/2224/") self.assert_element("FakeElement.DoesNotExist", timeout=0.4) ================================================ FILE: examples/test_swag_labs.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class SwagLabsTests(BaseCase): def login_to_swag_labs(self, username="standard_user"): """Login to Swag Labs and verify success.""" self.open("https://www.saucedemo.com") if username not in self.get_text("#login_credentials"): self.fail("Invalid user for login: %s" % username) self.type("#user-name", username) self.type("#password", "secret_sauce") self.click('input[type="submit"]') self.assert_element("div.inventory_list") self.assert_element('.inventory_item:contains("Sauce Labs Backpack")') def test_swag_labs_basic_flow(self): """This test checks functional flow of the Swag Labs store.""" self.login_to_swag_labs(username="standard_user") # Verify that the "Test.allTheThings() T-Shirt" appears on the page item_name = "Test.allTheThings() T-Shirt" self.assert_text(item_name) # Verify that a reverse-alphabetical sort works as expected self.select_option_by_value("select.product_sort_container", "za") if item_name not in self.get_text("div.inventory_item"): self.fail('Sort Failed! Expecting "%s" on top!' % item_name) # Add the "Test.allTheThings() T-Shirt" to the cart self.assert_exact_text("Add to cart", "button.btn_inventory") item_price = self.get_text("div.inventory_item_price") self.click("button.btn_inventory") self.assert_exact_text("Remove", "button.btn_inventory") self.assert_exact_text("1", "span.shopping_cart_badge") # Verify your cart self.click("#shopping_cart_container a") self.assert_element('span:contains("Your Cart")') self.assert_text(item_name, "div.inventory_item_name") self.assert_exact_text("1", "div.cart_quantity") self.assert_exact_text("Remove", "button.cart_button") self.assert_element("button#continue-shopping") # Checkout - Add info self.click("button#checkout") self.assert_element('span:contains("Checkout: Your Information")') self.assert_element("button#cancel") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Rocks") self.type("#postal-code", "01720") # Checkout - Overview self.click("input#continue") self.assert_element('span:contains("Checkout: Overview")') self.assert_element("button#cancel") self.assert_text(item_name, "div.inventory_item_name") self.assert_text(item_price, "div.inventory_item_price") self.assert_exact_text("1", "div.cart_quantity") # Finish Checkout and verify that the cart is now empty self.click("button#finish") self.assert_exact_text("Thank you for your order!", "h2") self.click("#shopping_cart_container a") self.assert_element_absent("div.inventory_item_name") self.click("button#continue-shopping") self.assert_element_absent("span.shopping_cart_badge") def tearDown(self): self.save_teardown_screenshot() # Only if a test fails # Reset App State and Logout if the controls are present try: self.wait_for_ready_state_complete() if self.is_element_visible("#react-burger-menu-btn"): self.click("#react-burger-menu-btn") self.wait_for_element("a#reset_sidebar_link") self.js_click_if_present("a#reset_sidebar_link") self.js_click_if_present("a#logout_sidebar_link") except Exception: pass super().tearDown() ================================================ FILE: examples/test_tinymce.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TinyMceTests(BaseCase): def test_tinymce(self): if self.headless: self.open_if_not_url("about:blank") self.skip("Skip this test in headless mode!") self.open("https://seleniumbase.io/tinymce/") self.wait_for_element("div.mce-container-body") self.click('span:contains("File")') self.click('span:contains("New document")') self.click('span:contains("Paragraph")') self.click('span:contains("Heading 2")') self.switch_to_frame("iframe") self.add_text("#tinymce", "Automate anything with SeleniumBase!\n") self.switch_to_parent_frame() self.click("button i.mce-i-image") self.type('input[aria-label="Width"].mce-textbox', "300") image_url = "https://seleniumbase.github.io/img/sb_logo_10.png" self.type("input.mce-textbox", image_url + "\n") with self.frame_switch("iframe"): self.click("h2") self.post_message("Automate anything with SeleniumBase!") self.click('span:contains("File")') self.click('span:contains("Preview")') self.switch_to_frame('iframe[sandbox="allow-scripts"]') self.post_message("Learn SeleniumBase Today!") ================================================ FILE: examples/test_todomvc.py ================================================ from parameterized import parameterized from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TodoMVC(BaseCase): def setUp(self): super().setUp() self.open_new_window() def tearDown(self): self.save_teardown_screenshot() self.driver.close() super().tearDown() @parameterized.expand([["jquery"], ["react"], ["vue"]]) def test_todomvc(self, framework): self.open("https://todomvc.com/") self.clear_local_storage() self.click('a[href*="examples/%s/dist"]' % framework) self.assert_element("section.todoapp") self.assert_text("todos", "header h1") self.wait_for_ready_state_complete() title = self.get_title() self.assert_in(framework, title.lower()) new_todo_input = "input.new-todo" todo_count_span = "span.todo-count" self.wait_for_ready_state_complete() self.type(new_todo_input, "Learn Python\n") self.type(new_todo_input, "Learn JavaScript\n") self.type(new_todo_input, "Learn SeleniumBase\n") self.assert_text("3 items left", todo_count_span) self.check_if_unchecked("ul.todo-list li input") self.check_if_unchecked("ul.todo-list li:nth-of-type(2) input") self.check_if_unchecked("ul.todo-list li:nth-of-type(3) input") self.assert_text("0 items left", todo_count_span) self.click("button.clear-completed") self.assert_element_not_visible(todo_count_span) ================================================ FILE: examples/test_url_asserts.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class URLTestClass(BaseCase): def test_url_asserts(self): self.open("https://seleniumbase.io/help_docs/how_it_works/") self.assert_url("https://seleniumbase.io/help_docs/how_it_works/") self.assert_title_contains("How it Works") self.js_click('nav a:contains("Coffee Cart")') self.assert_url_contains("/coffee") self.assert_title("Coffee Cart") ================================================ FILE: examples/test_usefixtures.py ================================================ import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.mark.usefixtures("sb") class Test_UseFixtures: def test_usefixtures_on_class(self): if not hasattr(self, "sb"): print("This test is for pytest only!") return sb: BaseCase = self.sb sb.open("https://seleniumbase.io/realworld/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit sb.assert_text("Welcome!", "h1") sb.highlight("img#image1") # A fancier assert_element() call sb.click('a:contains("This Page")') # Use :contains() on any tag sb.save_screenshot_to_logs() # In "./latest_logs/" folder. sb.click_link("Sign out") # Link must be "a" tag. Not "button". sb.assert_element('a:contains("Sign in")') sb.assert_exact_text("You have been signed out!", "#top_message") ================================================ FILE: examples/test_verify_chromedriver.py ================================================ import sys from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class ChromedriverTests(BaseCase): def test_fail_if_versions_dont_match(self): self.open("about:blank") if self.browser != "chrome": print("\n This test is only for Chrome!") self.skip("This test is only for Chrome!") chrome_version = self.get_chrome_version() major_chrome_version = chrome_version.split(".")[0] chromedriver_version = self.get_chromedriver_version() major_chromedriver_version = chromedriver_version.split(".")[0] install_sb = "sbase get chromedriver %s" % major_chrome_version arg_join = " ".join(sys.argv) message = ( 'Your version of chromedriver: "%s"\n ' 'does not match your version of Chrome: "%s"\n' 'Run this command to fix that: "%s"' % (chromedriver_version, chrome_version, install_sb) ) if "--driver-version=" in arg_join or "--driver-version=" in arg_join: if int(major_chromedriver_version) != int(major_chrome_version): print("\nWarning -> " + message) elif int(major_chromedriver_version) != int(major_chrome_version): raise Exception(message) else: print( "\n* Chrome version: {%s}\n* Driver version: {%s}" % (chromedriver_version, chrome_version) ) ================================================ FILE: examples/test_window_switching.py ================================================ """ Sometimes tests open new tabs/windows, and you'll need to switch to them first in order to interact with them. The starting window is window(0). Then increments by 1. """ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TabSwitchingTests(BaseCase): def test_switch_to_tabs(self): self.open("about:blank") self.get_new_driver() self.open("data:text/html,

      Page A

      ") self.assert_text("Page A") self.open_new_window() self.open("data:text/html,

      Page B

      ") self.assert_text("Page B") self.switch_to_window(0) self.assert_text("Page A") self.assert_text_not_visible("Page B") self.switch_to_window(-1) self.assert_text("Page B") self.assert_text_not_visible("Page A") ================================================ FILE: examples/test_xfail.py ================================================ """Testing the @pytest.mark.xfail marker. https://docs.pytest.org/en/latest/skipping.html (The test is expected to fail, but don't fail the entire build for it.)""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class XFailTests(BaseCase): @pytest.mark.xfail def test_xfail(self): self.open("https://xkcd.com/376/") self.sleep(1) # Time to read the comic self.fail("There is a known bug here!") ================================================ FILE: examples/test_xkcd.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_xkcd(self): self.open("https://xkcd.com/353/") self.assert_title("xkcd: Python") self.assert_element('img[alt="Python"]') self.click('a[rel="license"]') self.assert_text("free to copy and reuse") self.go_back() self.click_link("About") self.assert_exact_text("xkcd.com", "h2") self.click_link("comic #249") self.assert_element('img[alt*="Chess"]') ================================================ FILE: examples/time_limit_test.py ================================================ import pytest from seleniumbase import BaseCase from seleniumbase import decorators BaseCase.main(__name__, __file__) class TimeLimitTests(BaseCase): @pytest.mark.expected_failure def test_runtime_limit_decorator(self): """This test fails on purpose to show the runtime_limit() decorator for code blocks that run longer than the time limit specified.""" print("\n(This test should fail)") self.open("https://xkcd.com/2511") with decorators.runtime_limit(0.7): self.sleep(0.95) @pytest.mark.expected_failure def test_set_time_limit_method(self): """This test fails on purpose to show the set_time_limit() method for tests that run longer than the time limit specified (seconds). The time-limit clock starts after the browser has fully launched, which is after pytest starts it's own internal clock for tests. Usage: (inside tests) => self.set_time_limit(SECONDS) Usage: (command-line) => --time-limit=SECONDS""" self.set_time_limit(2.2) # Fail test if time exceeds 2.2 seconds print("\n(This test should fail)") self.open("https://xkcd.com/1658") self.sleep(3) ================================================ FILE: examples/tour_examples/ReadMe.md ================================================

      SeleniumBase Tour

      🌏 Interactive Product Tours 🚎

      Increase SaaS Product Adoption by 10x or more.

      * SeleniumBase Tours utilize 5 JavaScript libraries for creating interactive walkthroughs on **any website**: > **[IntroJS](https://introjs.com/)**, **[Bootstrap Tour](http://bootstraptour.com/)**, **[DriverJS](https://kamranahmed.info/driver.js/)**, **[Shepherd](https://shepherdjs.dev/)**, and **[Hopscotch](https://linkedinattic.github.io/hopscotch/)**. A tour demo: (with autoplay)
      [SeleniumBase maps_introjs_tour.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/maps_introjs_tour.py) ```zsh cd examples/tour_examples pytest maps_introjs_tour.py --interval=1 ``` Here's a longer version:
      [SeleniumBase google_tour.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/google_tour.py) ```zsh cd examples/tour_examples pytest google_tour.py ``` > (From [GitHub => SeleniumBase/examples/tour_examples](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples)) ### Creating a new tour: #### To create a tour utilizing the Shepherd Library, use one of the following: ``self.create_shepherd_tour()`` OR ``self.create_tour(theme="shepherd")`` You can pass a custom theme to change the look & feel of Shepherd tours. Valid themes for Shepherd Tours are ``dark``, ``light`` / ``arrows``, ``default``, ``square``, and ``square-dark``. #### To create a tour utilizing the Bootstrap Tour Library, use one of the following: ``self.create_bootstrap_tour()`` OR ``self.create_tour(theme="bootstrap")`` #### To create a tour utilizing the IntroJS Library, use one of the following: ``self.create_introjs_tour()`` OR ``self.create_tour(theme="introjs")`` #### To create a tour utilizing the DriverJS Library, use one of the following: ``self.create_driverjs_tour()`` OR ``self.create_tour(theme="driverjs")`` #### To create a tour utilizing the Hopscotch Library, use one of the following: ``self.create_hopscotch_tour()`` OR ``self.create_tour(theme="hopscotch")`` ### Adding a step to a tour: #### To add a tour step, use the following: ``self.add_tour_step(message, css_selector, title, alignment, theme)`` > With the ``self.add_tour_step()`` method, you must first pass a message to display. You can then specify a web element to attach to (by using [CSS selectors](https://www.w3schools.com/cssref/css_selectors.asp)). If no element is specified, the tour step will tether to the top of the screen by default. You can also add an optional title above the message to display with the tour step, as well as change the theme for that step (Shepherd tours only), and even specify the alignment (which is the side of the element that you want the tour message to tether to). ### Playing a tour: You can play a tour by calling: ``self.play_tour(interval)`` > If you specify an ``interval`` (optional), the tour will automatically walk through each step after that many seconds have passed. All methods have the optional ``name`` argument, which is only needed if you're creating multiple tours at once. Then, when you're adding a step or playing a tour, SeleniumBase knows which tour you're referring too. You can avoid using the ``name`` arg for multiple tours if you play a tour before creating a new one. ### Here's how the code looks: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MyTourClass(BaseCase): def test_google_tour(self): if not self.undetectable: self.get_new_driver(undetectable=True) self.open('https://google.com/ncr') self.wait_for_element('input[title="Search"]') self.hide_elements("iframe") self.create_tour(theme="dark") self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours") self.add_tour_step("Type in your query here.", '[title="Search"]') self.play_tour() self.highlight_type('input[title="Search"]', "Google") self.wait_for_element('[role="listbox"]') # Wait for autocomplete self.create_tour(theme="light") self.add_tour_step("Then click to search.", '[value="Google Search"]') self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]') self.play_tour() ``` #### That code is from [google_tour.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/google_tour.py), which you can run from the ``tour_examples/`` folder with the following command: ```zsh pytest google_tour.py ``` ### Exporting a Tour: If you want to save the tour you created as a JavaScript file, use: ``self.export_tour()`` OR ``self.export_tour(name=None, filename="my_tour.js")`` > (``name`` is optional unless you gave custom names to your tours when you created them. ``filename`` is the name of the file to save the JavaScript to.) Once you've exported your tour, you can use it outside of SeleniumBase. You can even copy the tour's JavaScript code to the Console of your web browser to play the tour from there (you need to be on the correct web page for it to work). --------

      SeleniumBase

      ================================================ FILE: examples/tour_examples/bootstrap_google_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MyTourClass(BaseCase): def test_google_tour(self): if not self.undetectable: self.get_new_driver(undetectable=True) self.open("https://google.com/ncr") self.wait_for_element('[title="Search"]') self.hide_elements("iframe") self.create_bootstrap_tour() # OR self.create_tour(theme="bootstrap") self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours") self.add_tour_step("Type in your query here.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "Google") self.wait_for_element('[role="listbox"]') # Wait for autocomplete self.create_bootstrap_tour() self.add_tour_step("Then click to search.", '[value="Google Search"]') self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "GitHub\n") self.ad_block() self.wait_for_element("#search") self.create_bootstrap_tour() self.add_tour_step("3-second autoplay...") self.add_tour_step("Here's the next tour:") self.play_tour(interval=3) # Tour automatically continues after 3 sec self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput", timeout=20) self.wait_for_element("#minimap", timeout=20) self.wait_for_element("#zoom", timeout=20) self.wait_for_element('[aria-label="Zoom out"]') self.wait_for_element('[jsaction*="minimap.main;"]') self.sleep(0.5) self.create_bootstrap_tour() self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour") self.add_tour_step( "The location goes here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", '[aria-label="Zoom in"]', alignment="left", ) self.add_tour_step( "Or click here to zoom out.", '[aria-label="Zoom out"]', alignment="left", ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="End of Guided Tour" ) self.export_tour(filename="bootstrap_google_maps_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/bootstrap_xkcd_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_bootstrap_tour(self): self.open("https://xkcd.com/1117/") self.assert_element('img[alt="My Sky"]') self.create_bootstrap_tour() self.add_tour_step("Welcome to XKCD!") self.add_tour_step("This is the XKCD logo.", "#masthead img") self.add_tour_step("Here's the daily webcomic.", "#comic img") self.add_tour_step("This is the title.", "#ctitle", alignment="top") self.add_tour_step("Click here for the next comic.", 'a[rel="next"]') self.add_tour_step("Click here for the previous one.", 'a[rel="prev"]') self.add_tour_step("Learn about the author here.", 'a[rel="author"]') self.add_tour_step("Click for a random comic.", 'a[href*="/random/"]') self.add_tour_step("Thanks for taking this tour!") self.export_tour(filename="bootstrap_xkcd_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/driverjs_maps_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_create_tour(self): self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput", timeout=20) self.wait_for_element("#minimap", timeout=20) self.wait_for_element("#zoom", timeout=20) self.wait_for_element("#widget-zoom-out") self.wait_for_element('[jsaction*="minimap.main;"]') self.sleep(0.5) # Create a website tour using the DriverJS library # Same as: self.create_driverjs_tour() self.create_tour(theme="driverjs") self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour") self.add_tour_step( "The location goes here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", "#widget-zoom-in", alignment="left" ) self.add_tour_step( "Or click here to zoom out.", "#widget-zoom-out", alignment="left" ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="End of Guided Tour" ) self.export_tour() # The default name for exports is "my_tour.js" self.play_tour(interval=0) # If interval > 0, autoplay after N seconds ================================================ FILE: examples/tour_examples/google_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MyTourClass(BaseCase): def test_google_tour(self): if not self.undetectable: self.get_new_driver(undetectable=True) self.open("https://google.com/ncr") self.wait_for_element('[title="Search"]') self.hide_elements("iframe") # Create a website tour using the ShepherdJS library with "dark" theme # Same as: self.create_shepherd_tour(theme="dark") self.create_tour(theme="dark") self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours") self.add_tour_step("Type in your query here.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "Google") self.wait_for_element('[role="listbox"]') # Wait for autocomplete # Create a website tour using the ShepherdJS library with "light" theme # Same as: self.create_shepherd_tour(theme="light") self.create_tour(theme="light") self.add_tour_step("Then click to search.", '[value="Google Search"]') self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "GitHub\n") self.ad_block() self.wait_for_element("#search") # Create a website tour using the Bootstrap Tour JS library # Same as: self.create_bootstrap_tour() self.create_tour(theme="bootstrap") self.add_tour_step("3-second autoplay...") self.add_tour_step("Here's the next tour:") self.play_tour(interval=3) # Tour automatically continues after 3 sec self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput") self.wait_for_element("#minimap") self.wait_for_element("#zoom") self.wait_for_element('[aria-label="Zoom out"]') self.wait_for_element('[jsaction*="minimap.main;"]') self.sleep(0.5) # Create a website tour using the IntroJS library # Same as: self.create_introjs_tour() self.create_tour(theme="introjs") self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour") self.add_tour_step( "The location goes here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", '[aria-label="Zoom in"]', alignment="left", ) self.add_tour_step( "Or click here to zoom out.", '[aria-label="Zoom out"]', alignment="left", ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="End of Guided Tour" ) self.export_tour() # The default name for exports is "my_tour.js" self.play_tour() ================================================ FILE: examples/tour_examples/hopscotch_google_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MyTourClass(BaseCase): def test_google_tour(self): if not self.undetectable: self.get_new_driver(undetectable=True) self.open("https://google.com/ncr") self.wait_for_element('[title="Search"]') self.hide_elements("iframe") self.create_hopscotch_tour() # OR self.create_tour(theme="hopscotch") self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours") self.add_tour_step("Type in your query here.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "Google") self.wait_for_element('[role="listbox"]') # Wait for autocomplete self.create_hopscotch_tour() self.add_tour_step("Then click to search.", '[value="Google Search"]') self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "GitHub\n") self.ad_block() self.wait_for_element("#search") self.create_hopscotch_tour() self.add_tour_step("3-second autoplay...") self.add_tour_step("Here's the next tour:") self.play_tour(interval=3) # Tour automatically continues after 3 sec self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput", timeout=20) self.wait_for_element("#minimap", timeout=20) self.wait_for_element("#zoom", timeout=20) self.wait_for_element('[aria-label="Zoom out"]') self.wait_for_element('[jsaction*="minimap.main;"]') self.sleep(0.5) self.create_hopscotch_tour() self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour") self.add_tour_step( "The location goes here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", '[aria-label="Zoom in"]', alignment="left", ) self.add_tour_step( "Or click here to zoom out.", '[aria-label="Zoom out"]', alignment="left", ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="End of Guided Tour" ) self.export_tour(filename="hopscotch_google_maps_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/introjs_google_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MyTourClass(BaseCase): def test_google_tour(self): if not self.undetectable: self.get_new_driver(undetectable=True) self.open("https://google.com/ncr") self.wait_for_element('[title="Search"]') self.hide_elements("iframe") self.create_introjs_tour() # OR self.create_tour(theme="introjs") self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours") self.add_tour_step("Type in your query here.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "Google") self.wait_for_element('[role="listbox"]') # Wait for autocomplete self.create_introjs_tour() self.add_tour_step("Then click to search.", '[value="Google Search"]') self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "GitHub\n") self.ad_block() self.wait_for_element("#search") self.create_introjs_tour() self.add_tour_step("3-second autoplay...") self.add_tour_step("Here's the next tour:") self.play_tour(interval=3) # Tour automatically continues after 3 sec self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput", timeout=20) self.wait_for_element("#minimap", timeout=20) self.wait_for_element("#zoom", timeout=20) self.wait_for_element('[aria-label="Zoom out"]') self.wait_for_element('[jsaction*="minimap.main;"]') self.sleep(0.5) self.set_introjs_colors("#f26721", "#db5409") self.create_introjs_tour() self.add_tour_step("Welcome to Google Maps", title="SeleniumBase Tour") self.add_tour_step( "The location goes here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", '[aria-label="Zoom in"]', alignment="left", ) self.add_tour_step( "Or click here to zoom out.", '[aria-label="Zoom out"]', alignment="left", ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="End of Guided Tour" ) self.export_tour(filename="introjs_google_maps_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/maps_introjs_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTourClass(BaseCase): def test_google_maps_tour(self): self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput", timeout=20) self.wait_for_element("#minimap", timeout=20) self.wait_for_element("#zoom", timeout=20) self.wait_for_element('[aria-label="Zoom out"]') self.wait_for_element('[jsaction*="minimap.main;"]') self.create_tour(theme="introjs") self.add_tour_step( "Welcome to Google Maps", title="✅ SeleniumBase Tours 🌎" ) self.add_tour_step( "The location goes here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", '[aria-label="Zoom in"]', alignment="left", ) self.add_tour_step( "Or click here to zoom out.", '[aria-label="Zoom out"]', alignment="left", ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="🚃 End of Guided Tour 🚃", ) self.export_tour(filename="maps_introjs_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/octocat_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTourClass(BaseCase): def test_octocat_tour(self): self.maximize_window() self.open("https://seleniumbase.io/error_page/") self.wait_for_element("#parallax_octocat") self.create_tour(theme="bootstrap") self.add_tour_step("Welcome to the Octocat Tour!") self.add_tour_step("This is Octocat", "#parallax_octocat") self.add_tour_step("This is Octobi-Wan Catnobi", "#octobi_wan_catnobi") self.add_tour_step("

      Ooops!!!

      ", "#parallax_error_text") self.add_tour_step("This is a Star Wars speeder.", "#speeder") self.add_tour_step("This is a sign with a 500-Error", "#parallax_sign") self.add_tour_step( "This is not the page you're looking for.", 'img[alt*="404"]' ) self.add_tour_step("Have a great day!", title="😃 ☀️ 😁") self.add_tour_step("And may the Force be with you!", title="⭐") self.export_tour(filename="octocat_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/shepherd_google_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class MyTourClass(BaseCase): def test_google_tour(self): if not self.undetectable: self.get_new_driver(undetectable=True) self.open("https://google.com/ncr") self.wait_for_element('[title="Search"]') self.hide_elements("iframe") self.create_shepherd_tour(theme="dark") self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours") self.add_tour_step("Type in your query here.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "Google") self.wait_for_element('[role="listbox"]') # Wait for autocomplete self.create_shepherd_tour(theme="light") self.add_tour_step("Then click to search.", '[value="Google Search"]') self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]') self.play_tour() self.highlight_type('[title="Search"]', "GitHub\n") self.ad_block() self.wait_for_element("#search") self.create_shepherd_tour(theme="square-dark") self.add_tour_step("3-second autoplay...") self.add_tour_step("Here's the next tour:") self.play_tour(interval=3) # Tour automatically continues after 3 sec self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.wait_for_element("#searchboxinput", timeout=20) self.wait_for_element("#minimap", timeout=20) self.wait_for_element("#zoom", timeout=20) self.wait_for_element('[aria-label="Zoom out"]') self.wait_for_element('[jsaction*="minimap.main;"]') self.sleep(0.5) self.create_shepherd_tour(theme="dark") self.add_tour_step("Welcome to Google Maps!") self.add_tour_step( "Type in a location here.", "#searchboxinput", title="Search Box" ) self.add_tour_step( "Then click here to show it on the map.", "#searchbox-searchbutton", alignment="bottom", ) self.add_tour_step( "Or click here to get driving directions.", 'button[aria-label="Directions"]', alignment="bottom", theme="dark", ) self.add_tour_step( "Use this button to switch to Satellite view.", 'button[jsaction*="minimap.main;"]', alignment="right", ) self.add_tour_step( "Click here to zoom in.", '[aria-label="Zoom in"]', alignment="left", ) self.add_tour_step( "Or click here to zoom out.", '[aria-label="Zoom out"]', alignment="left", theme="light", ) if self.is_element_visible('button[jsaction*="settings.open;"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction*="settings.open;"]', alignment="right", ) elif self.is_element_visible('button[jsaction="navigationrail.more"]'): self.add_tour_step( "Use the Menu button to see more options.", 'button[jsaction="navigationrail.more"]', alignment="right", ) self.add_tour_step( "Or click here to see more Google apps.", '[aria-label="Google apps"]', alignment="left", ) self.add_tour_step( "Thanks for using SeleniumBase Tours!", title="End of Guided Tour", theme="light", ) self.export_tour(filename="shepherd_google_maps_tour.js") self.play_tour() ================================================ FILE: examples/tour_examples/xkcd_tour.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_create_tour(self): self.open("https://xkcd.com/1117/") self.assert_element('img[alt="My Sky"]') self.create_tour(theme="dark") self.add_tour_step("Welcome to XKCD!") self.add_tour_step("This is the XKCD logo.", "#masthead img") self.add_tour_step("Here's the daily webcomic.", "#comic img") self.add_tour_step("This is the title.", "#ctitle", alignment="top") self.add_tour_step("Click here for the next comic.", 'a[rel="next"]') self.add_tour_step("Click here for the previous one.", 'a[rel="prev"]') self.add_tour_step("Learn about the author here.", 'a[rel="author"]') self.add_tour_step("Click here for the license.", 'a[rel="license"]') self.add_tour_step("Click for a random comic.", 'a[href*="/random/"]') self.add_tour_step("Thanks for taking this tour!") self.export_tour(filename="xkcd_tour.js") self.play_tour() ================================================ FILE: examples/translations/ReadMe.md ================================================

      🌏 Translated Tests 🈺

      SeleniumBase supports the following 10 languages: English, Chinese, Dutch, French, Italian, Japanese, Korean, Portuguese, Russian, and Spanish. (Examples can be found in SeleniumBase/examples/translations) Multi-language tests run with **pytest** like other tests. Test methods have a one-to-one mapping to supported languages. Here's an example of a translated test: ```python # Chinese Translation from seleniumbase.translate.chinese import 硒测试用例 class 我的测试类(硒测试用例): def test_例子1(self): self.开启("https://zh.wikipedia.org/wiki/") self.断言标题("维基百科,自由的百科全书") self.断言元素('a[title="Wikipedia:关于"]') self.如果可见请单击('button[aria-label="关闭"]') self.如果可见请单击('button[aria-label="關閉"]') self.断言元素('span:contains("创建账号")') self.断言元素('span:contains("登录")') self.输入文本('input[name="search"]', "舞龍") self.单击('button:contains("搜索")') self.断言文本("舞龍", "#firstHeading") self.断言元素('img[src*="Chinese_draak.jpg"]') ``` Here's another example: ```python # Japanese Translation from seleniumbase.translate.japanese import セレニウムテストケース class 私のテストクラス(セレニウムテストケース): def test_例1(self): self.を開く("https://ja.wikipedia.org/wiki/") self.テキストを確認する("ウィキペディア") self.要素を確認する('[title*="ウィキペディアへようこそ"]') self.JS入力('input[name="search"]', "アニメ") self.クリックして("#searchform button") self.テキストを確認する("アニメ", "#firstHeading") self.JS入力('input[name="search"]', "寿司") self.クリックして("#searchform button") self.テキストを確認する("寿司", "#firstHeading") self.要素を確認する('img[src*="Various_sushi"]') ```

      Translation API 🈺

      You can use SeleniumBase to selectively translate the method names of any test from one language to another with the console scripts interface. Additionally, the ``import`` line at the top of the Python file will change to import the new ``BaseCase``. Example: ``BaseCase`` becomes ``CasoDeTeste`` when a test is translated into Portuguese. ```zsh seleniumbase translate ``` ```zsh * Usage: seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION] * Languages: ``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` ``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` ``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` ``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` ``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` * Actions: ``-p`` / ``--print`` (Print translation output to the screen) ``-o`` / ``--overwrite`` (Overwrite the file being translated) ``-c`` / ``--copy`` (Copy the translation to a new ``.py`` file) * Options: ``-n`` (include line Numbers when using the Print action) * Examples: Translate test_1.py into Chinese and only print the output: >>> seleniumbase translate test_1.py --zh -p Translate test_2.py into Portuguese and overwrite the file: >>> seleniumbase translate test_2.py --pt -o Translate test_3.py into Dutch and make a copy of the file: >>> seleniumbase translate test_3.py --nl -c * Output: Translates a SeleniumBase Python file into the language specified. Method calls and ``import`` lines get swapped. Both a language and an action must be specified. The ``-p`` action can be paired with one other action. When running with ``-c`` (or ``--copy``) the new file name will be the original name appended with an underscore plus the 2-letter language code of the new language. (Example: Translating ``test_1.py`` into Japanese with ``-c`` will create a new file called ``test_1_ja.py``.) ``` --------

      ================================================ FILE: examples/translations/chinese_test_1.py ================================================ # Chinese Language Test from seleniumbase.translate.chinese import 硒测试用例 硒测试用例.main(__name__, __file__) class 我的测试类(硒测试用例): def test_例子1(self): self.开启("https://zh.wikipedia.org/wiki/") self.断言标题("维基百科,自由的百科全书") self.断言元素('a[title="Wikipedia:关于"]') self.如果可见请单击('button[aria-label="关闭"]') self.如果可见请单击('button[aria-label="關閉"]') self.断言元素('span:contains("创建账号")') self.断言元素('span:contains("登录")') self.输入文本('input[name="search"]', "舞龍") self.单击('button:contains("搜索")') self.断言文本("舞龍", "#firstHeading") self.断言元素('img[src*="Chinese_draak.jpg"]') self.回去() self.输入文本('input[name="search"]', "麻婆豆腐") self.单击('button:contains("搜索")') self.断言文本("麻婆豆腐", "#firstHeading") self.断言元素('figure:contains("一家中餐館的麻婆豆腐")') self.回去() self.输入文本('input[name="search"]', "精武英雄") self.单击('button:contains("搜索")') self.断言元素('img[src*="Fist_of_legend.jpg"]') self.断言文本("李连杰", 'li a[title="李连杰"]') ================================================ FILE: examples/translations/dutch_test_1.py ================================================ # Dutch Language Test from seleniumbase.translate.dutch import Testgeval Testgeval.main(__name__, __file__) class MijnTestklasse(Testgeval): def test_voorbeeld_1(self): self.openen("https://nl.wikipedia.org/wiki/Hoofdpagina") self.controleren_element('a[title*="Welkom voor nieuwkomers"]') self.controleren_tekst("Welkom op Wikipedia", "td.hp-welkom") self.typ("#searchform input", "Stroopwafel") self.klik("#searchform button") self.controleren_tekst("Stroopwafel", "#firstHeading") self.controleren_element('img[src*="Stroopwafels"]') self.typ("#searchform input", "Rijksmuseum Amsterdam") self.klik("#searchform button") self.controleren_tekst("Rijksmuseum", "#firstHeading") self.controleren_element('img[src*="Rijksmuseum"]') self.terug() self.controleren_url_bevat("Stroopwafel") self.vooruit() self.controleren_url_bevat("Rijksmuseum") ================================================ FILE: examples/translations/english_test_1.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_example_1(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("#inventory_container") self.assert_exact_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_exact_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click('button:contains("Remove")') # HTML innerText self.assert_text_not_visible("Backpack", "div.cart_item") self.js_click("a#logout_sidebar_link") ================================================ FILE: examples/translations/french_test_1.py ================================================ # French Language Test from seleniumbase.translate.french import CasDeBase CasDeBase.main(__name__, __file__) class MaClasseDeTest(CasDeBase): def test_exemple_1(self): self.ouvrir("https://fr.wikipedia.org/wiki/") self.vérifier_texte("Wikipédia") self.vérifier_élément('[alt="Wikipédia"]') self.cliquer_si_affiché('button[aria-label="Fermer"]') self.js_taper("#searchform input", "Crème brûlée") self.cliquer("#searchform button") self.vérifier_texte("Crème brûlée", "#firstHeading") self.vérifier_élément('img[alt*="Crème brûlée"]') self.js_taper("#searchform input", "Jardin des Tuileries") self.cliquer("#searchform button") self.vérifier_texte("Jardin des Tuileries", "#firstHeading") self.vérifier_élément('img[alt*="Jardin des Tuileries"]') self.retour() self.vérifier_url_contient("brûlée") self.en_avant() self.vérifier_url_contient("Jardin") ================================================ FILE: examples/translations/italian_test_1.py ================================================ # Italian Language Test from seleniumbase.translate.italian import CasoDiProva CasoDiProva.main(__name__, __file__) class MiaClasseDiTest(CasoDiProva): def test_esempio_1(self): self.apri("https://it.wikipedia.org/wiki/") self.verificare_testo("Wikipedia") self.verificare_elemento('a[title="Lingua italiana"]') self.digitare('input[name="search"]', "Pizza") self.fare_clic("#searchform button") self.verificare_testo("Pizza", "#firstHeading") self.verificare_elemento('figure img[src*="pizza"]') self.digitare('input[name="search"]', "Colosseo") self.fare_clic("#searchform button") self.verificare_testo("Colosseo", "#firstHeading") self.verificare_elemento('figure img[src*="Colosseo"]') self.indietro() self.verificare_url_contiene("Pizza") self.avanti() self.verificare_url_contiene("Colosseo") ================================================ FILE: examples/translations/japanese_test_1.py ================================================ # Japanese Language Test from seleniumbase.translate.japanese import セレニウムテストケース セレニウムテストケース.main(__name__, __file__) class 私のテストクラス(セレニウムテストケース): def test_例1(self): self.を開く("https://ja.wikipedia.org/wiki/") self.テキストを確認する("ウィキペディア") self.要素を確認する('[title*="ウィキペディアへようこそ"]') self.JS入力('input[name="search"]', "アニメ") self.クリックして("#searchform button") self.テキストを確認する("アニメ", "#firstHeading") self.JS入力('input[name="search"]', "寿司") self.クリックして("#searchform button") self.テキストを確認する("寿司", "#firstHeading") self.要素を確認する('img[src*="Various_sushi"]') self.JS入力("#searchInput", "レゴランド・ジャパン") self.クリックして("#searchform button") self.要素を確認する('img[src*="LEGOLAND_JAPAN"]') self.リンクテキストを確認する("名古屋城") self.リンクテキストをクリックします("テーマパーク") self.テキストを確認する("テーマパーク", "#firstHeading") ================================================ FILE: examples/translations/korean_test_1.py ================================================ # Korean Language Test from seleniumbase.translate.korean import 셀레늄_테스트_케이스 셀레늄_테스트_케이스.main(__name__, __file__) class 테스트_클래스(셀레늄_테스트_케이스): def test_실시예_1(self): self.열기("https://ko.wikipedia.org/wiki/") self.텍스트_확인("위키백과") self.요소_확인('[title="위키백과:소개"]') self.JS_입력("#searchform input", "김치") self.클릭("#searchform button") self.텍스트_확인("김치", "#firstHeading") self.요소_확인('img[src*="Various_kimchi.jpg"]') self.링크_텍스트_확인("한국 요리") self.JS_입력("#searchform input", "비빔밥") self.클릭("#searchform button") self.텍스트_확인("비빔밥", "#firstHeading") self.요소_확인('img[src*="Dolsot-bibimbap.jpg"]') self.링크_텍스트를_클릭합니다("돌솥비빔밥") self.텍스트_확인("돌솥비빔밥", "#firstHeading") ================================================ FILE: examples/translations/portuguese_test_1.py ================================================ # Portuguese Language Test from seleniumbase.translate.portuguese import CasoDeTeste CasoDeTeste.main(__name__, __file__) class MinhaClasseDeTeste(CasoDeTeste): def test_exemplo_1(self): self.abrir("https://pt.wikipedia.org/wiki/") self.verificar_texto("Wikipédia") self.verificar_elemento('[title="Língua portuguesa"]') self.digitar("#searchform input", "João Pessoa") self.clique("#searchform button") self.verificar_texto("João Pessoa", "#firstHeading") self.verificar_elemento('img[alt*="João Pessoa"]') self.digitar("#searchform input", "Florianópolis") self.clique("#searchform button") self.verificar_texto("Florianópolis", "h1#firstHeading") self.verificar_elemento('td:contains("Avenida Beira-Mar")') self.voltar() self.verificar_url_contém("João_Pessoa") self.atualizar_a_página() self.js_digitar("#searchform input", "Teatro Amazonas") self.clique("#searchform button") self.verificar_texto("Teatro Amazonas", "#firstHeading") self.verificar_texto_do_link("Festival Amazonas de Ópera") ================================================ FILE: examples/translations/pytest.ini ================================================ [pytest] # Display console output. Disable cacheprovider: addopts = --capture=tee-sys -p no:cacheprovider # Skip these directories during test collection: norecursedirs = .* build dist recordings temp assets # Ignore DeprecationWarning, PytestUnknownMarkWarning filterwarnings = ignore::pytest.PytestWarning ignore:.*U.*mode is deprecated:DeprecationWarning # Configure the junit_family option explicitly: junit_family = legacy # Set pytest discovery rules: # (Most of the rules here are similar to the default rules.) # (Inheriting unittest.TestCase could override these rules.) python_files = test_*.py *_test.py *_tests.py *_suite.py *_test_*.py python_classes = Test* *Test* *Test *Tests *Suite python_functions = test_* # Common pytest markers used in examples: # (pytest may require marker registration to prevent warnings.) # (Future versions may turn those marker warnings into errors.) markers = marker1: custom marker marker2: custom marker marker3: custom marker marker_test_suite: custom marker expected_failure: custom marker local: custom marker remote: custom marker offline: custom marker develop: custom marker qa: custom marker ci: custom marker e2e: custom marker ready: custom marker smoke: custom marker deploy: custom marker active: custom marker master: custom marker release: custom marker staging: custom marker production: custom marker ================================================ FILE: examples/translations/russian_test_1.py ================================================ # Russian Language Test from seleniumbase.translate.russian import ТестНаСелен ТестНаСелен.main(__name__, __file__) class МойТестовыйКласс(ТестНаСелен): def test_пример_1(self): self.открыть("https://ru.wikipedia.org/wiki/") self.подтвердить_элемент('[title="Русский язык"]') self.подтвердить_текст("Википедия", "div.main-wikimedia-header") self.введите("#searchInput", "МГУ") self.нажмите("#searchButton") self.подтвердить_текст("университет", "#firstHeading") self.подтвердить_элемент('img[alt*="Главное здание МГУ"]') self.введите("#searchInput", "приключения Шурика") self.нажмите("#searchButton") self.подтвердить_текст("Операция «Ы» и другие приключения Шурика") self.подтвердить_элемент('img[alt="Постер фильма"]') self.назад() self.подтвердить_URL_содержит("университет") self.вперед() self.подтвердить_URL_содержит("Шурика") ================================================ FILE: examples/translations/spanish_test_1.py ================================================ # Spanish Language Test from seleniumbase.translate.spanish import CasoDePrueba CasoDePrueba.main(__name__, __file__) class MiClaseDePrueba(CasoDePrueba): def test_ejemplo_1(self): self.abrir("https://es.wikipedia.org/wiki/") self.verificar_texto("Wikipedia") self.verificar_elemento('[title="Wikipedia:Bienvenidos"]') self.escriba('[name="search"]', "Parque de Atracciones Tibidabo") self.haga_clic('button:contains("Buscar")') self.verificar_texto("Tibidabo", "#firstHeading") self.verificar_elemento('img[src*="Tibidabo"]') self.escriba('input[name="search"]', "Palma de Mallorca") self.haga_clic('button:contains("Buscar")') self.verificar_texto("Palma de Mallorca", "#firstHeading") self.verificar_elemento('img[src*="Palma"]') self.volver() self.verificar_url_contiene("Tibidabo") self.adelante() self.verificar_url_contiene("Mallorca") ================================================ FILE: examples/uc_cdp_events.py ================================================ from rich.pretty import pprint from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc", "--uc-cdp") class CDPTests(BaseCase): def add_cdp_listener(self): # (To print everything, use "*". Otherwise select specific headers.) # self.driver.add_cdp_listener("*", lambda data: print(pformat(data))) self.driver.add_cdp_listener( "Network.requestWillBeSentExtraInfo", lambda data: pprint(data) ) def click_turnstile_and_verify(sb): sb.uc_gui_handle_captcha() sb.assert_element("img#captcha-success", timeout=3) sb.highlight("img#captcha-success", loops=8) def test_display_cdp_events(self): if not (self.undetectable and self.uc_cdp_events): self.get_new_driver(undetectable=True, uc_cdp_events=True) url = "seleniumbase.io/apps/turnstile" self.uc_open_with_reconnect(url, 2) self.add_cdp_listener() self.click_turnstile_and_verify() self.sleep(1) self.refresh() self.sleep(1.2) ================================================ FILE: examples/unit_tests/ReadMe.md ================================================

      pytest-specific unit tests

      The tests in this folder are for basic verification of the SeleniumBase framework with pytest. ================================================ FILE: examples/unit_tests/verify_framework.py ================================================ """ SeleniumBase Verification """ import sys headless = "--headless" if "win32" in sys.platform: headless = "--chs" if __name__ == "__main__": from pytest import main main([__file__, "-v", "-s"]) def test_simple_cases(pytester): """Verify a simple passing test and a simple failing test. The failing test is marked as xfail to have it skipped.""" pytester.makepyfile( """ import pytest from seleniumbase import BaseCase class MyTestCase(BaseCase): def test_passing(self): self.assert_equal('yes', 'yes') @pytest.mark.xfail def test_failing(self): self.assert_equal('yes', 'no') """ ) result = pytester.inline_run(headless, "--rs", "-v") assert result.matchreport("test_passing").passed assert result.matchreport("test_failing").skipped def test_basecase(pytester): pytester.makepyfile( """ from seleniumbase import BaseCase class MyTest(BaseCase): def test_basecase(self): self.open("data:text/html,

      Hello

      ") self.assert_element("html > body") # selector self.assert_text("Hello", "body p") # text, selector self.type("input", "Goodbye") # selector, text self.click("body p") # selector """ ) result = pytester.inline_run(headless, "-v") assert result.matchreport("test_basecase").passed def test_run_with_dashboard(pytester): pytester.makepyfile( """ from seleniumbase import BaseCase class MyTestCase(BaseCase): def test_1_passing(self): self.assert_equal('yes', 'yes') def test_2_failing(self): self.assert_equal('yes', 'no') def test_3_skipped(self): self.skip("Skip!") """ ) result = pytester.inline_run(headless, "--rs", "--dashboard", "-v") assert result.matchreport("test_1_passing").passed assert result.matchreport("test_2_failing").failed assert result.matchreport("test_3_skipped").skipped def test_sb_fixture(pytester): pytester.makepyfile( """ def test_sb_fixture(sb): sb.open("data:text/html,

      Hello

      ") sb.assert_element("html > body") # selector sb.assert_text("Hello", "body p") # text, selector sb.type("input", "Goodbye") # selector, text sb.click("body p") # selector """ ) result = pytester.inline_run(headless, "-v") assert result.matchreport("test_sb_fixture").passed def test_request_sb_fixture(pytester): pytester.makepyfile( """ def test_request_sb_fixture(request): sb = request.getfixturevalue('sb') sb.open("data:text/html,

      Hello

      ") sb.assert_element("html > body") # selector sb.assert_text("Hello", "body p") # text, selector sb.type("input", "Goodbye") # selector, text sb.click("body p") # selector sb.tearDown() """ ) result = pytester.inline_run(headless, "-v") assert result.matchreport("test_request_sb_fixture").passed def check_outcome_field(outcomes, field_name, expected_value): field_value = outcomes.get(field_name, 0) assert field_value == expected_value, ( "outcomes.%s has an unexpected value! " 'Expected "%s" but got "%s"!' % (field_name, expected_value, field_value) ) def assert_outcomes( result, passed=1, skipped=0, failed=0, xfailed=0, xpassed=0, rerun=0, ): outcomes = result.parseoutcomes() check_outcome_field(outcomes, "passed", passed) check_outcome_field(outcomes, "skipped", skipped) check_outcome_field(outcomes, "failed", failed) check_outcome_field(outcomes, "xfailed", xfailed) check_outcome_field(outcomes, "xpassed", xpassed) check_outcome_field(outcomes, "rerun", rerun) def test_rerun_failures(pytester): pytester.makepyfile( """ from seleniumbase import BaseCase class MyTestCase(BaseCase): def test_passing(self): self.assert_equal('yes', 'yes') def test_failing(self): self.assert_equal('yes', 'no') """ ) result = pytester.runpytest(headless, "--reruns=1", "--rs", "-v") assert_outcomes(result, passed=1, failed=1, rerun=1) def test_browser_launcher(pytester): pytester.makepyfile( """ import sys from seleniumbase import get_driver b = None if "win32" in sys.platform: b = "chs" def test_browser_launcher(): success = False try: driver = get_driver("chrome", headless=True, binary_location=b) driver.get("data:text/html,

      Data URL

      ") source = driver.page_source assert "Data URL" in source success = True # No errors finally: driver.quit() assert success """ ) result = pytester.inline_run(headless, "-v") assert result.matchreport("test_browser_launcher").passed def test_framework_components(pytester): pytester.makepyfile( """ import sys from seleniumbase import get_driver from seleniumbase import js_utils from seleniumbase import page_actions b = None if "win32" in sys.platform: b = "chs" def test_framework_components(): success = False try: driver = get_driver("chrome", headless=True, binary_location=b) driver.get('data:text/html,

      Data URL

      ') source = driver.page_source assert "Data URL" in source assert page_actions.is_element_visible(driver, "h1.top") js_utils.highlight_with_js(driver, "h1.top", 2, "") success = True # No errors finally: driver.quit() assert success """ ) result = pytester.inline_run(headless, "-v", "-s") assert result.matchreport("test_framework_components").passed ================================================ FILE: examples/upgrade_chromedriver.py ================================================ """This script installs the chromedriver version that matches your Chrome. On newer versions of Python, you may replace "testdir" with "pytester". (Run with "pytest")""" import subprocess class TestUpgradeChromedriver: def basic_run(self, testdir): testdir.makepyfile( """ from seleniumbase import BaseCase class MyTestCase(BaseCase): def test_passing(self): pass """ ) return testdir def upgrade_chromedriver(self, testdir): testdir.makepyfile( """ import subprocess from seleniumbase import BaseCase class MyTestCase(BaseCase): def test_upgrade(self): chrome_version = self.get_chrome_version() major_chrome_ver = chrome_version.split(".")[0] chromedriver_ver = self.get_chromedriver_version() major_chromedriver_ver = chromedriver_ver.split(".")[0] if major_chromedriver_ver != major_chrome_ver: subprocess.check_call( "sbase get chromedriver %s" % major_chrome_ver, shell=True ) """ ) return testdir def print_versions_of_chromedriver_and_chrome(self, testdir): testdir.makepyfile( """ from seleniumbase import BaseCase class MyTestCase(BaseCase): def test_print_versions(self): chrome_version = self.get_chrome_version() major_chrome_ver = chrome_version.split(".")[0] chromedriver_ver = self.get_chromedriver_version() major_chromedriver_ver = chromedriver_ver.split(".")[0] print( "\\n* Now using chromedriver %s with Chrome %s" % (chromedriver_ver, chrome_version) ) if major_chromedriver_ver == major_chrome_ver: print( "* SUCCESS: " "The chromedriver version is compatible " "with Chrome!" ) elif major_chromedriver_ver < major_chrome_ver: print("* !!! Version Mismatch !!!") print( "* The version of chromedriver is too low!\\n" "* Try upgrading to chromedriver %s manually:\\n" "* >>> sbase get chromedriver %s <<<" % (major_chrome_ver, major_chrome_ver) ) else: print("* !!! Version Mismatch !!!") print( "* The version of chromedriver is too high!\\n" "* Try downgrading to chromedriver %s manually:\\n" "* >>> sbase get chromedriver %s <<<" % (major_chrome_ver, major_chrome_ver) ) """ ) return testdir def test_upgrade_chromedriver(self, testdir): # Find out if the installed chromedriver version works with Chrome subprocess.check_call("seleniumbase get chromedriver", shell=True) testdir = self.basic_run(testdir) result = testdir.inline_run("--headless", "-s") # Upgrades as needed try: assert result.matchreport("test_passing").passed except Exception: # Install the compatibility version of chromedriver install_command = "seleniumbase get chromedriver 72.0.3626.69" subprocess.check_call(install_command, shell=True) testdir = self.upgrade_chromedriver(testdir) testdir.inline_run("--headless", "-s") # Print the final installed versions of chromedriver and Chrome testdir = self.print_versions_of_chromedriver_and_chrome(testdir) testdir.inline_run("--headless", "-s") if __name__ == "__main__": from pytest import main main([__file__]) ================================================ FILE: examples/upload_file_test.py ================================================ """Testing the self.choose_file() and self.assert_attribute() methods.""" import os from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class FileUploadButtonTests(BaseCase): def test_file_upload_button(self): self.open("https://seleniumbase.io/w3schools/file_upload") self.click("button#runbtn") self.switch_to_frame("iframeResult") zoom_in = 'input[type="file"]{zoom: 1.6;-moz-transform: scale(1.6);}' self.add_css_style(zoom_in) self.highlight('input[type="file"]') dir_name = os.path.dirname(os.path.abspath(__file__)) my_file = "screenshot.png" file_path = os.path.join(dir_name, "example_logs/%s" % my_file) self.assert_attribute("#myFile", "value", "") self.choose_file('input[type="file"]', file_path) self.assert_attribute("#myFile", "value", "C:\\fakepath\\%s" % my_file) self.highlight('input[type="file"]') ================================================ FILE: examples/user_agent_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class UserAgentTests(BaseCase): def test_user_agent(self): self.open("data:text/html,

      ") user_agent_detected = self.get_user_agent() self.set_text_content("h1", user_agent_detected) self.highlight("h1") original_user_agent = user_agent_detected if not self.user_agent: # Using the built-in user-agent string print("\n\nUser-Agent: %s" % user_agent_detected) else: # User-agent was overridden using: --agent=STRING print("\n\nUser-Agent override: %s" % user_agent_detected) if self.headed: self.sleep(2.75) # Now change the user-agent using "execute_cdp_cmd()" if not self.is_chromium(): msg = "\n* execute_cdp_cmd() is only for Chromium browsers" print(msg) self.skip(msg) print("\n--------------------------") try: self.execute_cdp_cmd( "Network.setUserAgentOverride", { "userAgent": "Mozilla/5.0 " "(Nintendo Switch; WifiWebAuthApplet) " "AppleWebKit/606.4 (KHTML, like Gecko) " "NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393" }, ) self.open("about:blank") self.sleep(0.1) # Enough to see that page was refreshed self.open("data:text/html,

      ") user_agent_detected = self.get_user_agent() self.set_text_content("h1", user_agent_detected) self.highlight("h1") print("\nUser-Agent override: %s" % user_agent_detected) if self.headed: self.sleep(2.75) finally: # Reset the user-agent back to the original self.execute_cdp_cmd( "Network.setUserAgentOverride", {"userAgent": original_user_agent}, ) ================================================ FILE: examples/verify_undetected.py ================================================ """Determine if your browser is detectable by anti-bot services. Some sites use scripts to detect Selenium, and then block you. To evade detection, add --uc as a pytest command-line option.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__, "--uc") class UndetectedTest(BaseCase): def test_browser_is_undetected(self): url = "https://gitlab.com/users/sign_in" if not self.undetectable: self.get_new_driver(undetectable=True) self.uc_open_with_reconnect(url) self.uc_gui_click_captcha() self.assert_text("Username", '[for="user_login"]', timeout=3) self.post_message("SeleniumBase wasn't detected", duration=4) self._print("\n Success! Website did not detect Selenium! ") ================================================ FILE: examples/visual_testing/ReadMe.md ================================================

      SeleniumBase

      Automated Visual Regression Testing

      Automated Visual Regression Testing can help you detect when the layout of a web page has changed. Instead of comparing pixels from screenshots, layout differences can be detected by comparing HTML tags and attributes with a baseline. If a change is detected, it could mean that something broke, the web page was redesigned, or dynamic content changed.

      (Watch the tutorial on YouTube)

      To handle automated visual testing, SeleniumBase uses the ``self.check_window()`` method, which can set visual baselines for comparison and then compare the latest versions of web pages to the existing baseline. The first time a test calls ``self.check_window()`` with a unique ``name`` parameter, the visual baseline is set, which means a folder is created with the following files:
    • page_url.txt -> The URL of the current window
    • baseline.png -> The baseline screenshot (PNG)
    • tags_level1.txt -> HTML tags from the window
    • tags_level2.txt -> HTML tags + attribute names
    • tags_level3.txt -> HTML tags + attribute names+values
    • After the first time ``self.check_window()`` is called, later calls will compare the HTML tags and attributes of the latest window to the ones from the first call (*or to the ones from the call when the baseline was last reset*). Additionally, a ``latest.png`` screenshot is saved in the same folder, which can help you determine if/when the existing baseline needs to be reset. Here's an example call: ```python self.check_window(name="first_test)", level=3) ``` On the first run (or if the baseline is being set/reset) the "level" doesn't matter because that's only used for comparing the current layout to the existing baseline. Here's how the level system works:
    • level=0 -> DRY RUN ONLY - Will perform a comparison to the baseline, and print out any differences that are found, but won't fail the test even if differences exist.
    • level=1 -> HTML tags are compared to tags_level1.txt
    • level=2 -> HTML tags and attribute names are compared to tags_level2.txt
    • level=3 -> HTML tags and attribute names+values are compared to tags_level3.txt
    • As shown, Level-3 is the most strict, Level-1 is the least strict. If the comparisons from the latest window to the existing baseline don't match, the current test will fail, except for Level-0 checks, which print Level-3 results without failing the test. You can reset the visual baseline on the command line by adding the following parameter at runtime: ```zsh --visual_baseline ``` As long as ``--visual_baseline`` is used on the command line while running tests, the ``self.check_window()`` method cannot fail because it will rebuild the visual baseline rather than comparing the html tags of the latest run to the existing baseline. If there are any expected layout changes to a website that you're testing, you'll need to reset the baseline to prevent unnecessary failures. ``self.check_window()`` will fail with "Page Domain Mismatch Failure" if the domain of the current URL doesn't match the domain of the baseline URL. If you want to use ``self.check_window()`` to compare a web page to a later version of itself in the same test, add the ``baseline=True`` parameter to your first ``self.check_window()`` call to use that as the baseline. (This only makes sense if you're calling ``self.check_window()`` more than once with the same "name" parameter in the same test.) Automated Visual Testing with ``self.check_window()`` is not very effective for websites that have dynamic content because that changes the layout and structure of web pages. For those pages, you're much better off using regular SeleniumBase functional testing, unless you can remove the dynamic content before performing the comparison, (such as by using ``self.ad_block()`` to remove dynamic ad content on a web page). Example usage of ``self.check_window()`` with different levels: ```python self.check_window(name="testing", level=0) self.check_window(name="xkcd_home", level=1) self.check_window(name="github_page", level=2) self.check_window(name="wikipedia_page", level=3) self.check_window(name="helloworld", baseline=True) ### Do something that may change the web page self.check_window(name="helloworld", level=3) ``` Here's an example where clicking a button makes a hidden element visible: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayoutTest(BaseCase): def test_applitools_layout_change_failure(self): self.open('https://applitools.com/helloworld?diff1') print('\nCreating baseline in "visual_baseline" folder.') self.check_window(name="helloworld", baseline=True) # Click a button that changes the text of an element self.click('a[href="?diff1"]') # Click a button that makes a hidden element visible self.click("button") self.check_window(name="helloworld", level=3) ``` Here's the output of that: (Text changes do not impact visual comparisons) ``` AssertionError: First differing element 39: ['div', [['class', ['section', 'hidden-section', 'image-section']]]] ['div', [['class', ['section', 'image-section']]]] - ['div', [['class', ['section', 'hidden-section', 'image-section']]]], ? ------------------ + ['div', [['class', ['section', 'image-section']]]], * *** Exception: Visual Diff Failure: * HTML tag attribute values don't match the baseline! ``` Here's an example where a button is removed from a web page: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayoutTest(BaseCase): def test_python_home_layout_change_failure(self): self.open('https://python.org/') print('\nCreating baseline in "visual_baseline" folder.') self.check_window(name="python_home", baseline=True) # Remove the "Donate" button self.remove_element('a.donate-button') self.check_window(name="python_home", level=3) ``` Here's the output of that: ``` AssertionError: First differing element 33: ['a', [['class', ['donate-button']], ['href', '/psf/donations/']]] ['div', [['class', ['options-bar']]]] - ['a', [['class', ['donate-button']], ['href', '/psf/donations/']]], - 'display: list-item; opacity: 0.995722;']]], ? ------------------- + 'display: list-item;']]], * *** Exception: Visual Diff Failure: * HTML tag attribute values don't match the baseline! ``` Here's the ``side_by_side.html`` file for that, (from the ``./latest_logs/`` folder), which shows a visual comparison of the two screenshots as a result of the missing "Donate" button: Here's another example, where a web site logo is resized: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayoutTest(BaseCase): def test_xkcd_layout_change_failure(self): self.open('https://xkcd.com/554/') print('\nCreating baseline in "visual_baseline" folder.') self.check_window(name="xkcd_554", baseline=True) # Change height: (83 -> 130) , Change width: (185 -> 120) self.set_attribute('[alt="xkcd.com logo"]', "height", "130") self.set_attribute('[alt="xkcd.com logo"]', "width", "120") self.check_window(name="xkcd_554", level=3) ``` Here's the output of that: ``` AssertionError: First differing element 22: ['img[30 chars]['height', '83'], ['src', '/s/0b7742.png'], ['width', '185']]] ['img[30 chars]['height', '130'], ['src', '/s/0b7742.png'], ['width', '120']]] - ['height', '83'], ? ^ + ['height', '130'], ? ^ + - ['width', '185']]], ? ^^ + ['width', '120']]], ? ^^ * *** Exception: Visual Diff Failure: * HTML tag attribute values don't match the baseline! ``` To run the example (from [examples/visual_testing/](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/visual_testing/)) with a pytest HTML Report, use: ```zsh pytest test_layout_fail.py --html=report.html ``` Here's what the pytest HTML Report looks like:
      [](https://seleniumbase.github.io/cdn/img/visual_testing_report_2.png) -------- In conclusion, open source automated visual testing tools are being built directly into test frameworks, and this trend is growing. Just like many years ago when free Wi-Fi at coffee shops replaced Internet cafes that charged money for Internet access, open source tools for visual testing will replace their paid counterparts in time. You'll remember this next time you're sipping your Starbucks® Pumpkin Spice Latte with your free Internet access, instead of paying for Internet at cybercafes. ================================================ FILE: examples/visual_testing/__init__.py ================================================ ================================================ FILE: examples/visual_testing/case_plans/layout_test.VisualLayoutTests.test_applitools_layout_change.md ================================================ ``layout_test.py::VisualLayoutTests::test_applitools_layout_change`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://applitools.com/helloworld?diff1.
      Call ``check_window()`` with ``baseline=True``.
      Click the button that changes the text of an element.
      Call ``check_window()`` three times for ``level=1``, ``level=2``, and ``level=3``. | No issues are detected because a text change should not affect ``check_window()`` | | 2 | Click the button that makes a hidden element visible.
      Call ``check_window()`` three times for ``level=1``, ``level=2``, and ``level=3``, but wrap the third call with ``self.assert_raises(Exception):``. | No exceptions are raised because the first two calls should pass and the third one was wrapped with ``self.assert_raises(Exception):``. | ================================================ FILE: examples/visual_testing/case_plans/python_home_test.VisualLayoutTests.test_python_home_layout_change.md ================================================ ``python_home_test.py::VisualLayoutTests::test_python_home_layout_change`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://python.org/.
      Call ``check_window()`` with ``baseline=True``. | | | 2 | Remove the ``Donate`` button using ``remove_element(SELECTOR)``.
      Call ``check_window()`` with ``level=0``. | The test detects that the ``Donate`` button was removed. The test does not fail because the check was set to ``level=0`` (print-only).
      A ``side_by_side_NAME.html`` file appears in the specific ``latest_logs/`` folder of the test. | ================================================ FILE: examples/visual_testing/case_plans/test_layout_fail.VisualLayoutFailureTests.test_applitools_change.md ================================================ ``test_layout_fail.py::VisualLayoutFailureTests::test_applitools_change`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://applitools.com/helloworld?diff1.
      Call ``check_window()`` with ``baseline=True``. | | | 2 | Click the button that makes a hidden element visible.
      Call ``check_window()`` with ``level=3``. | The test fails because the element attribute has changed.
      A ``side_by_side.html`` file appears in the specific ``latest_logs/`` folder of the test. | ================================================ FILE: examples/visual_testing/case_plans/test_layout_fail.VisualLayoutFailureTests.test_xkcd_logo_change.md ================================================ ``test_layout_fail.py::VisualLayoutFailureTests::test_xkcd_logo_change`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://xkcd.com/554/.
      Call ``check_window()`` with ``baseline=True``. | | | 2 | Resize the logo using ``set_attribute()``.
      Call ``check_window()`` with ``level=3``. | The test fails because the logo has changed.
      A ``side_by_side.html`` file appears in the specific ``latest_logs/`` folder of the test. | ================================================ FILE: examples/visual_testing/case_plans/test_layout_fail.VisualLayout_FixtureTests.test_python_home_change.md ================================================ ``test_layout_fail.py::VisualLayout_FixtureTests::test_python_home_change`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://python.org/.
      Call ``check_window()`` with ``baseline=True``. | | | 2 | Remove the ``Donate`` button using ``remove_element(SELECTOR)``.
      Call ``check_window()`` with ``level=3``. | The test fails because the ``Donate`` button was removed.
      A ``side_by_side.html`` file appears in the specific ``latest_logs/`` folder of the test. | ================================================ FILE: examples/visual_testing/case_plans/xkcd_visual_test.VisualLayoutTests.test_xkcd_layout_change.md ================================================ ``xkcd_visual_test.py::VisualLayoutTests::test_xkcd_layout_change`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://xkcd.com/554/.
      Call ``check_window()`` with ``baseline=True``. | | | 2 | Resize the logo using ``set_attribute()``.
      Call ``check_window()`` with ``level=0``. | The test detects that the logo has changed. The test does not fail because the check was set to ``level=0`` (print-only).
      A ``side_by_side_NAME.html`` file appears in the specific ``latest_logs/`` folder of the test. | ================================================ FILE: examples/visual_testing/layout_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayoutTests(BaseCase): def test_xkcd_layout_change(self): self.demo_mode = False # (It would interfere with html comparisons) self.open("https://xkcd.com/1424/") print('\nCreating baseline in "visual_baseline" folder.') self.sleep(0.08) self.check_window(name="xkcd", baseline=True) # Go to a different comic self.open("https://xkcd.com/1425/") # Verify html tags match the baseline self.check_window(name="xkcd", level=1) # Verify html tags and attribute names match the baseline self.check_window(name="xkcd", level=2) # Verify html tags and attribute values don't match the baseline with self.assert_raises(Exception): self.check_window(name="xkcd", level=3) # Now that we know the Exception was raised as expected, # let's print out the comparison results by running a Level-0 check. # (NOTE: Running with level-0 will print but NOT raise an Exception.) self.check_window(name="xkcd", level=0) ================================================ FILE: examples/visual_testing/python_home_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayoutTests(BaseCase): def test_python_home_layout_change(self): self.open("https://python.org/") print('\nCreating baseline in "visual_baseline" folder.') self.check_window(name="python_home", baseline=True) # Remove the "Donate" button self.remove_element("a.donate-button") self.check_window(name="python_home", level=0) ================================================ FILE: examples/visual_testing/test_layout_fail.py ================================================ """Visual Layout Testing with different Syntax Formats""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayout_FixtureTests: def test_python_home_change(self, sb): sb.open("https://python.org/") print('\nCreating baseline in "visual_baseline" folder.') sb.check_window(name="python_home", baseline=True) # Remove the "Donate" button sb.remove_element("a.donate-button") print("(This test should fail)") # due to missing button sb.check_window(name="python_home", level=3) class VisualLayoutFailureTests(BaseCase): def test_xkcd_logo_change(self): self.open("https://xkcd.com/554/") print('\nCreating baseline in "visual_baseline" folder.') self.check_window(name="xkcd_554", baseline=True) # Change height: (83 -> 110) , Change width: (185 -> 120) self.set_attribute('[alt="xkcd.com logo"]', "height", "110") self.set_attribute('[alt="xkcd.com logo"]', "width", "120") print("(This test should fail)") # due to a resized logo self.check_window(name="xkcd_554", level=3) ================================================ FILE: examples/visual_testing/xkcd_visual_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class VisualLayoutTests(BaseCase): def test_xkcd_layout_change(self): self.open("https://xkcd.com/554/") print('\nCreating baseline in "visual_baseline" folder.') self.check_window(name="xkcd_554", baseline=True) # Change height: (83 -> 130) , Change width: (185 -> 120) self.set_attribute('[alt="xkcd.com logo"]', "height", "130") self.set_attribute('[alt="xkcd.com logo"]', "width", "120") self.check_window(name="xkcd_554", level=0) ================================================ FILE: examples/wordle_test.py ================================================ """Solve Wordle with SeleniumBase.""" import ast import random import requests from seleniumbase import BaseCase class WordleTests(BaseCase): word_list = [] def initialize_word_list(self): txt_file = "https://seleniumbase.github.io/cdn/txt/wordle_words.txt" word_string = requests.get(txt_file, timeout=3).text self.word_list = ast.literal_eval(word_string) def modify_word_list(self, word, letter_status): new_word_list = [] correct_letters = [] present_letters = [] for i in range(len(word)): if letter_status[i] == "correct": correct_letters.append(word[i]) for w in self.word_list: if w[i] == word[i]: new_word_list.append(w) self.word_list = new_word_list new_word_list = [] for i in range(len(word)): if letter_status[i] == "present": present_letters.append(word[i]) for w in self.word_list: if word[i] in w and word[i] != w[i]: new_word_list.append(w) self.word_list = new_word_list new_word_list = [] for i in range(len(word)): if letter_status[i] == "absent": if ( word[i] not in correct_letters and word[i] not in present_letters ): for w in self.word_list: if word[i] not in w: new_word_list.append(w) else: for w in self.word_list: if word[i] != w[i]: new_word_list.append(w) self.word_list = new_word_list new_word_list = [] def test_wordle(self): if self.headless: self.open_if_not_url("about:blank") self.skip("Skip this test in headless mode!") self.open("https://www.nytimes.com/games/wordle/index.html") self.click_if_visible("button.purr-blocker-card__button", timeout=2) self.click_if_visible('button:contains("Play")', timeout=2) self.click_if_visible('svg[data-testid="icon-close"]', timeout=2) self.remove_elements("div.place-ad") self.initialize_word_list() random.seed() word = random.choice(self.word_list) num_attempts = 0 found_word = False for attempt in range(6): num_attempts += 1 if len(self.word_list) == 0: self.fail("Today's word was not found in my dictionary!") word = random.choice(self.word_list) letters = [] for letter in word: letters.append(letter) button = 'button[data-key="%s"]' % letter self.click(button) button = 'button[class*="oneAndAHalf"]' self.click(button) row = ( 'div[class*="Board"] div[class*="Row-module"]:nth-of-type(%s) ' % num_attempts ) tile = row + 'div:nth-child(%s) div[class*="module_tile__"]' self.wait_for_element(tile % "5" + '[data-state$="t"]') self.wait_for_element(tile % "5" + '[data-animation="idle"]') letter_status = [] for i in range(1, 6): letter_eval = self.get_attribute(tile % str(i), "data-state") letter_status.append(letter_eval) if letter_status.count("correct") == 5: found_word = True break self.word_list.remove(word) self.modify_word_list(word, letter_status) self.save_screenshot_to_logs() if found_word: print('\nWord: "%s"\nAttempts: %s' % (word.upper(), num_attempts)) else: print('Final guess: "%s" (Not the correct word!)' % word.upper()) self.fail("Unable to solve for the correct word in 6 attempts!") self.sleep(3) if __name__ == "__main__": from pytest import main main([__file__, "-s"]) ================================================ FILE: examples/xpath_test.py ================================================ """Test that SeleniumBase can autodetect and use xpath selectors.""" from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class XPathTests(BaseCase): def test_xpath(self): self.open("https://seleniumbase.io/demo_page") self.assert_element('//h1[(text()="Demo Page")]') self.type('//*[@id="myTextInput"]', "XPath Test!") self.click('//button[starts-with(text(),"Click Me")]') self.assert_element('//button[contains(., "Purple")]') self.assert_text("SeleniumBase", "//table/tbody/tr[1]/td[2]/h2") ================================================ FILE: examples/youtube_search_test.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class YouTubeSearchTests(BaseCase): def test_youtube_autocomplete_results(self): """Verify YouTube autocomplete search results.""" if self.headless or self.browser == "safari": self.open_if_not_url("about:blank") print("\n Unsupported mode for this test.") self.skip("Unsupported mode for this test.") self.open("https://www.youtube.com/c/MichaelMintz") search_term = "seleniumbase" search_selector = 'input[name="search_query"]' results_selector = '[role="listbox"]' self.click_if_visible('button[aria-label="Close"]') self.sleep(0.25) self.double_click(search_selector) self.sleep(0.25) self.type(search_selector, search_term) self.sleep(0.25) # First verify that an autocomplete result exists self.assert_element(results_selector) self.sleep(0.25) top_results = self.get_text(results_selector) # Now verify that the autocomplete result is good self.assert_true( search_term in top_results, 'Expected text "%s" not found in top results! ' 'Actual text was "%s"!' % (search_term, top_results), ) self.sleep(1) ================================================ FILE: help_docs/ReadMe.md ================================================

      SeleniumBase

      Help Docs

      🚀 Start | 📊 Dashboard
      🏰 Features | 🎛️ Options
      📚 Examples | 📱 Emulator
      🪄 Console Scripts | 🌐 Grid
      📘 Methods / APIs | 🚎 Tours
      🔠 Syntax Formats | 🤖 CI/CD
      ♻️ Boilerplates | 🗾 Locale Codes
      ❇️ JS Manager | 🖼️ Visual Testing
      🌏 Translator | 🛂 Dialog Boxes
      🔴 Recorder | 🖥️ Device Farm
      🎭 Stealthy Playwright Mode
      🎞️ Slides | 📶 Chart Maker
      🎖️ GUI | 👤 UC Mode
      🐙 CDP Mode

      --------

      Table of Contents (seleniumbase.io)

      --------

      Demo Pages / Web Examples

      --------

      Presentations

      --------

      GitHub Pages (seleniumbase.dev)

      --------

      SeleniumBase

      ================================================ FILE: help_docs/behave_gui.md ================================================

      SeleniumBase Behave GUI / Commander 🐝🎖️

      🐝🎖️ The SeleniumBase Behave GUI lets you run behave scripts from a Desktop GUI.
      🐝🎖️ To launch it, call ``sbase behave-gui`` or ``sbase gui-behave``: ```zsh > sbase behave-gui * Starting the SeleniumBase Behave Commander GUI App... ``` 🐝🎖️ SeleniumBase Behave GUI loads the same tests that are found by: ```zsh behave -d ``` 🐝🎖️ You can customize which tests are loaded by passing additional args: ```zsh sbase behave-gui [OPTIONAL PATH or TEST FILE] ``` 🐝🎖️ Here are examples of customizing test collection: ```zsh sbase behave-gui # all tests sbase behave-gui -i=calculator # tests with "calculator" in the name sbase behave-gui features/ # tests located in the "features/" folder sbase behave-gui features/calculator.feature # tests in that feature ``` 🐝🎖️ Once launched, you can further customize which tests to run and what settings to use. There are various controls for changing settings, modes, and other "behave" command line options that are specific to SeleniumBase. You can also set additional options that don't have a visible toggle. When you're ready to run the selected tests with the specified options, click on the Run Selected Tests button. 🐝⚪ With the Dashboard enabled, you'll get one of these: --------
      To learn more about SeleniumBase, check out the Docs Site:
      SeleniumBase.io Docs
      All the code is on GitHub:
      SeleniumBase on GitHub ================================================ FILE: help_docs/case_plans.md ================================================

      SeleniumBase Case Plans 🗂️

      🗂️ SeleniumBase Case Plans is Test Case Management Software that uses Markdown tables for displaying test plans directly in GitHub (and other source code management systems that support Markdown format). 🗂️ The ``case_summary.md`` file is generated from individual Case Plans that exist in the ``case_plans/`` folders of your repository. (See the example below to learn how the Case Summary file may look.) -------- > **Example of a ``case_summary.md`` file:**

      Summary of existing Case Plans

      | | | | | - | -: | - | | 🔵 | 8 | Case Plans with customized tables | | ⭕ | 2 | Case Plans using boilerplate code | | 🚧 | 1 | Case Plan that is missing a table | --------

      🔎 (Click rows to expand) 🔍

      🔵 basic_test.py::MyTestClass::test_basics | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Remove the ``Backpack`` from the cart. | The ``Backpack`` is no longer in the cart. | | 5 | Log out from the website. | Logout was successful. |
      🔵 list_assert_test.py::MyTestClass::test_assert_list_of_elements | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/demo_page. | | | 2 | Use ``self.assert_elements_present("head", "style", "script")`` to verify that multiple elements are present in the HTML. | The assertion is successful. | | 3 | Use ``self.assert_elements("h1", "h2", "h3")`` to verify that multiple elements are visible. | The assertion is successful. | | 4 | Use ``self.assert_elements(["#myDropdown", "#myButton", "#svgRect"])`` to verify that multiple elements are visible. | The assertion is successful. |
      locale_code_test.py::LocaleCodeTests::test_locale_code | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Perform Action 1 | Verify Action 1 | | 2 | Perform Action 2 | Verify Action 2 |
      🔵 my_first_test.py::MyTestClass::test_swag_labs | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Click on the ``CHECKOUT`` button.
      Enter user details and click ``CONTINUE``. | The ``Backpack`` is seen in the cart on the ``CHECKOUT: OVERVIEW`` page. | | 5 | Click on the ``FINISH`` button. | There is a ``Thank You`` message and a ``Pony Express`` delivery logo. | | 6 | Log out from the website. | Logout was successful. |
      proxy_test.py::ProxyTests::test_proxy | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Perform Action 1 | Verify Action 1 | | 2 | Perform Action 2 | Verify Action 2 |
      🔵 shadow_root_test.py::ShadowRootTest::test_shadow_root | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/other/shadow_dom.
      Click each tab and verify the text contained within the Shadow Root sections. | Tab 1 text: ``Content Panel 1``
      Tab 2 text: ``Content Panel 2``
      Tab 3 text: ``Content Panel 3`` |
      🚧 test_agent.py::UserAgentTests::test_user_agent
      🔵 test_calculator.py::CalculatorTests::test_6_times_7_plus_12_equals_54 | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/apps/calculator.
      Perform the following calculation: ``6 × 7 + 12`` | The output is ``54`` after pressing ``=`` |
      🔵 test_demo_site.py::DemoSiteTests::test_demo_site | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/demo_page | | | 2 | Assert the title of the current web page.
      Assert that a given element is visible on the page.
      Assert that a text substring appears in an element's text. | The assertions were successful. | | 3 | Type text into various text fields and then verify. | The assertions were successful. | | 4 | Verify that a hover dropdown link changes page text. | The assertion was successful. | | 5 | Verify that a button click changes text on the page. | The assertion was successful. | | 6 | Verify that an SVG element is located on the page. | The assertion was successful. | | 7 | Verify that a slider control updates a progress bar. | The assertion was successful. | | 8 | Verify that a "select" option updates a meter bar. | The assertion was successful. | | 9 | Assert an element located inside an iFrame. | The assertion was successful. | | 10 | Assert text located inside an iFrame. | The assertion was successful. | | 11 | Verify that clicking a radio button selects it. | The assertion was successful. | | 12 | Verify that clicking an empty checkbox makes it selected. | The assertion was successful. | | 13 | Verify clicking on multiple elements with one call. | The assertions were successful. | | 14 | Verify that clicking an iFrame checkbox selects it. | The assertions were successful. | | 15 | Verify that Drag and Drop works. | The assertion was successful. | | 16 | Assert link text. | The assertion was successful. | | 17 | Verify clicking on link text. | The action was successful. | | 18 | Assert exact text in an element. | The assertion was successful. | | 19 | Highlight a page element. | The action was successful. | | 20 | Verify that Demo Mode works. | The assertion was successful. |
      🔵 test_login.py::SwagLabsLoginTests::test_swag_labs_login | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Log out from the website. | Logout was successful. |
      🔵 test_mfa_login.py::TestMFALogin::test_mfa_login | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Open https://seleniumbase.io/realworld/login
      Enter credentials and Sign In. | Sign In was successful. | | 2 | Click the ``This Page`` button.
      Save a screenshot to the logs. | | | 3 | Click to Sign Out | Sign Out was successful. |
      -------- 🗂️ Before you can generate a ``case_summary.md`` file that includes your existing Case Plans, first you'll need to select which existing tests you want to create boilerplate Case Plans from. For that, you can use the SeleniumBase Case Plans GUI: ```zsh sbase caseplans ``` 🗂️ Once you are running the Case Plans GUI, select the existing tests that need Case Plans, and then click: ``Generate boilerplate Case Plans for selected tests missing them``. For each selected test that didn't already have a Case Plan file, one will be generated. Each new Case Plan file starts with default boilerplate code with a Markdown table. Eg: ```zsh ``proxy_test.py::ProxyTests::test_proxy`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Perform Action 1 | Verify Action 1 | | 2 | Perform Action 2 | Verify Action 2 | ``` 🗂️ When rendered as a Markdown table, the result looks like this: ``proxy_test.py::ProxyTests::test_proxy`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Perform Action 1 | Verify Action 1 | | 2 | Perform Action 2 | Verify Action 2 | 🗂️ Markdown tables are flexible, but must be constructed correctly to be displayed. For a Markdown table to render, it's important that you place pipes (``|``), dashes (``-``), and spaces in the correct locations. If you want a line break in a step, use ``
      ``. If you want an empty step, put a space between pipes, eg: ``| |``. 🗂️ Here's an example of a Case Plan for [my_first_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py): ``my_first_test.py::MyTestClass::test_swag_labs`` --- | # | Step Description | Expected Result | | - | ---------------- | --------------- | | 1 | Log in to https://www.saucedemo.com with ``standard_user``. | Login was successful. | | 2 | Click on the ``Backpack`` ``ADD TO CART`` button. | The button text changed to ``REMOVE``. | | 3 | Click on the cart icon. | The ``Backpack`` is seen in the cart. | | 4 | Click on the ``CHECKOUT`` button.
      Enter user details and click ``CONTINUE``. | The ``Backpack`` is seen in the cart on the ``CHECKOUT: OVERVIEW`` page. | | 5 | Click on the ``FINISH`` button. | There is a ``Thank you`` message. | | 6 | Log out from the website. | Logout was successful. | 🗂️ After you've created some Case Plans, you can use the ``Generate Summary of existing Case Plans`` button in the Case Plans GUI to generate the Case Plans Summary file. 🗂️ The generated Case Plans summary file, ``case_summary.md``, gets created in the same location where the Case Plans GUI was launched. This is NOT the same location where individual Case Plan boilerplates are generated, which is in the ``case_plans/`` folders. The ``case_plans/`` folders are generated where individual tests live, which means that if you have your tests in multiple folders, then you could also have multiple ``case_plans/`` folders. A ``case_summary.md`` file may look like this when rendered: 🗂️ When calling ``sbase caseplans``, you can provide additional arguments to limit the tests that appear in the list. The same discovery rules apply as when using ``pytest``. Eg: ```zsh sbase caseplans sbase caseplans -k agent sbase caseplans -m marker2 sbase caseplans test_suite.py sbase caseplans offline_examples/ ``` --------
      To learn more about SeleniumBase, check out the Docs Site:
      SeleniumBase.io Docs
      All the code is on GitHub:
      SeleniumBase on GitHub ================================================ FILE: help_docs/cdp_mode_methods.md ================================================

      SeleniumBase CDP Mode Methods (CDP Mode API Reference)

      Here's a list of SeleniumBase CDP Mode method definitions, which are defined in **[sb_cdp.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/core/sb_cdp.py)** ### 🐙 CDP Mode API / Methods ```python sb.cdp.get(url, **kwargs) sb.cdp.open(url, **kwargs) # Same as sb.cdp.get(url, **kwargs) sb.cdp.reload(ignore_cache=True, script_to_evaluate_on_load=None) sb.cdp.refresh(*args, **kwargs) sb.cdp.get_event_loop() sb.cdp.get_rd_host() # Returns the remote-debugging host sb.cdp.get_rd_port() # Returns the remote-debugging port sb.cdp.get_rd_url() # Returns the remote-debugging URL sb.cdp.get_endpoint_url() # Same as sb.cdp.get_rd_url() sb.cdp.get_port() # Same as sb.cdp.get_rd_port() sb.cdp.get_websocket_url() # Returns the websocket URL sb.cdp.add_handler(event, handler) sb.cdp.find_element(selector, best_match=False, timeout=None) sb.cdp.find(selector, best_match=False, timeout=None) sb.cdp.locator(selector, best_match=False, timeout=None) sb.cdp.find_element_by_text(text, tag_name=None, timeout=None) sb.cdp.find_all(selector, timeout=None) sb.cdp.find_elements_by_text(text, tag_name=None) sb.cdp.select(selector, timeout=None) sb.cdp.select_all(selector, timeout=None) sb.cdp.find_elements(selector, timeout=None) sb.cdp.find_visible_elements(selector, timeout=None) sb.cdp.click(selector, timeout=None) sb.cdp.click_if_visible(selector) sb.cdp.click_visible_elements(selector, limit=0) sb.cdp.click_nth_element(selector, number) sb.cdp.click_nth_visible_element(selector, number) sb.cdp.click_with_offset(selector, x, y, center=False) sb.cdp.click_link(link_text) sb.cdp.go_back() sb.cdp.go_forward() sb.cdp.get_navigation_history() sb.cdp.tile_windows(windows=None, max_columns=0) sb.cdp.grant_permissions(permissions, origin=None) sb.cdp.grant_all_permissions() sb.cdp.reset_permissions() sb.cdp.get_all_cookies(*args, **kwargs) sb.cdp.set_all_cookies(*args, **kwargs) sb.cdp.save_cookies(*args, **kwargs) sb.cdp.load_cookies(*args, **kwargs) sb.cdp.clear_cookies() sb.cdp.sleep(seconds) sb.cdp.bring_active_window_to_front() sb.cdp.bring_to_front() sb.cdp.get_active_element() sb.cdp.get_active_element_css() sb.cdp.click_active_element() sb.cdp.mouse_click(selector, timeout=None) sb.cdp.nested_click(parent_selector, selector) sb.cdp.get_nested_element(parent_selector, selector) sb.cdp.select_option_by_text(dropdown_selector, option) sb.cdp.select_option_by_index(dropdown_selector, option) sb.cdp.select_option_by_value(dropdown_selector, option) sb.cdp.flash(selector, duration=1, color="44CC88", pause=0) sb.cdp.highlight(selector) sb.cdp.focus(selector) sb.cdp.highlight_overlay(selector) sb.cdp.get_parent(element) sb.cdp.remove_element(selector) sb.cdp.remove_from_dom(selector) sb.cdp.remove_elements(selector) sb.cdp.send_keys(selector, text, timeout=None) sb.cdp.press_keys(selector, text, timeout=None) sb.cdp.type(selector, text, timeout=None) sb.cdp.set_value(selector, text, timeout=None) sb.cdp.clear_input(selector, timeout=None) sb.cdp.clear(selector, timeout=None) sb.cdp.submit(selector) sb.cdp.evaluate(expression) sb.cdp.execute_script(expression) sb.cdp.js_dumps(obj_name) sb.cdp.maximize() sb.cdp.minimize() sb.cdp.medimize() sb.cdp.set_window_rect(x, y, width, height) sb.cdp.reset_window_size() sb.cdp.open_new_window(url=None, switch_to=True) sb.cdp.switch_to_window(window) sb.cdp.switch_to_newest_window() sb.cdp.open_new_tab(url=None, switch_to=True) sb.cdp.switch_to_tab(tab) sb.cdp.switch_to_newest_tab() sb.cdp.close_active_tab() sb.cdp.get_active_tab() sb.cdp.get_tabs() sb.cdp.get_window() sb.cdp.get_text(selector) sb.cdp.get_title() sb.cdp.get_current_url() sb.cdp.get_origin() sb.cdp.get_html(include_shadow_dom=True) sb.cdp.get_page_source(include_shadow_dom=True) sb.cdp.get_user_agent() sb.cdp.get_cookie_string() sb.cdp.get_locale_code() sb.cdp.get_local_storage_item(key) sb.cdp.get_session_storage_item(key) sb.cdp.get_screen_rect() sb.cdp.get_window_rect() sb.cdp.get_window_size() sb.cdp.get_window_position() sb.cdp.get_element_rect(selector, timeout=None) sb.cdp.get_element_size(selector, timeout=None) sb.cdp.get_element_position(selector, timeout=None) sb.cdp.get_gui_element_rect(selector, timeout=None) sb.cdp.get_gui_element_center(selector, timeout=None) sb.cdp.get_document() sb.cdp.get_flattened_document() sb.cdp.get_element_attributes(selector) sb.cdp.get_element_attribute(selector, attribute) sb.cdp.get_attribute(selector, attribute) sb.cdp.get_element_html(selector) sb.cdp.get_mfa_code(totp_key=None) sb.cdp.enter_mfa_code(selector, totp_key=None, timeout=None) sb.cdp.activate_messenger() sb.cdp.set_messenger_theme(theme="default", location="default") sb.cdp.post_message(message, duration=None, pause=True, style="info") sb.cdp.set_locale(locale) sb.cdp.set_local_storage_item(key, value) sb.cdp.set_session_storage_item(key, value) sb.cdp.set_attributes(selector, attribute, value) sb.cdp.is_attribute_present(selector, attribute, value=None) sb.cdp.is_online() sb.cdp.solve_captcha() sb.cdp.click_captcha() sb.cdp.gui_press_key(key) sb.cdp.gui_press_keys(keys) sb.cdp.gui_write(text) sb.cdp.gui_click_x_y(x, y, timeframe=0.25) sb.cdp.gui_click_element(selector, timeframe=0.25) sb.cdp.gui_click_with_offset(selector, x, y, timeframe=0.25, center=False) sb.cdp.gui_click_captcha() sb.cdp.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.35) sb.cdp.gui_drag_and_drop(drag_selector, drop_selector, timeframe=0.35) sb.cdp.gui_click_and_hold(selector, timeframe=0.35) sb.cdp.gui_hover_x_y(x, y) sb.cdp.gui_hover_element(selector) sb.cdp.gui_hover_and_click(hover_selector, click_selector) sb.cdp.hover_element(selector) sb.cdp.hover_and_click(hover_selector, click_selector) sb.cdp.internalize_links() sb.cdp.is_checked(selector) sb.cdp.is_selected(selector) sb.cdp.check_if_unchecked(selector) sb.cdp.select_if_unselected(selector) sb.cdp.uncheck_if_checked(selector) sb.cdp.unselect_if_selected(selector) sb.cdp.is_element_present(selector) sb.cdp.is_element_visible(selector) sb.cdp.is_text_visible(text, selector="body") sb.cdp.is_exact_text_visible(text, selector="body") sb.cdp.wait_for_text(text, selector="body", timeout=None) sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None) sb.cdp.wait_for_element_visible(selector, timeout=None) sb.cdp.wait_for_element(selector, timeout=None) sb.cdp.wait_for_element_not_visible(selector, timeout=None) sb.cdp.wait_for_element_absent(selector, timeout=None) sb.cdp.wait_for_any_of_elements_visible(*args, **kwargs) sb.cdp.wait_for_any_of_elements_present(*args, **kwargs) sb.cdp.assert_any_of_elements_visible(*args, **kwargs) sb.cdp.assert_any_of_elements_present(*args, **kwargs) sb.cdp.assert_element(selector, timeout=None) sb.cdp.assert_element_visible(selector, timeout=None) sb.cdp.assert_element_present(selector, timeout=None) sb.cdp.assert_element_absent(selector, timeout=None) sb.cdp.assert_element_not_visible(selector, timeout=None) sb.cdp.assert_element_attribute(selector, attribute, value=None) sb.cdp.assert_title(title) sb.cdp.assert_title_contains(substring) sb.cdp.assert_url(url) sb.cdp.assert_url_contains(substring) sb.cdp.assert_text(text, selector="html", timeout=None) sb.cdp.assert_exact_text(text, selector="html", timeout=None) sb.cdp.assert_text_not_visible(text, selector="body", timeout=None) sb.cdp.assert_true() sb.cdp.assert_false() sb.cdp.assert_equal(first, second) sb.cdp.assert_not_equal(first, second) sb.cdp.assert_in(first, second) sb.cdp.assert_not_in(first, second) sb.cdp.scroll_into_view(selector) sb.cdp.scroll_to_y(y) sb.cdp.scroll_by_y(y) sb.cdp.scroll_to_top() sb.cdp.scroll_to_bottom() sb.cdp.scroll_up(amount=25) sb.cdp.scroll_down(amount=25) sb.cdp.save_page_source(name, folder=None) sb.cdp.save_as_html(name, folder=None) sb.cdp.save_screenshot(name, folder=None, selector=None) sb.cdp.print_to_pdf(name, folder=None) sb.cdp.save_as_pdf(name, folder=None) ``` ℹ️ When available, calling `sb.METHOD()` redirects to `sb.cdp.METHOD()` when CDP Mode is active. From Pure CDP Mode, always call these methods with `sb.METHOD()` instead of `sb.cdp.METHOD()`. -------- ### 🐙 Pure CDP Mode (sb_cdp) Pure CDP Mode doesn't use WebDriver for anything. The browser is launched using CDP, and all browser actions are performed using CDP (or PyAutoGUI). Initialization: ```python from seleniumbase import sb_cdp sb = sb_cdp.Chrome(url=None, **kwargs) ``` Pure CDP Mode includes all methods from regular CDP Mode, except that they're called directly from sb instead of sb.cdp. Eg: sb.gui_click_captcha(). To quit a CDP-launched browser, use `sb.driver.stop()`. Basic example from [SeleniumBase/examples/cdp_mode/raw_cdp_turnstile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_turnstile.py): ```python from seleniumbase import sb_cdp url = "https://seleniumbase.io/apps/turnstile" sb = sb_cdp.Chrome(url) sb.solve_captcha() sb.assert_element("img#captcha-success") sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) sb.driver.stop() ``` Another example: ([SeleniumBase/examples/cdp_mode/raw_cdp_methods.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_cdp_methods.py)) ```python from seleniumbase import sb_cdp url = "https://seleniumbase.io/demo_page" sb = sb_cdp.Chrome(url) sb.press_keys("input", "Text") sb.highlight("button") sb.type("textarea", "Here are some words") sb.click("button") sb.set_value("input#mySlider", "100") sb.click_visible_elements("input.checkBoxClassB") sb.select_option_by_text("#mySelect", "Set to 75%") sb.gui_hover_and_click("#myDropdown", "#dropOption2") sb.gui_click_element("#checkBox1") sb.gui_drag_and_drop("img#logo", "div#drop2") sb.nested_click("iframe#myFrame3", ".fBox") sb.sleep(2) sb.driver.stop() ``` ℹ️ Even if you don't call `sb.driver.stop()`, the browser still quits after the script goes out-of-scope. -------- ### 🐙 CDP Mode Async API / Methods Initialization: ```python from seleniumbase import cdp_driver driver = await cdp_driver.start_async() tab = await driver.get(url, **kwargs) ``` Methods: (Sometimes `tab` is named `page` in examples) ```python await tab.get(url="about:blank") await tab.open(url="about:blank") await tab.find(text, best_match=False, timeout=10) # text can be selector await tab.find_all(text, timeout=10) # text can be selector await tab.select(selector, timeout=10) await tab.select_all(selector, timeout=10, include_frames=False) await tab.query_selector(selector) await tab.query_selector_all(selector) await tab.find_element_by_text(text, best_match=False) await tab.find_elements_by_text(text) await tab.reload(ignore_cache=True, script_to_evaluate_on_load=None) await tab.evaluate(expression) await tab.js_dumps(obj_name) await tab.back() await tab.forward() await tab.get_window() await tab.get_content() await tab.maximize() await tab.minimize() await tab.fullscreen() await tab.medimize() await tab.set_window_size(left=0, top=0, width=1280, height=1024) await tab.set_window_rect(left=0, top=0, width=1280, height=1024) await tab.activate() await tab.bring_to_front() await tab.set_window_state( left=0, top=0, width=1280, height=720, state="normal") await tab.get_navigation_history() await tab.get_user_agent() await tab.get_cookie_string() await tab.get_locale_code() await tab.is_online() await tab.open_external_inspector() # Open separate browser for debugging await tab.close() await tab.scroll_down(amount=25) await tab.scroll_up(amount=25) await tab.wait_for(selector="", text="", timeout=10) await tab.set_attributes(selector, attribute, value) await tab.internalize_links() await tab.download_file(url, filename=None) await tab.save_screenshot( filename="auto", format="png", full_page=False) await tab.print_to_pdf(filename="auto") await tab.set_download_path(path) await tab.get_all_linked_sources() await tab.get_all_urls(absolute=True) await tab.get_html() await tab.get_page_source() await tab.is_element_present(selector) await tab.is_element_visible(selector) await tab.get_element_rect(selector, timeout=5) # (window-based) await tab.get_window_rect() await tab.get_gui_element_rect(selector, timeout=5) # (screen-based) await tab.get_title() await tab.get_current_url() await tab.get_origin() await tab.send_keys(selector, text, timeout=5) await tab.type(selector, text, timeout=5) await tab.click(selector, timeout=5) await tab.click_if_visible(selector, timeout=0) await tab.click_with_offset(selector, x, y, center=False, timeout=5) await tab.solve_captcha() await tab.click_captcha() # Same as solve_captcha() await tab.get_document() await tab.get_flattened_document() await tab.get_local_storage() await tab.set_local_storage(items) ``` -------- ### 🐙 CDP Mode WebElement API / Methods After finding an element in CDP Mode, you can access `WebElement` methods: (Eg. After `element = sb.find_element(selector)`) ```python element.clear_input() element.click() element.click_with_offset(x, y, center=False) element.flash(duration=0.5, color="EE4488") element.focus() element.gui_click(timeframe=0.25) element.highlight_overlay() element.mouse_click() element.mouse_drag(destination) element.mouse_move() element.press_keys(text) element.query_selector(selector) element.querySelector(selector) element.query_selector_all(selector) element.querySelectorAll(selector) element.remove_from_dom() element.save_screenshot(*args, **kwargs) element.save_to_dom() element.scroll_into_view() element.select_option() element.send_file(*file_paths) element.send_keys(text) element.set_text(value) element.type(text) element.get_position() element.get_html() element.get_js_attributes() element.get_attribute(attribute) element.get_parent() ``` -------- SeleniumBase
      SeleniumBase
      ================================================ FILE: help_docs/commander.md ================================================

      SeleniumBase Commander 🎖️

      🎖️ SeleniumBase Commander lets you run pytest scripts from a Desktop GUI.
      🎖️ To launch it, call ``sbase commander`` or ``sbase gui``: ```zsh sbase gui * Starting the SeleniumBase Commander Desktop App... ``` 🎖️ SeleniumBase Commander loads the same tests that are found by: ```zsh pytest --co -q ``` 🎖️ You can customize which tests are loaded by passing additional args: ```zsh sbase commander [OPTIONAL PATH or TEST FILE] sbase gui [OPTIONAL PATH or TEST FILE] ``` 🎖️ Here are examples of customizing test collection: ```zsh sbase gui sbase gui -k agent sbase gui -m marker2 sbase gui test_suite.py sbase gui offline_examples/ ``` 🎖️ Once launched, you can further customize which tests to run and what settings to use. There are various controls for changing settings, modes, and other pytest command line options that are specific to SeleniumBase. You can also set additional options that don't have a visible toggle. When you're ready to run the selected tests with the specified options, click on the Run Selected Tests button. --------

      Other SeleniumBase Commanders:

      * 🐝🎖️ [SeleniumBase Behave GUI / Commander](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/behave_gui.md) --------
      To learn more about SeleniumBase, check out the Docs Site:
      SeleniumBase.io Docs
      All the code is on GitHub:
      SeleniumBase on GitHub ================================================ FILE: help_docs/customizing_test_runs.md ================================================

      Options / Customization

      🎛️ SeleniumBase has different ways of setting options, depending on which format you're using. Options can be set via the command-line or method call.

      --------

      01. pytest command-line options

      🎛️ SeleniumBase's [pytest plugin](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/pytest_plugin.py) lets you customize test runs from the CLI (Command-Line Interface), which adds options for setting/enabling the browser type, Dashboard Mode, Demo Mode, Headless Mode, Mobile Mode, Multi-threading Mode, Recorder Mode, UC Mode (stealth), reuse-session mode, Proxy Mode, and more. 🎛️ Here are some examples of configuring tests, which can be run from the [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder: ```zsh # Run a test in Chrome (default browser) pytest my_first_test.py # Run a test in Edge pytest test_swag_labs.py --edge # Run a test in Demo Mode (highlight assertions) pytest test_demo_site.py --demo # Run a test in Headless Mode (invisible browser) pytest test_demo_site.py --headless # Run tests multi-threaded using [n] threads pytest test_suite.py -n4 # Reuse the browser session for all tests ("--reuse-session") pytest test_suite.py --rs # Reuse the browser session, but erase cookies between tests pytest test_suite.py --rs --crumbs # Create a real-time dashboard for test results pytest test_suite.py --dashboard # Create a pytest-html report after tests are done pytest test_suite.py --html=report.html # Rerun failing tests more times pytest test_suite.py --reruns=1 # Activate Debug Mode at the start ("n": next. "c": continue) pytest test_null.py --trace -s # Activate Debug Mode on failures ("n": next. "c": continue) pytest test_fail.py --pdb -s # Activate Debug Mode at the end ("n": next. "c": continue) pytest test_fail.py --ftrace -s # Activate Recorder/Debug Mode as the test begins ("c" to continue) pytest test_null.py --recorder --trace -s # Pass extra data into tests (retrieve by calling self.data) pytest my_first_test.py --data="ABC" # Run tests on a local Selenium Grid pytest test_suite.py --server="127.0.0.1" # Run tests on a remote Selenium Grid pytest test_suite.py --server=IP_ADDRESS --port=4444 # Run tests on a remote Selenium Grid with authentication pytest test_suite.py --server=USERNAME:KEY@IP_ADDRESS --port=80 # Run tests through a proxy server pytest proxy_test.py --proxy=IP_ADDRESS:PORT # Run tests through a proxy server with authentication pytest proxy_test.py --proxy=USERNAME:PASSWORD@IP_ADDRESS:PORT # Run tests while setting the web browser's User Agent pytest user_agent_test.py --agent="USER-AGENT-STRING" # Run tests using Chrome's mobile device emulator (default settings) pytest test_swag_labs.py --mobile # Run mobile tests specifying CSS Width, CSS Height, and Pixel-Ratio pytest test_swag_labs.py --mobile --metrics="360,640,2" # Run tests using UC Mode to evade bot-detection services pytest verify_undetected.py --uc # Run tests while changing SeleniumBase default settings pytest my_first_test.py --settings-file=custom_settings.py ``` 🎛️ You can interchange `pytest` with `nosetests` for most tests, but using `pytest` is recommended. (`chrome` is the default browser if not specified.) 🎛️ If you're using `pytest` for running tests outside of the SeleniumBase repo, you'll want a copy of [pytest.ini](https://github.com/seleniumbase/SeleniumBase/blob/master/pytest.ini) at the base of the new folder structure. If using `nosetests`, the same applies for [setup.cfg](https://github.com/seleniumbase/SeleniumBase/blob/master/setup.cfg). 🎛️ Here are some useful command-line options that come with `pytest`: ```zsh -v # Verbose mode. Prints the full name of each test and shows more details. -q # Quiet mode. Print fewer details in the console output when running tests. -x # Stop running the tests after the first failure is reached. --html=report.html # Creates a detailed pytest-html report after tests finish. --co | --collect-only # Show what tests would get run. (Without running them) --co -q # (Both options together!) - Do a dry run with full test names shown. -n=NUM # Multithread the tests using that many threads. (Speed up test runs!) -s # See print statements. (Should be on by default with pytest.ini present.) --junit-xml=report.xml # Creates a junit-xml report after tests finish. --pdb # If a test fails, enter Post Mortem Debug Mode. (Don't use with CI!) --trace # Enter Debug Mode at the beginning of each test. (Don't use with CI!) -m=MARKER # Run tests with the specified pytest marker. ``` 🎛️ SeleniumBase provides additional `pytest` command-line options for tests: ```zsh --browser=BROWSER # (The web browser to use. Default: "chrome".) --chrome # (Shortcut for "--browser=chrome". On by default.) --edge # (Shortcut for "--browser=edge".) --firefox # (Shortcut for "--browser=firefox".) --safari # (Shortcut for "--browser=safari".) --settings-file=FILE # (Override default SeleniumBase settings.) --env=ENV # (Set the test env. Access with "self.env" in tests.) --account=STR # (Set account. Access with "self.account" in tests.) --data=STRING # (Extra test data. Access with "self.data" in tests.) --var1=STRING # (Extra test data. Access with "self.var1" in tests.) --var2=STRING # (Extra test data. Access with "self.var2" in tests.) --var3=STRING # (Extra test data. Access with "self.var3" in tests.) --variables=DICT # (Extra test data. Access with "self.variables".) --user-data-dir=DIR # (Set the Chrome user data directory to use.) --protocol=PROTOCOL # (The Selenium Grid protocol: http|https.) --server=SERVER # (The Selenium Grid server/IP used for tests.) --port=PORT # (The Selenium Grid port used by the test server.) --cap-file=FILE # (The web browser's desired capabilities to use.) --cap-string=STRING # (The web browser's desired capabilities to use.) --proxy=SERVER:PORT # (Connect to a proxy server:port as tests are running) --proxy=USERNAME:PASSWORD@SERVER:PORT # (Use an authenticated proxy server) --proxy-bypass-list=STRING # (";"-separated hosts to bypass, Eg "*.foo.com") --proxy-pac-url=URL # (Connect to a proxy server using a PAC_URL.pac file.) --proxy-pac-url=USERNAME:PASSWORD@URL # (Authenticated proxy with PAC URL.) --proxy-driver # (If a driver download is needed, will use: --proxy=PROXY.) --multi-proxy # (Allow multiple authenticated proxies when multi-threaded.) --agent=STRING # (Modify the web browser's User-Agent string.) --mobile # (Use the mobile device emulator while running tests.) --metrics=STRING # (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".) --chromium-arg="ARG=N,ARG2" # (Set Chromium args, ","-separated, no spaces.) --firefox-arg="ARG=N,ARG2" # (Set Firefox args, comma-separated, no spaces.) --firefox-pref=SET # (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP # (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR # (Load a Chrome Extension directory, comma-separated.) --disable-features="F1,F2" # (Disable features, comma-separated, no spaces.) --binary-location=PATH # (Set path of the Chromium browser binary to use.) --driver-version=VER # (Set the chromedriver or uc_driver version to use.) --sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.) --wfa # (Wait for AngularJS to be done loading after specific web actions.) --pls=PLS # (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) --headless # (The default headless mode. Linux uses this mode by default.) --headless1 # (Use Chrome's old headless mode. Fast, but has limitations.) --headless2 # (Use Chrome's new headless mode, which supports extensions.) --headed # (Run tests in headed/GUI mode on Linux OS, where not default.) --xvfb # (Run tests using the Xvfb virtual display server on Linux OS.) --xvfb-metrics=STRING # (Set Xvfb display size on Linux: "Width,Height".) --locale=LOCALE_CODE # (Set the Language Locale Code for the web browser.) --interval=SECONDS # (The autoplay interval for presentations & tour steps) --start-page=URL # (The starting URL for the web browser when tests begin.) --archive-logs # (Archive existing log files instead of deleting them.) --archive-downloads # (Archive old downloads instead of deleting them.) --time-limit=SECONDS # (Safely fail any test that exceeds the time limit.) --slow # (Slow down the automation. Faster than using Demo Mode.) --demo # (Slow down and visually see test actions as they occur.) --demo-sleep=SECONDS # (Set the wait time after Slow & Demo Mode actions.) --highlights=NUM # (Number of highlight animations for Demo Mode actions.) --message-duration=SECONDS # (The time length for Messenger alerts.) --check-js # (Check for JavaScript errors after page loads.) --ad-block # (Block some types of display ads from loading.) --host-resolver-rules=RULES # (Set host-resolver-rules, comma-separated.) --block-images # (Block images from loading during tests.) --do-not-track # (Indicate to websites that you don't want to be tracked.) --verify-delay=SECONDS # (The delay before MasterQA verification checks.) --ee | --esc-end # (Lets the user end the current test via the ESC key.) --recorder # (Enables the Recorder for turning browser actions into code.) --rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.) --rec-sleep # (If the Recorder is enabled, also records self.sleep calls.) --rec-print # (If the Recorder is enabled, prints output after tests end.) --disable-cookies # (Disable Cookies on websites. Pages might break!) --disable-js # (Disable JavaScript on websites. Pages might break!) --disable-csp # (Disable the Content Security Policy of websites.) --disable-ws # (Disable Web Security on Chromium-based browsers.) --enable-ws # (Enable Web Security on Chromium-based browsers.) --enable-sync # (Enable "Chrome Sync" on websites.) --uc | --undetected # (Use undetected-chromedriver to evade bot-detection.) --uc-cdp-events # (Capture CDP events when running in "--undetected" mode.) --log-cdp # ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) --remote-debug # (Sync to Chrome Remote Debugger chrome://inspect/#devices) --ftrace | --final-trace # (Debug Mode after each test. Don't use with CI!) --dashboard # (Enable the SeleniumBase Dashboard. Saved at: dashboard.html) --dash-title=STRING # (Set the title shown for the generated dashboard.) --enable-3d-apis # (Enables WebGL and 3D APIs.) --swiftshader # (Chrome "--use-gl=angle" / "--use-angle=swiftshader-webgl") --incognito # (Enable Chrome's Incognito mode.) --guest # (Enable Chrome's Guest mode.) --dark # (Enable Chrome's Dark mode.) --devtools # (Open Chrome's DevTools when the browser opens.) --rs | --reuse-session # (Reuse browser session for all tests.) --rcs | --reuse-class-session # (Reuse session for tests in class.) --crumbs # (Delete all cookies between tests reusing a session.) --disable-beforeunload # (Disable the "beforeunload" event on Chrome.) --window-position=X,Y # (Set the browser's starting window position.) --window-size=WIDTH,HEIGHT # (Set the browser's starting window size.) --maximize # (Start tests with the browser window maximized.) --screenshot # (Save a screenshot at the end of each test.) --no-screenshot # (No screenshots saved unless tests directly ask it.) --visual-baseline # (Set the visual baseline for Visual/Layout tests.) --wire # (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf # (Set Chromium "plugins.always_open_pdf_externally":True.) --timeout-multiplier=MULTIPLIER # (Multiplies the default timeout values.) --list-fail-page # (After each failing test, list the URL of the failure.) ``` (For more details, see the full list of command-line options **[here](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/pytest_plugin.py)**.) 🎛️ You can also view a list of popular `pytest` options for SeleniumBase by typing: ```zsh seleniumbase options ``` Or the short form: ```zsh sbase options ``` --------

      Example tests using Logging:

      To see logging abilities, you can run a test suite that includes tests that fail on purpose: ```zsh pytest test_suite.py ``` 🔵 During test failures, logs and screenshots from the most recent test run will get saved to the `latest_logs/` folder. If `--archive-logs` is specified (or if ARCHIVE_EXISTING_LOGS is set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py)), test logs will also get archived to the `archived_logs/` folder. Otherwise, the log files will be cleaned out when the next test run begins (by default).

      Demo Mode:

      🔵 If any test is moving too fast for your eyes to see what's going on, you can run it in **Demo Mode** by adding `--demo` on the command line, which pauses the browser briefly between actions, highlights page elements being acted on, and lets you know what test assertions are happening in real-time: ```zsh pytest my_first_test.py --demo ``` 🔵 You can override the default wait time by either updating [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) or by using `--demo-sleep=NUM` when using Demo Mode. (NOTE: If you use `--demo-sleep=NUM` without using `--demo`, nothing will happen.) ```zsh pytest my_first_test.py --demo --demo-sleep=1.2 ```

      Passing additional data to tests:

      If you want to pass additional data from the command line to your tests, you can use `--data=STRING`. Now inside your tests, you can use `self.data` to access that.

      Running tests multithreaded:

      To run `pytest` with multiple processes, add `-n=NUM`, `-n NUM`, or `-nNUM` on the command line, where `NUM` is the number of CPUs you want to use. ```zsh pytest -n=8 pytest -n 8 pytest -n8 ```

      How to retry failing tests automatically:

      You can use pytest --reruns=NUM to retry failing tests that many times. Add --reruns-delay=SECONDS to wait that many seconds between retries. Example:

      ```zsh pytest --reruns=1 --reruns-delay=1 ```

      Debugging tests:

      🔵 You can use the following calls in your scripts to help you debug issues: ```python import time; time.sleep(5) # Makes the test wait and do nothing for 5 seconds. import pdb; pdb.set_trace() # Debug Mode. n: next, c: continue, s: step, u: up, d: down. import pytest; pytest.set_trace() # Debug Mode. n: next, c: continue, s: step, u: up, d: down. ``` 🔵 To pause an active test that throws an exception or error, (*and keep the browser window open while **Debug Mode** begins in the console*), add **`--pdb`** as a `pytest` option: ```zsh pytest test_fail.py --pdb ``` 🔵 To start tests in Debug Mode, add **`--trace`** as a `pytest` option: ```zsh pytest test_coffee_cart.py --trace ``` (**`pdb`** commands: `n`, `c`, `s`, `u`, `d` => `next`, `continue`, `step`, `up`, `down`).

      Combinations of options:

      🎛️ There are times when you'll want to combine various command-line options for added effect. For instance, the multi-process option, `-n8`, can be customized by adding: `--dist=loadscope` or `--dist=loadfile` to it. There's more info on that here: [pytest-xdist](https://pypi.org/project/pytest-xdist/2.5.0/): * `-n8 --dist=loadscope`: Tests are grouped by module for test functions and by class for test methods. Groups are distributed to available workers as whole units. This guarantees that all tests in a group run in the same process. This can be useful if you have expensive module-level or class-level fixtures. Grouping by class takes priority over grouping by module. * `-n8 --dist=loadfile`: Tests are grouped by their containing file. Groups are distributed to available workers as whole units. This guarantees that all tests in a file run in the same worker.
      ▶️ -n8 --dist=loadgroup (click to expand)
      • Tests are grouped by the xdist_group mark. Groups are distributed to available workers as whole units. This guarantees that all tests with the same xdist_group name run in the same worker.
      ```python @pytest.mark.xdist_group(name="group1") def test_1(): pass class Test: @pytest.mark.xdist_group("group1") def test_2(): pass ```

      This makes test_1 and Test::test_2 run in the same worker. Tests without the xdist_group mark are distributed normally.

      🎛️ You might also want to combine multiple options at once. For example: ```zsh pytest --headless -n8 --dashboard --html=report.html -v --rs --crumbs ``` The above not only runs tests in parallel processes, but it also tells tests in the same process to share the same browser session, runs the tests in headless mode, displays the full name of each test on a separate line, creates a real-time dashboard of the test results, and creates a full report after all tests complete. -------- 🎛️ For extra speed, run your tests using `chrome-headless-shell`: First, get `chrome-headless-shell` if you don't already have it: ```zsh sbase get chs ``` Then, run scripts with `--chs` / `chs=True`: ```zsh pytest --chs -n8 --dashboard --html=report.html -v --rs ``` That makes your tests run very quickly in headless mode. --------

      The SeleniumBase Dashboard:

      🔵 The `--dashboard` option for pytest generates a SeleniumBase Dashboard located at `dashboard.html`, which updates automatically as tests run and produce results. Example: ```zsh pytest --dashboard --rs --headless ``` The SeleniumBase Dashboard 🔵 Additionally, you can host your own SeleniumBase Dashboard Server on a port of your choice. Here's an example of that using Python 3's `http.server`: ```zsh python -m http.server 1948 ``` 🔵 Now you can navigate to `http://localhost:1948/dashboard.html` in order to view the dashboard as a web app. This requires two different terminal windows: one for running the server, and another for running the tests, which should be run from the same directory. (Use Ctrl+C to stop the http server.) 🔵 Here's a full example of what the SeleniumBase Dashboard may look like: ```zsh pytest test_suite.py --dashboard --rs --headless ``` The SeleniumBase Dashboard --------

      Pytest Reports:

      🔵 Using `--html=report.html` gives you a fancy report of the name specified after your test suite completes. ```zsh pytest test_suite.py --html=report.html ``` Example Pytest Report 🔵 When combining pytest html reports with SeleniumBase Dashboard usage, the pie chart from the Dashboard will get added to the html report. Additionally, if you set the html report URL to be the same as the Dashboard URL when also using the dashboard, (example: `--dashboard --html=dashboard.html`), then the Dashboard will become an advanced html report when all the tests complete. 🔵 Here's an example of an upgraded html report: ```zsh pytest test_suite.py --dashboard --html=report.html ``` Dashboard Pytest HTML Report If viewing pytest html reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356/7058266) for the html to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/system-administration/security/configuring-content-security-policy/). You can also use `--junit-xml=report.xml` to get an xml report instead. Jenkins can use this file to display better reporting for your tests. ```zsh pytest test_suite.py --junit-xml=report.xml ``` --------

      Nosetest Reports:

      The `--report` option gives you a fancy report after your test suite completes. ```zsh nosetests test_suite.py --report ``` Example Nosetest Report (NOTE: You can add `--show_report` to immediately display Nosetest reports after the test suite completes. Only use `--show_report` when running tests locally because it pauses the test run.) --------

      Language Locale Codes

      You can specify a Language Locale Code to customize web pages on supported websites. With SeleniumBase, you can change the web browser's Locale on the command line by doing this: ```zsh pytest --locale=CODE # Example: --locale=ru ``` Visit 🗾 Locales for a full list of codes. --------

      Changing the default driver version:

      🔵 By default, SeleniumBase will make sure that the major driver version matches the major browser version for Chromium tests. (Eg. If Chrome `117.X` is installed and you have chromedriver `117.X`, then nothing happens, but if you had chromedriver `116.X` instead, then SeleniumBase would download chromedriver `117.X` to match the browser version.) 🎛️ To change this default behavior, you can use: ```zsh pytest --driver-version=VER ``` The `VER` in `--driver-version=VER` can be: * A major driver version. Eg. `117`. (milestone) * An exact driver version. Eg. `117.0.5938.92`. * `"browser"` (exact match on browser version) * `"keep"` (keep using the driver you already have) * `"latest"` / `"stable"` (latest stable version) * `"previous"` / `"latest-1"` (latest minus one) * `"beta"` (latest beta version) * `"dev"` (latest dev version) * `"canary"` (latest canary version) * `"mlatest"` (latest version for the milestone) Note that different options could lead to the same result. (Eg. If you have the latest version of a browser for a milestone, then `"browser"` and `"mlatest"` should give you the same driver if the latest driver version for that milestone matches the browser version.) --------

      Setting the binary location:

      🔵 By default, SeleniumBase uses the browser binary detected on the System PATH. 🎛️ To change this default behavior, you can use: ```zsh pytest --binary-location=PATH ``` The `PATH` in `--binary-location=PATH` / `--bl=PATH` can be: * A relative or exact path to the browser binary. * `"cft"` as a special option for `Chrome for Testing`. * `"chs"` as a special option for `Chrome-Headless-Shell`. Before using the `"cft"` / `"chs"` options, call `sbase get cft` / `sbase get chs` in order to download the specified binaries into the `seleniumbase/drivers` folder. The default version is the latest stable version on https://googlechromelabs.github.io/chrome-for-testing/. You can change that by specifying the arg as a parameter. (Eg. `sbase get cft 131`, `sbase get chs 132`, etc.) With the `SB()` and `Driver()` formats, the binary location is set via the `binary_location` parameter. -------- 🎛️ To use the special `Chromium` binary: ```zsh sbase get chromium ``` Then, run scripts with `--use-chromium` / `use_chromium=True`: ```zsh pytest --use-chromium -n8 --dashboard --html=report.html -v --rs --headless ``` -------- 🎛️ To use the special `Chrome for Testing` binary: ```zsh sbase get cft ``` Then, run scripts with `--cft` / `cft=True`: ```zsh pytest --cft -n8 --dashboard --html=report.html -v --rs --headless ``` -------- (Note that `--chs` / `chs=True` activates `Chrome-Headless-Shell`) `Chrome-Headless-Shell` is the fastest version of Chrome, designed specifically for headless automation. (This mode is NOT compatible with UC Mode!) --------

      Customizing default settings:

      🎛️ An easy way to override [seleniumbase/config/settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) is by using a custom settings file. Here's the command-line option to add to tests: (See [examples/custom_settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/custom_settings.py)) ```zsh pytest --settings-file=custom_settings.py ``` (Settings include default timeout values, a two-factor auth key, DB credentials, S3 credentials, and other important settings used by tests.) --------

      Running tests on a remote Selenium Grid:

      🌐 SeleniumBase lets you run tests on remote Selenium Grids such as [BrowserStack](https://www.browserstack.com/automate#)'s Selenium Grid, [Sauce Labs](https://saucelabs.com/products/platform-configurator)'s Selenium Grid, other Grids, and even your own Grid: 🌐 For setting browser desired capabilities while running Selenium remotely, see the ReadMe located here: https://github.com/seleniumbase/SeleniumBase/tree/master/examples/capabilities Here's how to connect to a BrowserStack Selenium Grid server for running tests: ```zsh pytest test_demo_site.py --server=USERNAME:KEY@hub.browserstack.com --port=80 ``` Here's how to connect to a Sauce Labs Selenium Grid server for running tests: ```zsh pytest test_demo_site.py --server=USERNAME:KEY@ondemand.us-east-1.saucelabs.com --port=443 --protocol=https ``` 🌐 Or you can create your own Selenium Grid for test distribution. ([See this ReadMe for details](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid/ReadMe.md)) 🌐 To use a server on the `https` protocol, add `--protocol=https`: (*Now automatic if the port is 443.*) ```zsh pytest test_demo_site.py --protocol=https --server=IP_ADDRESS --port=PORT ``` --------

      Using a Proxy Server:

      🌐 If you wish to use a proxy server for your browser tests (Chromium or Firefox), you can add `--proxy=IP_ADDRESS:PORT` as an argument on the command line. ```zsh pytest proxy_test.py --proxy=IP_ADDRESS:PORT ``` 🌐 If the proxy server that you wish to use requires authentication, you can do the following (Chromium only): ```zsh pytest proxy_test.py --proxy=USERNAME:PASSWORD@IP_ADDRESS:PORT ``` 🌐 SeleniumBase also supports SOCKS4 and SOCKS5 proxies: ```zsh pytest proxy_test.py --proxy="socks4://IP_ADDRESS:PORT" pytest proxy_test.py --proxy="socks5://IP_ADDRESS:PORT" ``` To make things easier, you can add your frequently-used proxies to PROXY_LIST in [proxy_list.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/proxy_list.py), and then use `--proxy=KEY_FROM_PROXY_LIST` to use the IP_ADDRESS:PORT of that key. ```zsh pytest proxy_test.py --proxy=proxy1 ``` --------

      Changing the User-Agent:

      🔤 If you wish to change the User-Agent for your browser tests (Chrome and Firefox only), you can add `--agent="USER-AGENT-STRING"` as an argument on the command line. ```zsh pytest user_agent_test.py --agent="Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7412.EU" ```

      Mobile Device Testing:

      📱 Use `--mobile` to quickly run your tests using Chrome's mobile device emulator with default values for device metrics (CSS Width, CSS Height, Pixel-Ratio) and a default value set for the user agent. To configure the mobile device metrics, use `--metrics="CSS_Width,CSS_Height,Pixel_Ratio"` to set those values. You'll also be able to set the user agent with `--agent="USER-AGENT-STRING"` (a default user agent will be used if not specified). To find real values for device metrics, [see this GitHub Gist](https://gist.github.com/sidferreira/3f5fad525e99b395d8bd882ee0fd9d00). For a list of available user agent strings, [check out this page](https://developers.whatismybrowser.com/useragents/explore/). ```zsh # Run tests using Chrome's mobile device emulator (default settings) pytest test_swag_labs.py --mobile # Run mobile tests specifying CSS Width, CSS Height, and Pixel-Ratio pytest test_swag_labs.py --mobile --metrics="412,732,3" # Run mobile tests specifying the user agent pytest test_swag_labs.py --mobile --agent="Mozilla/5.0 (Linux; Android 9; Pixel 3 XL)" ``` --------

      02. SB() method options

      ```python test=None # Test Mode: Output, Logging, Continue on failure unless "rtf". rtf=None # Shortcut / Duplicate of "raise_test_failure". raise_test_failure=None # If "test" mode, raise Exception on 1st failure. browser=None # Choose from "chrome", "edge", "firefox", or "safari". headless=None # Use the default headless mode for Chromium and Firefox. headless1=None # Use Chromium's old headless mode. (Fast, but limited) headless2=None # Use Chromium's new headless mode. (Has more features) locale_code=None # Set the Language Locale Code for the web browser. protocol=None # The Selenium Grid protocol: "http" or "https". servername=None # The Selenium Grid server/IP used for tests. port=None # The Selenium Grid port used by the test server. proxy=None # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT". proxy_bypass_list=None # Skip proxy when using the listed domains. proxy_pac_url=None # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL) multi_proxy=None # Allow multiple proxies with auth when multi-threaded. agent=None # Modify the web browser's User-Agent string. cap_file=None # The desired capabilities to use with a Selenium Grid. cap_string=None # The desired capabilities to use with a Selenium Grid. recorder_ext=None # Enables the SeleniumBase Recorder Chromium extension. disable_cookies=None # Disable Cookies on websites. (Pages might break!) disable_js=None # Disable JavaScript on websites. (Pages might break!) disable_csp=None # Disable the Content Security Policy of websites. enable_ws=None # Enable Web Security on Chromium-based browsers. enable_sync=None # Enable "Chrome Sync" on websites. use_auto_ext=None # Use Chrome's automation extension. undetectable=None # Use undetected-chromedriver to evade bot-detection. uc_cdp_events=None # Capture CDP events in undetected-chromedriver mode. uc_subprocess=None # Use undetected-chromedriver as a subprocess. log_cdp_events=None # Capture {"performance": "ALL", "browser": "ALL"} incognito=None # Enable Chromium's Incognito mode. guest_mode=None # Enable Chromium's Guest mode. dark_mode=None # Enable Chromium's Dark mode. devtools=None # Open Chromium's DevTools when the browser opens. remote_debug=None # Enable Chrome's Debugger on "http://localhost:9222". enable_3d_apis=None # Enable WebGL and 3D APIs. swiftshader=None # Chrome: --use-gl=angle / --use-angle=swiftshader-webgl ad_block_on=None # Block some types of display ads from loading. host_resolver_rules=None # Set host-resolver-rules, comma-separated. block_images=None # Block images from loading during tests. do_not_track=None # Tell websites that you don't want to be tracked. chromium_arg=None # "ARG=N,ARG2" (Set Chromium args, ","-separated.) firefox_arg=None # "ARG=N,ARG2" (Set Firefox args, comma-separated.) firefox_pref=None # SET (Set Firefox PREFERENCE:VALUE set, ","-separated) user_data_dir=None # Set the Chrome user data directory to use. extension_zip=None # Load a Chrome Extension .zip|.crx, comma-separated. extension_dir=None # Load a Chrome Extension directory, comma-separated. disable_features=None # "F1,F2" (Disable Chrome features, ","-separated.) binary_location=None # Set path of the Chromium browser binary to use. driver_version=None # Set the chromedriver or uc_driver version to use. skip_js_waits=None # Skip JS Waits (readyState=="complete" and Angular). wait_for_angularjs=None # Wait for AngularJS to load after some actions. use_wire=None # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None # Set the browser's starting window position: "X,Y" window_size=None # Set the browser's starting window size: "Width,Height" is_mobile=None # Use the mobile device emulator while running tests. mobile=None # Shortcut / Duplicate of "is_mobile". device_metrics=None # Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio" xvfb=None # Run tests using the Xvfb virtual display server on Linux OS. xvfb_metrics=None # Set Xvfb display size on Linux: "Width,Height". start_page=None # The starting URL for the web browser when tests begin. rec_print=None # If Recorder is enabled, prints output after tests end. rec_behave=None # Like Recorder Mode, but also generates behave-gherkin. record_sleep=None # If Recorder enabled, also records self.sleep calls. data=None # Extra test data. Access with "self.data" in tests. var1=None # Extra test data. Access with "self.var1" in tests. var2=None # Extra test data. Access with "self.var2" in tests. var3=None # Extra test data. Access with "self.var3" in tests. variables=None # DICT (Extra test data. Access with "self.variables") account=None # Set account. Access with "self.account" in tests. environment=None # Set the test env. Access with "self.env" in tests. headed=None # Run tests in headed/GUI mode on Linux, where not default. maximize=None # Start tests with the browser window maximized. disable_ws=None # Reverse of "enable_ws". (None and False are different) disable_beforeunload=None # Disable the "beforeunload" event on Chromium. settings_file=None # A file for overriding default SeleniumBase settings. position=None # Shortcut / Duplicate of "window_position". size=None # Shortcut / Duplicate of "window_size". uc=None # Shortcut / Duplicate of "undetectable". undetected=None # Shortcut / Duplicate of "undetectable". uc_cdp=None # Shortcut / Duplicate of "uc_cdp_events". uc_sub=None # Shortcut / Duplicate of "uc_subprocess". locale=None # Shortcut / Duplicate of "locale_code". log_cdp=None # Shortcut / Duplicate of "log_cdp_events". ad_block=None # Shortcut / Duplicate of "ad_block_on". server=None # Shortcut / Duplicate of "servername". guest=None # Shortcut / Duplicate of "guest_mode". wire=None # Shortcut / Duplicate of "use_wire". pls=None # Shortcut / Duplicate of "page_load_strategy". sjw=None # Shortcut / Duplicate of "skip_js_waits". wfa=None # Shortcut / Duplicate of "wait_for_angularjs". cft=None # Use "Chrome for Testing" chs=None # Use "Chrome-Headless-Shell" use_chromium=None # Use base "Chromium" save_screenshot=None # Save a screenshot at the end of each test. no_screenshot=None # No screenshots saved unless tests directly ask it. page_load_strategy=None # Set Chrome PLS to "normal", "eager", or "none". timeout_multiplier=None # Multiplies the default timeout values. js_checking_on=None # Check for JavaScript errors after page loads. slow=None # Slow down the automation. Faster than using Demo Mode. demo=None # Slow down and visually see test actions as they occur. demo_sleep=None # SECONDS (Set wait time after Slow & Demo Mode actions.) message_duration=None # SECONDS (The time length for Messenger alerts.) highlights=None # Number of highlight animations for Demo Mode actions. interval=None # SECONDS (Autoplay interval for SB Slides & Tour steps.) time_limit=None # SECONDS (Safely fail tests that exceed the time limit.) ``` Example: [SeleniumBase/examples/raw_robot.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_robot.py) --------

      03. Driver() method options

      ```python browser=None # Choose from "chrome", "edge", "firefox", or "safari". headless=None # Use the default headless mode for Chromium and Firefox. headless1=None # Use Chromium's old headless mode. (Fast, but limited) headless2=None # Use Chromium's new headless mode. (Has more features) headed=None # Run tests in headed/GUI mode on Linux, where not default. locale_code=None # Set the Language Locale Code for the web browser. protocol=None # The Selenium Grid protocol: "http" or "https". servername=None # The Selenium Grid server/IP used for tests. port=None # The Selenium Grid port used by the test server. proxy=None # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT". proxy_bypass_list=None # Skip proxy when using the listed domains. proxy_pac_url=None # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL) multi_proxy=None # Allow multiple proxies with auth when multi-threaded. agent=None # Modify the web browser's User-Agent string. cap_file=None # The desired capabilities to use with a Selenium Grid. cap_string=None # The desired capabilities to use with a Selenium Grid. recorder_ext=None # Enables the SeleniumBase Recorder Chromium extension. disable_cookies=None # Disable Cookies on websites. (Pages might break!) disable_js=None # Disable JavaScript on websites. (Pages might break!) disable_csp=None # Disable the Content Security Policy of websites. enable_ws=None # Enable Web Security on Chromium-based browsers. disable_ws=None # Reverse of "enable_ws". (None and False are different) enable_sync=None # Enable "Chrome Sync" on websites. use_auto_ext=None # Use Chrome's automation extension. undetectable=None # Use undetected-chromedriver to evade bot-detection. uc_cdp_events=None # Capture CDP events in undetected-chromedriver mode. uc_subprocess=None # Use undetected-chromedriver as a subprocess. log_cdp_events=None # Capture {"performance": "ALL", "browser": "ALL"} no_sandbox=None # (DEPRECATED) - "--no-sandbox" is always used now. disable_gpu=None # (DEPRECATED) - GPU is disabled if not "swiftshader". incognito=None # Enable Chromium's Incognito mode. guest_mode=None # Enable Chromium's Guest mode. dark_mode=None # Enable Chromium's Dark mode. devtools=None # Open Chromium's DevTools when the browser opens. remote_debug=None # Enable Chrome's Debugger on "http://localhost:9222". enable_3d_apis=None # Enable WebGL and 3D APIs. swiftshader=None # Chrome: --use-gl=angle / --use-angle=swiftshader-webgl ad_block_on=None # Block some types of display ads from loading. host_resolver_rules=None # Set host-resolver-rules, comma-separated. block_images=None # Block images from loading during tests. do_not_track=None # Tell websites that you don't want to be tracked. chromium_arg=None # "ARG=N,ARG2" (Set Chromium args, ","-separated.) firefox_arg=None # "ARG=N,ARG2" (Set Firefox args, comma-separated.) firefox_pref=None # SET (Set Firefox PREFERENCE:VALUE set, ","-separated) user_data_dir=None # Set the Chrome user data directory to use. extension_zip=None # Load a Chrome Extension .zip|.crx, comma-separated. extension_dir=None # Load a Chrome Extension directory, comma-separated. disable_features=None # "F1,F2" (Disable Chrome features, ","-separated.) binary_location=None # Set path of the Chromium browser binary to use. driver_version=None # Set the chromedriver or uc_driver version to use. page_load_strategy=None # Set Chrome PLS to "normal", "eager", or "none". use_wire=None # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None # Set the browser's starting window position: "X,Y" window_size=None # Set the browser's starting window size: "Width,Height" is_mobile=None # Use the mobile device emulator while running tests. mobile=None # Shortcut / Duplicate of "is_mobile". d_width=None # Set device width d_height=None # Set device height d_p_r=None # Set device pixel ratio position=None # Shortcut / Duplicate of "window_position". size=None # Shortcut / Duplicate of "window_size". uc=None # Shortcut / Duplicate of "undetectable". undetected=None # Shortcut / Duplicate of "undetectable". uc_cdp=None # Shortcut / Duplicate of "uc_cdp_events". uc_sub=None # Shortcut / Duplicate of "uc_subprocess". locale=None # Shortcut / Duplicate of "locale_code". log_cdp=None # Shortcut / Duplicate of "log_cdp_events". ad_block=None # Shortcut / Duplicate of "ad_block_on". server=None # Shortcut / Duplicate of "servername". guest=None # Shortcut / Duplicate of "guest_mode". wire=None # Shortcut / Duplicate of "use_wire". pls=None # Shortcut / Duplicate of "page_load_strategy". use_chromium=None # Use base "Chromium" cft=None # Use "Chrome for Testing" chs=None # Use "Chrome-Headless-Shell" ``` Example: [SeleniumBase/examples/raw_driver_manager.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_driver_manager.py) --------

      04. sb_cdp.Chrome() method options

      ```python url: Optional[str] = None user_data_dir: Optional[PathLike] = None headless: Optional[bool] = False incognito: Optional[bool] = False guest: Optional[bool] = False browser_executable_path: Optional[PathLike] = None browser_args: Optional[List[str]] = None xvfb_metrics: Optional[List[str]] = None # "Width,Height" for Linux ad_block: Optional[bool] = False sandbox: Optional[bool] = True lang: Optional[str] = None # Set the Language Locale Code host: Optional[str] = None # Chrome remote-debugging-host port: Optional[int] = None # Chrome remote-debugging-port xvfb: Optional[int] = None # Use a special virtual display on Linux headed: Optional[bool] = None # Override default Xvfb mode on Linux expert: Optional[bool] = None # Open up closed Shadow-root elements agent: Optional[str] = None # Set the user-agent string proxy: Optional[str] = None # "host:port" or "user:pass@host:port" tzone: Optional[str] = None # Eg "America/New_York", "Asia/Kolkata" geoloc: Optional[list | tuple] = None # Eg (48.87645, 2.26340) extension_dir: Optional[str] = None # Chrome extension directory platform: Optional[str] = None # Set the Platform. Eg: "MacIntel" ``` Example: [SeleniumBase/examples/cdp_mode/raw_geolocation.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_geolocation.py) --------

      05. activate_cdp_mode() method options

      ```python url: Optional[str] = None # The URL to navigate to lang: Optional[str] = None # Set the Language Locale Code agent: Optional[str] = None # Set the user-agent string tzone: Optional[str] = None # Eg "America/New_York", "Asia/Kolkata" geoloc: Optional[list | tuple] = None # Eg (48.87645, 2.26340) platform: Optional[str] = None # Set the Platform. Eg: "MacIntel" ``` Example: [SeleniumBase/examples/cdp_mode/raw_geolocation_sb.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_geolocation_sb.py) Note that if CDP Mode is already active, the options above can also be used when calling `sb.cdp.open()`. (The `url` arg is required in this case.) -------- [](https://github.com/seleniumbase/SeleniumBase) ================================================ FILE: help_docs/demo_mode.md ================================================

      Demo Mode 🎦

      SeleniumBase Example

      🔵 Demo Mode helps you see what a test is doing.

      🏇💨 👀 If a test runs too fast for your eyes, use Demo Mode to slow it down, highlight actions, and display assertions. Example usage:

      ```zsh cd examples/ pytest test_coffee_cart.py --demo ```

      SeleniumBase Coffee Cart Test

      >

      (--demo mode slows down tests and highlights actions)

      -------- Another example: ```zsh pytest my_first_test.py --demo ``` --------

      Here's how to run test_swag_labs.py from examples/ in Demo Mode:

      ```zsh pytest test_swag_labs.py --demo ```

      SeleniumBase Example

      --------

      Here's an example that only uses the highlight() method for highlighting browser actions:

      (test_error_page.py from examples/)

      ```zsh pytest test_error_page.py ```

      SeleniumBase Example

      -------- Here's an example of a mobile test in Demo Mode:

      SeleniumBase Example

      ================================================ FILE: help_docs/desired_capabilities.md ================================================

      Using Desired Capabilities

      You can specify browser capabilities when running SeleniumBase tests on a remote Selenium Grid server (such as BrowserStack or Sauce Labs). Sample run commands may look like this when run from the [SeleniumBase/examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder: (The browser is now specified in the capabilities file.) ```zsh pytest test_demo_site.py --browser=remote --server=USERNAME:KEY@hub.browserstack.com --port=80 --cap_file=capabilities/sample_cap_file_BS.py ``` ```zsh pytest test_demo_site.py --browser=remote --server=USERNAME:KEY@ondemand.us-east-1.saucelabs.com --port=443 --protocol=https --cap_file=capabilities/sample_cap_file_SL.py ``` (Parameters: ``--browser=remote``, ``--server=SERVER``, ``--port=PORT``, and ``--cap_file=CAP_FILE.py``) Here's an example desired capabilities file for BrowserStack using the newer SDK format in a `.yml` / `.yaml` file: ```yml platforms: - browserName: safari osVersion: 17 deviceName: iPhone 15 Pro Max buildIdentifier: ${BUILD_NUMBER} parallelsPerPlatform: 1 projectName: My Project browserstackLocal: true debug: true networkLogs: true ``` Here's an example desired capabilities file for BrowserStack using the legacy JSONWP format in a `.py` file: ```python desired_cap = { "browser": "Chrome", "os": "Windows", "os_version": "11", "browser_version": "latest", "browserstack.console": "info", "browserstack.debug": "true", "browserstack.networkLogs": "true", "browserstack.local": "true", } ``` Here's an example desired capabilities file for Sauce Labs: ```python capabilities = { "browserName": "chrome", "browserVersion": "latest", "platformName": "macOS 10.14", "sauce:options": {}, } ``` (Note that the browser is now being specified in the capabilities file, rather than with ``--BROWSER`` when using a **remote** Selenium Grid. If using a **local** Selenium Grid, specify the browser, eg: ``--firefox``.)
      You can generate specific desired capabilities using:
      Parsing desired capabilities:
      SeleniumBase has a desired capabilities parser that can capture all lines from the specified file in the following formats: ```python 'KEY': 'VALUE' 'KEY': True 'KEY': False caps['KEY'] = "VALUE" caps['KEY'] = True caps['KEY'] = False ``` (Each pair must be on a separate line. You can interchange single and double quotes.) You can also swap ``--browser=remote`` with an actual browser, eg ``--browser=chrome``, which will combine the default SeleniumBase desired capabilities with those that were specified in the capabilities file when using ``--cap_file=FILE.py``. Capabilities will override other parameters, so if you set the browser to one thing and the capabilities browser to another, SeleniumBase will use the capabilities browser. You'll need default SeleniumBase capabilities for: * Using a proxy server (not the same as a Selenium Grid server) * Downloading files to a desired folder * Disabling some warnings on Chrome * Overriding a website's Content Security Policy on Chrome * Other possible reasons You can also set browser desired capabilities from a command-line string. Eg: ```zsh pytest test_swag_labs.py --cap-string='{"browserName":"chrome","name":"test1"}' --server="127.0.0.1" --browser=remote ``` (Enclose cap-string in single quotes. Enclose parameter keys in double quotes.) If you pass ``"*"`` into the ``"name"`` field of ``--cap-string``, the name will become the test identifier. Eg: ```zsh pytest my_first_test.py --cap-string='{"browserName":"chrome","name":"*"}' --server="127.0.0.1" --browser=chrome ``` Example name: ``"my_first_test.MyTestClass.test_basics"``

      Using a local Selenium Grid

      If using a local Selenium Grid with SeleniumBase, start up the Grid Hub and nodes first: ```zsh sbase grid-hub start sbase grid-node start ``` (The Selenium Server JAR file will be automatically downloaded for first-time Grid users. You'll also need Java installed to start up the Grid.) ================================================ FILE: help_docs/features_list.md ================================================

      (Watch the SeleniumBase tutorial from Selenium-Conf-2023 on YouTube)

      SeleniumBase Features: 🏰

      * A powerful Python framework for browser automation and E2E UI testing. * Includes [Recorder Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md) for instantly generating browser tests in Python. * Supports multiple browsers, tabs, iframes, and proxies in the same test. * Includes [Test Case Management Software](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/case_plans.md) with Markdown technology. * Automatic smart-waiting improves reliability and prevents flaky tests. * Supports [pytest](https://docs.pytest.org/en/latest/), [unittest](https://docs.python.org/3/library/unittest.html), [nose](http://nose.readthedocs.io/en/latest/), and [behave](https://behave.readthedocs.io/en/stable/index.html) for finding/running tests. * All the code is open source. Look inside to learn about any feature. * Powerful logging tools for [dashboards, reports, and screenshots](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md). * Can run tests in Headless Mode to hide the browser. (``--headless``) * Can run tests multithreaded from parallel browsers. (``-n NUM_THREADS``) * Can run tests from a shared browser session. (``--reuse-session``/``--rs``) * Can run tests using [Chromium's mobile device emulator](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/mobile_testing.md). (``--mobile``) * Can run tests through a proxy server. (``--proxy=IP_ADDRESS:PORT``) * Can run tests with proxy settings via PAC URL. (``--proxy-pac-url=URL.pac``) * Can run tests through an authenticated proxy server. (``--proxy=USER:PASS@HOST:PORT``) * Can run tests with proxy+auth via PAC URL. (``--proxy-pac-url=USER:PASS@URL.pac``) * Can run tests with a customized browser user agent. (``--agent=USER_AGENT_STRING``) * Can set a Chromium User Data Directory/Profile to load. (``--user-data-dir=DIR``) * Can [avoid detection](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md) by sites that try to block Selenium. (``--undetected``/``--uc``) * Can [use CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md) for advanced stealth abilities. (``activate_cdp_mode(url)``) * Can integrate with [selenium-wire](https://github.com/wkeeling/selenium-wire) for inspecting browser requests. (``--wire``) * Can load Chrome Extension ZIP files. (``--extension-zip=ZIP``) * Can load Chrome Extension folders. (``--extension-dir=DIR``) * Powerful [console scripts](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md). (Type **``seleniumbase``** or **``sbase``** to use.) * Has the ability to translate tests into [multiple spoken languages](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/translations). * Has a flexible [command-line interface](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) for customizing test runs. * Has a [global config file](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) for configuring settings as needed. * Includes a tool for [creating interactive web presentations](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/ReadMe.md). * Includes [Chart Maker](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/chart_maker/ReadMe.md), a tool for creating interactive charts. * Includes a [dialog box builder](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/dialog_boxes/ReadMe.md) to allow user-input during automation. * Includes a [website tour builder](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md) for creating interactive walkthroughs. * Includes a GUI for running pytest scripts: [SeleniumBase Commander](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/commander.md). * Includes integrations for [GitHub Actions](https://seleniumbase.io/integrations/github/workflows/ReadMe/), [Google Cloud](https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/google_cloud/ReadMe.md), [Azure](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/azure/jenkins/ReadMe.md), [S3](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/s3_logging_plugin.py), and [Docker](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/docker/ReadMe.md). * Can handle Google Authenticator logins with [Python's one-time password library](https://pyotp.readthedocs.io/en/latest/). * Can load and make assertions on PDF files from websites or the local file system. * Can inspect HTML to find issues and points of interest with the [HTML Inspector](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/html_inspector.md). * Is backwards-compatible with Python [WebDriver](https://www.selenium.dev/projects/) methods. (Use: ``self.driver``) * Can execute JavaScript code from Python calls. (Use: ``self.execute_script()``) * Can pierce through [Shadow DOM selectors](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/shadow_dom.md). (Add ``::shadow`` to CSS fragments.) * Includes a hybrid-automation solution, [MasterQA](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/masterqa/ReadMe.md), to speed up manual testing. * Includes useful [Python decorators and password obfuscation methods](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/common/ReadMe.md). --------

      (Have fun with test automation!)

      (Watch the original tutorial on YouTube)

      SeleniumBase

      ================================================ FILE: help_docs/handling_iframes.md ================================================

      How to handle iframes

      🖼️ iframes follow the same principle as new windows: You must first switch to the iframe if you want to perform actions in there: ```python self.switch_to_frame("iframe") # ... Now perform actions inside the iframe self.switch_to_parent_frame() # Exit the current iframe ``` To exit from multiple iframes, use ``self.switch_to_default_content()``. (If inside a single iframe, this has the same effect as ``self.switch_to_parent_frame()``.) ```python self.switch_to_frame('iframe[name="frame1"]') self.switch_to_frame('iframe[name="frame2"]') # ... Now perform actions inside the inner iframe self.switch_to_default_content() # Back to the main page ``` 🖼️ You can also use a context manager to act inside iframes: ```python with self.frame_switch("iframe"): # ... Now perform actions while inside the code block # You have left the iframe ``` This also works with nested iframes: ```python with self.frame_switch('iframe[name="frame1"]'): with self.frame_switch('iframe[name="frame2"]'): # ... Now perform actions while inside the code block # You are now back inside the first iframe # You have left all the iframes ``` 🖼️ In special cases, you may want to set the page to the content of an iframe: ```python self.set_content_to_frame("iframe") ``` To back out of one call of that, use: ```python self.set_content_to_parent() ``` To back out of all nested calls of that, use: ```python self.set_content_to_default() ``` 🖼️ See [SeleniumBase/examples/iframe_tests.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/iframe_tests.py) for tests that use all available iframe commands. --------

      ================================================ FILE: help_docs/happy_customers.md ================================================

      SeleniumBase

      ### Businesses who have used SeleniumBase: * [MIT](https://web.mit.edu/) * [Intel](https://www.intel.com/) * [Sony](https://www.sony.com/) * [Tesla](https://www.tesla.com/) * [iboss](https://www.iboss.com/) * [Apple](https://www.apple.com/) * [Oracle](https://www.oracle.com/) * [Akamai](https://www.akamai.com/) * [Allstate](https://www.allstate.com/) * [Amazon](https://www.amazon.com/) * [Sequoia](https://www.sequoia.com/) * [HubSpot](https://www.hubspot.com/) * [Streamlit](https://streamlit.io/) * [Autodesk](https://www.autodesk.com/) * [Veracode](https://www.veracode.com/) * [Microsoft](https://www.microsoft.com/) * [Medtronic](https://www.medtronic.com/) * [Broadcom](https://www.broadcom.com/) * [Snowflake](https://www.snowflake.com/) * [PlayStation](https://www.playstation.com/) * [Bright Data](https://brightdata.com/) * [GlobalLogic](https://www.globallogic.com/) * [Thoma Bravo](https://www.thomabravo.com/) * [ASICS Digital](https://www.asicsdigital.com/) * [Expedia Group](https://lifeatexpediagroup.com/) * [Deutsche Bank](https://www.db.com/) * [The Very Group](https://www.theverygroup.com/) * [ActiveCampaign](https://www.activecampaign.com/) * [Blue Trail Software](https://www.bluetrail.software/) * [Ness Technologies](https://www.ness-tech.co.il/) * [Johnson & Johnson](https://www.jnj.com/) * [AutomationAnywhere](https://www.automationanywhere.com/) * [Harvard Medical School](https://hms.harvard.edu/) * [Singapore Institute of Tech](https://www.singaporetech.edu.sg) * [Mississippi State University](https://www.msstate.edu/) * [The New York Public Libary](https://www.nypl.org/) * [CCC Intelligent Solutions](https://www.cccis.com/) * [Medical Home Network](https://www.medicalhomenetwork.org/) * [Boehringer Ingelheim](https://www.boehringer-ingelheim.com/) * [Queen's University](https://www.queensu.ca/) * [L1NNA Laboratory](https://l1nna.com/) * [Canfield Scientific](https://www.canfieldsci.com/) * [Thomson Reuters](https://www.thomsonreuters.com/) * [Wellesley College](https://www.wellesley.edu/) * [CA Technologies](https://tinyurl.com/ca-technologies) * [Momin Solutions](https://www.mominsolutions.com/) * [Optum Financial](https://www.optum.com/financial-services.html) * [ActiveCampaign](https://www.activecampaign.com/) * [Sphere Partners](https://www.sphereinc.com/) * [MadeiraMadeira](https://www.madeiramadeira.com.br/) * [Raid The Room](https://raidtheroom.com/) * [First American](https://www.firstam.com/) * [Origin Energy](https://www.originenergy.com.au/) * [InterSystems](https://www.intersystems.com/) * [Develop Soft](https://www.developsoft.com/) * [AstraZeneca](https://www.astrazeneca.com/) * [Ping Identity](https://www.pingidentity.com/) * [Scale Media](https://scalemedia.com/) * [CaptivateIQ](https://www.captivateiq.com/) * [StreamSets](https://streamsets.com/) * [Visual Data](https://www.visualdatamedia.com/) * [Betterteem](https://www.betterteem.com/) * [SenseTime](https://www.sensetime.com/en) * [Rubiscape](https://www.rubiscape.com/) * [Aeturnum](https://aeturnum.com/) * [Cellebrite](https://www.cellebrite.com/en/home/) * [RaySecur](https://www.raysecur.com/) * [Payability](https://www.payability.com/) * [Ben Fatto](https://benfatto.net.br/en/) * [BW Legal](https://www.bwlegal.co.uk/) * [GeoNode](http://geonode.org/) * [Empower](https://empower.me/) * [CardFree](https://cardfree.com/) * [Softrams](https://www.softrams.com/) * [Invensity](https://www.invensity.com/) * [SunCorp](https://www.suncorpgroup.com.au/) * [Logitech](https://www.logitech.com/) * [Modulos](https://www.modulos.ai/) * [Igrowker](https://igrowker.com/) * [axxessio](https://www.axxessio.com/en/) * [StorkJet](https://storkjet.com/) * [Networx](https://www.networx.com/) * [Elevatus](https://www.elevatus.io/) * [VMware](https://www.vmware.com/) * [Rakuten](https://global.rakuten.com/corp/about/) * [Nagarro](https://www.nagarro.com/en) * [Ark PES](https://www.arkpes.com/) * [SkillsVR](https://skillsvr.com/) * [Evereve](https://evereve.com/) * [Planeks](https://www.planeks.net/) * [Upwork](https://www.upwork.com/) * [Yandex](https://yandex.ru/) * [Picsart](https://picsart.com/) * [Serquo](https://serquo.com/) * [WPILib](https://wpilib.org/) * [QBurst](https://www.qburst.com/) * [Kinetik](https://kinetik.care/) * [Exadel](https://exadel.com/) * [netLex](https://netlex.io/en/) * [XP Inc](https://www.xpinc.com/) * [Roche](https://www.roche.com/) * [Alokin](https://alokin.in/) * [Stride](https://stride.ai/) * [Cubic](https://www.cubic.com/) * [Baidu](https://www.baidu.com/) * [Intive](https://intive.com/) * [Seek](https://www.seek.com.au/) * [Vultr](https://www.vultr.com/) * [HqO](https://www.hqo.co/) * [PDS](https://www.pdsinc.com/) * [Pico](https://trypico.com/) * [Iver](https://www.iver.com/) * And more... -------- ### Case Study: > **HubSpot**: In addition to using SeleniumBase for testing the UI of their content management system, HubSpot used SeleniumBase to automate the migration of website pages from their old CMS to their new one, which saved them over one million USD and a significant amount of time. Learn how HubSpot uses SeleniumBase for website testing by reading: [Automated Testing with Selenium](https://dev.hubspot.com/blog/bid/88880/Automated-Integration-Testing-with-Selenium-at-HubSpot#hs_cos_wrapper_name) For more reading about automation at HubSpot, see: [The Classic "QA Team" is Obsolete](https://product.hubspot.com/blog/the-classic-qa-team-is-obsolete#hs_cos_wrapper_name) -------- ### How is this list generated? Most of these rows come from [LinkedIn search results](https://www.linkedin.com/search/results/people/?keywords=seleniumbase&origin=SWITCH_SEARCH_VERTICAL) of profile descriptions, where employees mentioned that they have used SeleniumBase at their company. These details may also be found in other public networks and social media sites, such as GitHub (where organizations could have public repos that use SeleniumBase). ================================================ FILE: help_docs/hidden_files_info.md ================================================ ## Showing hidden files on macOS Depending on your macOS settings, some files may be hidden from view in your Finder window, such as ``.gitignore``. * On newer versions of macOS, use the following in a Finder window to view hidden files: Press the **“Command” + “Shift” + “.” (period)** keys at the same time. (The hidden files will show up as translucent in the folder. If you want to obscure the files again, press the same “Command” + “Shift” + “.” (period) combination.) * On older versions of macOS, use the following command in a Terminal window to view hidden files, and then reopen the Finder window: ```zsh defaults write com.apple.finder AppleShowAllFiles -bool true ``` More info on that can be found here: ================================================ FILE: help_docs/how_it_works.md ================================================

      How SeleniumBase Works 👁️

      👁️🔎 The primary [SeleniumBase syntax format](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md) works by extending [pytest](https://docs.pytest.org/en/latest/) as a direct plugin. SeleniumBase automatically spins up web browsers for tests, and then gives those tests access to the SeleniumBase libraries through the [BaseCase class](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py). Tests are also given access to [SeleniumBase command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) and [SeleniumBase methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md). 👁️🔎 ``pytest`` uses a feature called test discovery to automatically find and run Python methods that start with ``test_`` when those methods are located in Python files that start with ``test_`` or end with ``_test.py``. 👁️🔎 The primary [SeleniumBase syntax format](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md) starts by importing ``BaseCase``: ```python from seleniumbase import BaseCase ``` 👁️🔎 This next line activates ``pytest`` when a file is called directly with ``python`` by accident: ```python BaseCase.main(__name__, __file__) ``` 👁️🔎 Classes can inherit ``BaseCase`` to gain SeleniumBase functionality: ```python class MyTestClass(BaseCase): ``` 👁️🔎 Test methods inside ``BaseCase`` classes become SeleniumBase tests: (These tests automatically launch a web browser before starting, and quit the web browser after ending. Default settings can be changed via command-line options.) ```python class MyTestClass(BaseCase): def test_abc(self): # ... ``` 👁️🔎 [SeleniumBase APIs](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md) can be called from tests via ``self``: ```python class MyTestClass(BaseCase): def test_abc(self): self.open("https://example.com") ``` 👁️🔎 Here's what a full test might look like: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class TestSimpleLogin(BaseCase): def test_simple_login(self): self.open("seleniumbase.io/simple/login") self.type("#username", "demo_user") self.type("#password", "secret_pass") self.click('a:contains("Sign in")') self.assert_exact_text("Welcome!", "h1") self.assert_element("img#image1") self.highlight("#image1") self.click_link("Sign out") self.assert_text("signed out", "#top_message") ``` (See the example, [test_simple_login.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_simple_login.py), for reference.) 👁️🔎 Here are some examples of running tests with ``pytest``: ```zsh pytest test_mfa_login.py pytest --headless -n8 --dashboard --html=report.html -v --rs --crumbs pytest -m marker2 pytest -k agent pytest offline_examples/ ``` 👁️🔎 Here's a [SeleniumBase syntax format](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md) that uses the raw `driver`. Unlike the format mentioned earlier, it can be run with `python` instead of `pytest`. The `driver` includes original `driver` methods and new ones added by SeleniumBase: ```python from seleniumbase import Driver driver = Driver() try: driver.open("seleniumbase.io/simple/login") driver.type("#username", "demo_user") driver.type("#password", "secret_pass") driver.click('a:contains("Sign in")') driver.assert_exact_text("Welcome!", "h1") driver.assert_element("img#image1") driver.highlight("#image1") driver.click_link("Sign out") driver.assert_text("signed out", "#top_message") finally: driver.quit() ``` (See the example, [raw_login_driver.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_driver.py), for reference.) 👁️🔎 Note that regular SeleniumBase formats (ones that use `BaseCase`, the `SB` context manager, or the `sb` `pytest` fixture) have more methods than the improved `driver` format. The regular formats also have more features. Some features, (such as the [SeleniumBase dashboard](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md)), require a `pytest` format. -------- ### ✅ No More Flaky Tests!

      SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (up to a timeout limit). This means you no longer need random time.sleep() statements in your scripts.

      NO MORE FLAKY TESTS! **There are three layers of protection that provide reliability for tests using SeleniumBase:** * **(1)**: Selenium's default ``pageLoadStrategy`` is ``normal``: This strategy causes Selenium to wait for the full page to load, with HTML content and sub-resources downloaded and parsed. * **(2)**: SeleniumBase includes methods such as ``wait_for_ready_state_complete()``, which run inside other SeleniumBase methods to ensure that it's safe to proceed with the next command. * **(3)**: SeleniumBase methods automatically wait for elements to be visible and interactable before interacting with those elements. **If you want to speed up your tests and you think the third level of protection is enough by itself, you can use command-line options to remove the first, the second, or both of those first two levels of protection:** * ``--pls=none`` --> Set ``pageLoadStrategy`` to ``"none"``: This strategy causes Selenium to return immediately after the initial HTML content is fully received by the browser. * ``--sjw`` --> Skip JS Waits, such as ``wait_for_ready_state_complete()``. --------

      SeleniumBase

      ================================================ FILE: help_docs/html_inspector.md ================================================

      The HTML Inspector 🕵️

      🕵️ HTML Inspector provides useful info about a web page. 🕵️ (Based on: [github.com/philipwalton/html-inspector](https://github.com/philipwalton/html-inspector)) 🕵️ Example: [examples/test_inspect_html.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_inspect_html.py) (Chromium-only) ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class HtmlInspectorTests(BaseCase): def test_html_inspector(self): self.open("https://xkcd.com/1144/") self.inspect_html() ``` -------- ```zsh pytest test_inspect_html.py ============== test session starts ============== * HTML Inspection Results: https://xkcd.com/1144/ ⚠️ 'property' is not a valid attribute of the element. ⚠️ Do not use
      or elements without any attributes. ⚠️ 'srcset' is not a valid attribute of the element. ⚠️ The 'border' attribute is no longer valid on the element. ⚠️ The
      element is obsolete. ⚠️ The id 'comicLinks' appears more than once in the document. * (See the Console output for details!) ``` ================================================ FILE: help_docs/install.md ================================================

      SeleniumBase Installation

      If installing seleniumbase directly from PyPI, (the Python Package Index), use:

      ```zsh pip install seleniumbase ```

      To upgrade an existing seleniumbase install from PyPI:

      ```zsh pip install -U seleniumbase ```

      If installing seleniumbase from a Git clone, use:

      ```zsh git clone https://github.com/seleniumbase/SeleniumBase.git cd SeleniumBase/ pip install . ```

      For a development mode install in editable mode, use:

      ```zsh git clone https://github.com/seleniumbase/SeleniumBase.git cd SeleniumBase/ pip install -e . ```

      To upgrade an existing seleniumbase install from GitHub:

      ```zsh git pull # To pull the latest version pip install -e . # Or "pip install ." ```

      If installing seleniumbase from a GitHub branch, use:

      ```zsh pip install git+https://github.com/seleniumbase/SeleniumBase.git@master#egg=seleniumbase ```

      pip install can be customized:

      * (Add ``--upgrade`` OR ``-U`` to upgrade SeleniumBase.) * (Add ``--force-reinstall`` to upgrade indirect libraries.) (If you're not using a virtual environment, you may need to add ``--user`` to your ``pip`` command if you're seeing errors during installation.) -------- [](https://github.com/seleniumbase/SeleniumBase/) ================================================ FILE: help_docs/install_python_pip_git.md ================================================ ## Installation instructions for ``Git``, ``Python``, and ``pip`` ### [Git](http://www.git-scm.com) You can [download Git from here](http://git-scm.com/downloads). (A Git GUI tool like [SourceTree](https://www.sourcetreeapp.com/) or [GitHub Desktop](https://desktop.github.com/) can help you with Git commands.) (You can also download SeleniumBase from GitHub without using git-related commands.) ### [Python](https://www.python.org) You can download Python from [https://www.python.org/downloads/](https://www.python.org/downloads/) if it's not already preinstalled on your machine. ### [pip](https://en.wikipedia.org/wiki/Pip_%28package_manager%29) **``pip`` already comes with Python!** (It lets you install packages, such as ``seleniumbase``.) ⚠️ If something went wrong with your ``pip`` installation, try this: ```zsh python -m ensurepip --default-pip ``` If your existing version of pip is old, upgrade to the latest version: ```zsh python -m pip install --upgrade pip setuptools ``` On CentOS 7 and some versions of Linux, you may need to install pip with ``yum``: ```zsh yum -y update yum -y install python-pip ``` If you're having any trouble getting pip, you can [GET PIP HERE](https://pip.pypa.io/en/latest/installation/). When done, make sure the location of pip is on your path, which is ``$PATH`` for macOS/Linux. (On Windows, it's the System Variables ``Path`` within System Environment Variables.) You can also get pip (or fix pip) by using: ```zsh curl https://bootstrap.pypa.io/get-pip.py | python ``` * (If you get SSL errors while trying to install packages with pip, see [this Stackoverflow post](https://stackoverflow.com/questions/49768770/not-able-to-install-python-packages-ssl-tlsv1-alert-protocol-version), which tells you to run the above command.) **Keep Pip and Setuptools up-to-date:** ```zsh python -m pip install -U pip setuptools ``` * (Depending on your user permissions, you may need to add ``--user`` to the command if you're not inside a [Python virtual environment](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/virtualenv_instructions.md), or use "[sudo](https://en.wikipedia.org/wiki/Sudo)" on a UNIX-based OS if you're getting errors during installation.) ================================================ FILE: help_docs/js_package_manager.md ================================================

      JS Package Manager and Code Generators

      ❇️ SeleniumBase lets you load JavaScript packages from any CDN link into any website via Python.

      🎨 The following SeleniumBase solutions utilize this feature: 🎦 (Demo Mode) 🚎 (Website Tours) 🎞️ (Presentation Maker) 📊 (Chart Maker / Dashboard) 🛂 (Dialog Boxes / MasterQA) --------

      🗺️ Here's an example of loading a website-tour library into the browser for a Google Maps tour:


      🗺️ This example is from maps_introjs_tour.py. (The --interval=1 makes the tour go automatically to the next step after 1 second.)

      ```zsh cd examples/tour_examples pytest maps_introjs_tour.py --interval=1 ```

      ❇️ SeleniumBase includes powerful JS code generators for converting Python into JavaScript for using the supported JS packages. A few lines of Python in your tests might generate hundreds of lines of JavaScript.

      🗺️ Here is some tour code in Python from maps_introjs_tour.py that expands into a lot of JavaScript.

      ```python self.open("https://www.google.com/maps/@42.3591234,-71.0915634,15z") self.create_tour(theme="introjs") self.add_tour_step("Welcome to Google Maps!", title="SeleniumBase Tours") self.add_tour_step("Enter Location", "#searchboxinput", title="Search Box") self.add_tour_step("See it", "#searchbox-searchbutton", alignment="bottom") self.add_tour_step("Thanks for using Tours!", title="End of Guided Tour") self.export_tour(filename="maps_introjs_tour.js") self.play_tour() ```

      ❇️ For existing features, SeleniumBase already takes care of loading all the necessary JS and CSS files into the web browser. To load other packages, here are a few useful methods that you should know about:

      ```python self.add_js_link(js_link) ```

      ❇️ This example loads the IntroJS JavaScript library:

      ```python self.add_js_link("https://cdn.jsdelivr.net/npm/intro.js@5.1.0/intro.min.js") ```
      ❇️ You can load any JS package this way as long as you know the URL.

      ❇️ If you're wondering how SeleniumBase does this, here's the full Python code from js_utils.py, which uses WebDriver's execute_script() method for making JS calls after escaping quotes with backslashes as needed:

      ```python def add_js_link(driver, js_link): script_to_add_js = ( """function injectJS(link) { var body = document.getElementsByTagName("body")[0]; var script = document.createElement("script"); script.src = link; script.defer; script.type="text/javascript"; script.crossorigin = "anonymous"; script.onload = function() { null }; body.appendChild(script); } injectJS("%s");""") js_link = escape_quotes_if_needed(js_link) driver.execute_script(script_to_add_js % js_link) ```

      ❇️ Now that you've loaded JavaScript into the browser, you may also want to load some CSS to go along with it:

      ```python self.add_css_link(css_link) ```

      ❇️ Here's code that loads the IntroJS CSS:

      ```python self.add_css_link("https://cdnjs.cloudflare.com/ajax/libs/intro.js/2.9.3/introjs.css") ```

      ❇️ And here's the Python WebDriver code that makes this possible:

      ```python def add_css_link(driver, css_link): script_to_add_css = ( """function injectCSS(css) { var head = document.getElementsByTagName("head")[0]; var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = css; link.crossorigin = "anonymous"; head.appendChild(link); } injectCSS("%s");""") css_link = escape_quotes_if_needed(css_link) driver.execute_script(script_to_add_css % css_link) ```
      ❇️ Website tours are just one of the many uses of the JS Package Manager.

      🛂 The following example shows the JqueryConfirm package loaded into a website for creating fancy dialog boxes:

      SeleniumBase

      ↕️ (Example: dialog_box_tour.py) ↕️

      SeleniumBase

      Here's how to run that example:

      ```zsh cd examples/dialog_boxes pytest test_dialog_boxes.py ```

      (Example from the Dialog Boxes ReadMe)

      ❇️ Since packages are loaded directly from a CDN link, you won't need other package managers like NPM, Bower, or Yarn to get the packages that you need into the websites that you want.
      --------
      To learn more about SeleniumBase, check out the Docs Site:
      SeleniumBase.io Docs
      All the code is on GitHub:
      SeleniumBase on GitHub ================================================ FILE: help_docs/locale_codes.md ================================================

      Language Locale Codes

      You can specify a Language Locale Code to customize web pages on supported websites. With SeleniumBase, you can change the web browser's Locale on the command-line by doing this: ```zsh pytest --locale=CODE # Example: --locale=ru ``` From the ``SB()`` and ``Driver()`` formats, you can also set the ``locale_code`` arg like this: ```python locale_code="CODE" # Example: SB(locale_code="en") ```

      List of Language Locale Codes:

      LanguageCode
      Afrikaansaf
      Amharicam
      Arabicar
      Arabic (Egypt)ar_eg
      Arabic (Saudi Arabia)ar_sa
      Basqueeu
      Belarusianbe
      Bengalibn
      Bulgarianbg
      Catalanca
      Chinesezh
      Chinese (China Mainland)zh_cn
      Chinese (Hong Kong)zh_hk
      Chinese (Taiwan)zh_tw
      Croatianhr
      Czechcs
      Danishda
      Dutchnl
      Englishen
      English (United States)en_us
      English (Australia)en_au
      English (Canada)en_ca
      English (United Kingdom)en_gb
      English (Ireland)en_ie
      English (India)en_in
      English (Singapore)en_sg
      English (South Africa)en_za
      Estonianet
      Farsifa
      Filipinofil
      Finnishfi
      Frenchfr
      French (Canada)fr_ca
      French (Switzerland)fr_ch
      Galiciangl
      Germande
      German (Austria)de_at
      Greekel
      Gujaratigu
      Hebrewhe
      Hindihi
      Hungarianhu
      Icelandicis
      Indonesianid
      Italianit
      Japaneseja
      Kannadakn
      Koreanko
      Laolo
      Latvianlv
      Lingalaln
      Lithuanianlt
      Malayms
      Malayalamml
      Marathimr
      Norwegianno
      Polishpl
      Portuguesept
      Portuguese (Brazil)pt_br
      Portuguese (Portugal)pt_pt
      Romanianro
      Russianru
      Serbiansr
      Slovaksk
      Sloveniansl
      Spanishes
      Spanish (Latin America)es_419
      Spanish (Argentina)es_ar
      Spanish (Chile)es_cl
      Spanish (Colombia)es_co
      Spanish (Costa Rica)es_cr
      Spanish (Dominican Rep.)es_do
      Spanish (Ecuador)es_ec
      Spanish (El Salvador)es_sv
      Spanish (Guatemala)es_gt
      Spanish (Honduras)es_hn
      Spanish (Mexico)es_mx
      Spanish (Nicaragua)es_ni
      Spanish (Panama)es_pa
      Spanish (Peru)es_pe
      Spanish (Puerto Rico)es_pr
      Spanish (Paraguay)es_py
      Spanish (United States)es_us
      Spanish (Uruguay)es_uy
      Spanish (Venezuela)es_ve
      Swahilisw
      Swedishsv
      Swiss Germangsw
      Tagalogtl
      Tamilta
      Telugute
      Thaith
      Turkishtr
      Ukrainianuk
      Urduur
      Vietnamesevi
      Zuluzu
      ================================================ FILE: help_docs/method_summary.md ================================================

      SeleniumBase Methods (API Reference)

      (Common API Methods on YouTube)

      Here's a list of SeleniumBase method definitions, which are defined in **[base_case.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py)** For backwards compatibility, older versions of method names have remained to keep older scripts working. *(E.g: wait_for_element_visible was shortened to wait_for_element and then to find_element.)* ```python self.open(url) # Duplicates: self.open_url(url), self.visit(url), visit_url(url), # self.goto(url), self.go_to(url) self.get(url) # If the url parameter is a URL: Perform self.open(url) # Otherwise: return self.get_element(URL_AS_A_SELECTOR) self.click(selector, by="css selector", timeout=None, delay=0, scroll=True) self.slow_click(selector, by="css selector", timeout=None) self.double_click(selector, by="css selector", timeout=None) self.context_click(selector, by="css selector", timeout=None) # Duplicates: # self.right_click(selector, by="css selector", timeout=None) self.click_chain(selectors_list, by="css selector", timeout=None, spacing=0) self.type(selector, text, by="css selector", timeout=None) # Duplicates: # self.update_text(selector, text, by="css selector", timeout=None) # self.input(selector, text, by="css selector", timeout=None) # self.fill(selector, text, by="css selector", timeout=None) # self.write(selector, text, by="css selector", timeout=None) self.send_keys(selector, text, by="css selector", timeout=None) # Duplicates: # self.add_text(selector, text, by="css selector", timeout=None) self.press_keys(selector, text, by="css selector", timeout=None) self.submit(selector, by="css selector") self.clear(selector, by="css selector", timeout=None) self.focus(selector, by="css selector", timeout=None) self.refresh() # Duplicates: self.refresh_page(), self.reload_page(), self.reload() self.get_current_url() self.get_origin() self.get_html() self.get_page_source() self.get_title() # Duplicates: self.get_page_title() self.get_user_agent() self.get_locale_code() self.go_back() self.go_forward() self.open_start_page() self.open_if_not_url(url) self.is_element_present(selector, by="css selector") self.is_element_visible(selector, by="css selector") self.is_element_clickable(selector, by="css selector") self.is_element_enabled(selector, by="css selector") self.is_text_visible(text, selector="html", by="css selector") self.is_exact_text_visible(text, selector="html", by="css selector") self.is_non_empty_text_visible(selector="html", by="css selector") self.is_attribute_present(selector, attribute, value=None, by="css selector") self.is_link_text_visible(link_text) self.is_partial_link_text_visible(partial_link_text) self.is_link_text_present(link_text) self.is_partial_link_text_present(link_text) self.get_link_attribute(link_text, attribute, hard_fail=True) # Duplicates: # self.get_link_text_attribute(link_text, attribute, hard_fail=True) self.get_partial_link_text_attribute(link_text, attribute, hard_fail=True) self.click_link(link_text, timeout=None) # Duplicates: # self.click_link_text(link_text, timeout=None) self.click_partial_link(partial_link_text, timeout=None) # Duplicates: # self.click_partial_link_text(partial_link_text, timeout=None) self.get_text(selector="html", by="css selector", timeout=None) self.get_attribute(selector, attribute, by="css selector", timeout=None, hard_fail=True) self.set_attribute(selector, attribute, value, by="css selector", timeout=None, scroll=False) self.set_attributes(selector, attribute, value, by="css selector") # Duplicates: # self.set_attribute_all(selector, attribute, value, by="css selector") self.remove_attribute(selector, attribute, by="css selector", timeout=None) self.remove_attributes(selector, attribute, by="css selector") self.internalize_links() self.get_parent(element, by="css selector", timeout=None) self.get_property(selector, property, by="css selector", timeout=None) self.get_text_content(selector="html", by="css selector", timeout=None) self.get_property_value(selector, property, by="css selector", timeout=None) self.get_image_url(selector, by="css selector", timeout=None) self.find_elements(selector, by="css selector", limit=0) # Duplicates: # self.select_all(selector, by="css selector", limit=0) self.find_visible_elements(selector, by="css selector", limit=0) self.click_visible_elements(selector, by="css selector", limit=0, timeout=None) self.click_nth_visible_element(selector, number, by="css selector", timeout=None) self.click_if_visible(selector, by="css selector", timeout=0) self.click_active_element() self.click_with_offset( selector, x, y, by="css selector", mark=None, timeout=None, center=None) self.double_click_with_offset( selector, x, y, by="css selector", mark=None, timeout=None, center=None) self.is_checked(selector, by="css selector", timeout=None) # Duplicates: # self.is_selected(selector, by="css selector", timeout=None) self.check_if_unchecked(selector, by="css selector") # Duplicates: # self.select_if_unselected(selector, by="css selector") self.uncheck_if_checked(selector, by="css selector") # Duplicates: # self.unselect_if_selected(selector, by="css selector") self.is_element_in_an_iframe(selector, by="css selector") self.switch_to_frame_of_element(selector, by="css selector") self.hover(selector, by="css selector", timeout=None) # Duplicates: # self.hover_element(selector, by="css selector", timeout=None) # self.hover_on_element(selector, by="css selector", timeout=None) # self.hover_over_element(selector, by="css selector", timeout=None) self.hover_and_click( hover_selector, click_selector, hover_by="css selector", click_by="css selector", timeout=None, js_click=False) self.hover_and_js_click( hover_selector, click_selector, hover_by="css selector", click_by="css selector", timeout=None) self.hover_and_double_click( hover_selector, click_selector, hover_by="css selector", click_by="css selector", timeout=None) self.drag_and_drop( drag_selector, drop_selector, drag_by="css selector", drop_by="css selector", timeout=None, jquery=False) self.drag_and_drop_with_offset( selector, x, y, by="css selector", timeout=None) self.select_option_by_text( dropdown_selector, option, dropdown_by="css selector", timeout=None) self.select_option_by_index( dropdown_selector, option, dropdown_by="css selector", timeout=None) self.select_option_by_value( dropdown_selector, option, dropdown_by="css selector", timeout=None) self.get_select_options( dropdown_selector, attribute="text", by="css selector", timeout=None) self.load_html_string(html_string, new_page=True) self.set_content(html_string, new_page=False) self.load_html_file(html_file, new_page=True) self.open_html_file(html_file) self.evaluate(expression) self.execute_script(script, *args, **kwargs) self.execute_cdp_cmd(script, *args, **kwargs) self.execute_async_script(script, timeout=None) self.safe_execute_script(script, *args, **kwargs) self.get_element_at_x_y(x, y) self.get_gui_element_rect(selector, by="css selector") self.get_gui_element_center(selector, by="css selector") self.get_screen_rect() self.get_window_rect() self.get_window_size() self.get_window_position() self.set_window_rect(x, y, width, height) self.set_window_size(width, height) self.set_window_position(x, y) self.maximize_window() # Duplicates: self.maximize() self.minimize_window() # Duplicates: self.minimize() self.reset_window_size() self.switch_to_frame(frame="iframe", timeout=None, invisible=False) self.switch_to_default_content() self.switch_to_parent_frame() with self.frame_switch(frame, timeout=None): # Indented Code Block for Context Manager (Must use "with") self.set_content_to_frame(frame, timeout=None) self.set_content_to_default(nested=False) # Duplicates: self.set_content_to_default_content(nested=False) self.set_content_to_parent() # Duplicates: self.set_content_to_parent_frame() self.open_new_window(switch_to=True) # Duplicates: self.open_new_tab(switch_to=True) self.switch_to_window(window, timeout=None) # Duplicates: self.switch_to_tab(tab, timeout=None) self.switch_to_default_window() # Duplicates: self.switch_to_default_tab() self.switch_to_newest_window() # Duplicates: self.switch_to_newest_tab() self.get_new_driver( browser=None, headless=None, locale_code=None, protocol=None, servername=None, port=None, proxy=None, proxy_bypass_list=None, proxy_pac_url=None, multi_proxy=None, agent=None, switch_to=True, cap_file=None, cap_string=None, recorder_ext=None, disable_cookies=None, disable_js=None, disable_csp=None, enable_ws=None, enable_sync=None, use_auto_ext=None, undetectable=None, uc_cdp_events=None, uc_subprocess=None, log_cdp_events=None, no_sandbox=None, disable_gpu=None, headless1=None, headless2=None, incognito=None, guest_mode=None, dark_mode=None, devtools=None, remote_debug=None, enable_3d_apis=None, swiftshader=None, ad_block_on=None, host_resolver_rules=None, block_images=None, do_not_track=None, chromium_arg=None, firefox_arg=None, firefox_pref=None, user_data_dir=None, extension_zip=None, extension_dir=None, disable_features=None, binary_location=None, driver_version=None, page_load_strategy=None, use_wire=None, external_pdf=None, is_mobile=None, d_width=None, d_height=None, d_p_r=None, ) self.switch_to_driver(driver) self.switch_to_default_driver() self.save_screenshot(name, folder=None, selector=None, by="css selector") self.save_screenshot_to_logs(name=None, selector=None, by="css selector") self.save_as_pdf_to_logs(name=None) self.save_data_to_logs(data, file_name=None) self.append_data_to_logs(data, file_name=None) self.save_page_source_to_logs(name=None) self.save_page_source(name, folder=None) # Duplicates: self.save_as_html(name, folder=None) self.save_cookies(name="cookies.txt") self.load_cookies(name="cookies.txt", expiry=False) self.delete_all_cookies() # Duplicates: self.clear_all_cookies() self.delete_saved_cookies(name="cookies.txt") self.get_saved_cookies(name="cookies.txt") self.get_cookie(name) self.get_cookies() self.get_cookie_string() self.add_cookie(cookie_dict, expiry=False) self.add_cookies(cookies, expiry=False) self.wait_for_ready_state_complete(timeout=None) self.wait_for_angularjs(timeout=None) self.sleep(seconds) # Duplicates: self.wait(seconds) self.install_addon(xpi_file) self.activate_jquery() self.activate_demo_mode() self.deactivate_demo_mode() self.activate_design_mode() self.deactivate_design_mode() self.activate_recorder() self.save_recorded_actions() self.bring_active_window_to_front() self.bring_to_front(selector, by="css selector") self.highlight_click(selector, by="css selector", loops=3, scroll=True, timeout=None) self.highlight_type(selector, text, by="css selector", loops=3, scroll=True, timeout=None) # Duplicates: # self.highlight_update_text( # selector, text, by="css selector", loops=3, scroll=True, timeout=None) self.highlight_if_visible(selector, by="css selector", loops=4, scroll=True) self.highlight(selector, by="css selector", loops=4, scroll=True, timeout=None) self.highlight_elements(selector, by="css selector", loops=4, scroll=True, limit=0) self.press_up_arrow(selector="html", times=1, by="css selector") self.press_down_arrow(selector="html", times=1, by="css selector") self.press_left_arrow(selector="html", times=1, by="css selector") self.press_right_arrow(selector="html", times=1, by="css selector") self.scroll_to(selector, by="css selector", timeout=None) # Duplicates: # self.scroll_to_element(selector, by="css selector") self.slow_scroll_to(selector, by="css selector", timeout=None) # Duplicates: # self.slow_scroll_to_element(selector, by="css selector") self.scroll_into_view(selector, by="css selector", timeout=None) self.scroll_to_top() self.scroll_to_bottom() self.scroll_to_y(y) self.scroll_by_y(y) self.scroll_up(amount=25) self.scroll_down(amount=25) self.click_xpath(xpath) self.js_click(selector, by="css selector", all_matches=False, timeout=None, scroll=True) self.js_click_if_present(selector, by="css selector", timeout=0) self.js_click_if_visible(selector, by="css selector", timeout=0) self.js_click_all(selector, by="css selector", timeout=None) self.jquery_click(selector, by="css selector", timeout=None) self.jquery_click_all(selector, by="css selector", timeout=None) self.hide_element(selector, by="css selector") self.hide_elements(selector, by="css selector") self.show_element(selector, by="css selector") self.show_elements(selector, by="css selector") self.remove_element(selector, by="css selector") self.remove_elements(selector, by="css selector") self.ad_block() # Duplicates: self.block_ads() self.show_file_choosers() self.disable_beforeunload() self.get_domain_url(url) self.get_active_element_css() self.get_beautiful_soup(source=None) self.get_unique_links() self.get_link_status_code(link, allow_redirects=False, timeout=5, verify=False) self.assert_link_status_code_is_not_404(link) self.assert_no_404_errors(multithreaded=True, timeout=None) # Duplicates: # self.assert_no_broken_links(multithreaded=True, timeout=None) self.print_unique_links_with_status_codes() self.get_pdf_text( pdf, page=None, maxpages=None, password=None, codec='utf-8', wrap=False, nav=False, override=False, caching=True) self.assert_pdf_text( pdf, text, page=None, maxpages=None, password=None, codec='utf-8', wrap=True, nav=False, override=False, caching=True) self.create_folder(folder) self.choose_file(selector, file_path, by="css selector", timeout=None) self.save_element_as_image_file(selector, file_name, folder=None, overlay_text="") self.download_file(file_url, destination_folder=None) self.save_file_as(file_url, new_file_name, destination_folder=None) self.save_data_as(data, file_name, destination_folder=None) self.append_data_to_file(data, file_name, destination_folder=None) self.get_file_data(file_name, folder=None) self.print_to_pdf(name, folder=None) # Duplicates: self.save_as_pdf(name, folder=None) self.get_downloads_folder() self.get_browser_downloads_folder() self.get_downloaded_files(regex=None, browser=False) self.get_path_of_downloaded_file(file, browser=False) self.get_data_from_downloaded_file(file, timeout=None, browser=False) self.is_downloaded_file_present(file, browser=False) self.is_downloaded_file_regex_present(regex, browser=False) self.delete_downloaded_file_if_present(file, browser=False) # Duplicates: self.delete_downloaded_file(file, browser=False) self.assert_downloaded_file(file, timeout=None, browser=False) self.assert_downloaded_file_regex(regex, timeout=None, browser=False) self.assert_data_in_downloaded_file(data, file, timeout=None, browser=False) self.assert_true(expr, msg=None) self.assert_false(expr, msg=None) self.assert_equal(first, second, msg=None) self.assert_not_equal(first, second, msg=None) self.assert_in(first, second, msg=None) self.assert_not_in(first, second, msg=None) self.assert_raises(*args, **kwargs) self.wait_for_attribute(selector, attribute, value=None, by="css selector", timeout=None) self.assert_attribute(selector, attribute, value=None, by="css selector", timeout=None) self.assert_title(title) self.assert_title_contains(substring) self.assert_url(url) self.assert_url_contains(substring) self.assert_no_js_errors(exclude=[]) self.inspect_html() self.is_valid_url(url) self.is_alert_present() self.is_online() self.is_chromium() self.get_chrome_version() self.get_chromium_version() self.get_chromedriver_version() self.get_chromium_driver_version() self.get_mfa_code(totp_key=None) # Duplicates: # self.get_totp_code(totp_key=None) # self.get_google_auth_password(totp_key=None) # self.get_google_auth_code(totp_key=None) self.enter_mfa_code(selector, totp_key=None, by="css selector", timeout=None) # Duplicates: # self.enter_totp_code(selector, totp_key=None, by="css selector", timeout=None) self.convert_css_to_xpath(css) self.convert_xpath_to_css(xpath) self.convert_to_css_selector(selector, by) self.set_value(selector, text, by="css selector", timeout=None, scroll=True) self.js_update_text(selector, text, by="css selector", timeout=None) # Duplicates: # self.js_type(selector, text, by="css selector", timeout=None) # self.set_text(selector, text, by="css selector", timeout=None) self.set_text_content(selector, text, by="css selector", timeout=None, scroll=False) self.jquery_update_text(selector, text, by="css selector", timeout=None) # Duplicates: # self.jquery_type(selector, text, by="css selector", timeout=None) self.get_value(selector, by="css selector", timeout=None) self.set_time_limit(time_limit) self.set_default_timeout(timeout) self.reset_default_timeout() self.fail(msg=None) self.skip(reason="") ############ self.start_recording_console_logs() self.console_log_string(string) self.console_log_script(script) self.get_recorded_console_logs() ############ self.set_local_storage_item(key, value) self.get_local_storage_item(key) self.remove_local_storage_item(key) self.clear_local_storage() # Duplicates: self.delete_local_storage() self.get_local_storage_keys() self.get_local_storage_items() self.set_session_storage_item(key, value) self.get_session_storage_item(key) self.remove_session_storage_item(key) self.clear_session_storage() # Duplicates: self.delete_session_storage() self.get_session_storage_keys() self.get_session_storage_items() ############ self.set_wire_proxy(string) # Requires "--wire"! ############ self.add_css_link(css_link) self.add_js_link(js_link) self.add_css_style(css_style) self.add_js_code_from_link(js_link) self.add_js_code(js_code) self.add_meta_tag(http_equiv=None, content=None) ############ self.create_presentation(name=None, theme="default", transition="default") self.add_slide( content=None, image=None, code=None, iframe=None, content2=None, notes=None, transition=None, name=None) self.save_presentation(name=None, filename=None, show_notes=False, interval=0) self.begin_presentation(name=None, filename=None, show_notes=False, interval=0) ############ self.create_pie_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, libs=True, labels=True, legend=True) self.create_bar_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, libs=True, labels=True, legend=True) self.create_column_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, libs=True, labels=True, legend=True) self.create_line_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, zero=False, libs=True, labels=True, legend=True) self.create_area_chart( chart_name=None, title=None, subtitle=None, data_name=None, unit=None, zero=False, libs=True, labels=True, legend=True) self.add_series_to_chart(data_name=None, chart_name=None) self.add_data_point(label, value, color=None, chart_name=None) self.save_chart(chart_name=None, filename=None, folder=None) self.display_chart(chart_name=None, filename=None, interval=0) self.extract_chart(chart_name=None) ############ self.create_tour(name=None, theme=None) self.create_shepherd_tour(name=None, theme=None) self.create_bootstrap_tour(name=None) self.create_hopscotch_tour(name=None) self.create_introjs_tour(name=None) self.set_introjs_colors(theme_color=None, hover_color=None) self.add_tour_step(message, selector=None, name=None, title=None, theme=None, alignment=None) self.play_tour(name=None, interval=0) # Duplicates: self.start_tour(name=None, interval=0): self.export_tour(name=None, filename="my_tour.js", url=None) ############ self.activate_jquery_confirm() self.set_jqc_theme(theme, color=None, width=None) self.reset_jqc_theme() self.get_jqc_button_input(message, buttons, options=None) self.get_jqc_text_input(message, button=None, options=None) self.get_jqc_form_inputs(message, buttons, options=None) ############ self.activate_messenger() self.post_message(message, duration=None, pause=True, style="info") self.post_message_and_highlight(message, selector, by="css selector") self.post_success_message(message, duration=None, pause=True) self.post_error_message(message, duration=None, pause=True) self.set_messenger_theme(theme="default", location="default", max_messages="default") ############ self.generate_referral(start_page, destination_page, selector=None) self.generate_traffic(start_page, destination_page, loops=1, selector=None) self.generate_referral_chain(pages) self.generate_traffic_chain(pages, loops=1) ############ self.get_element(selector, by="css selector", timeout=None) # Duplicates: # self.wait_for_selector(selector, by="css selector", timeout=None) # self.locator(selector, by="css selector", timeout=None) # self.wait_for_element_present(selector, by="css selector", timeout=None) self.wait_for_query_selector(selector, by="css selector", timeout=None) self.assert_element_present(selector, by="css selector", timeout=None) self.assert_elements_present(*args, **kwargs) ############ self.find_element(selector, by="css selector", timeout=None) # Duplicates: # self.wait_for_element(selector, by="css selector", timeout=None) # self.wait_for_element_visible(selector, by="css selector", timeout=None) self.assert_element(selector, by="css selector", timeout=None) # Duplicates: # self.assert_element_visible(selector, by="css selector", timeout=None) self.assert_elements(*args, **kwargs) # Duplicates: # self.assert_elements_visible(*args, **kwargs) ############ self.wait_for_any_of_elements_visible(*args, **kwargs) self.wait_for_any_of_elements_present(*args, **kwargs) self.assert_any_of_elements_visible(*args, **kwargs) self.assert_any_of_elements_present(*args, **kwargs) ############ self.find_text(text, selector="html", by="css selector", timeout=None) # Duplicates: # self.wait_for_text(text, selector="html", by="css selector", timeout=None) # self.wait_for_text_visible(text, selector="html", by="css selector", timeout=None) self.find_exact_text(text, selector="html", by="css selector", timeout=None) # Duplicates: # self.wait_for_exact_text(text, selector="html", by="css selector", timeout=None) # self.wait_for_exact_text_visible(text, selector="html", by="css selector", timeout=None) self.find_non_empty_text(selector="html", by="css selector", timeout=None) # Duplicates: # self.wait_for_non_empty_text(selector="html", by="css selector", timeout=None) # self.wait_for_non_empty_text_visible(selector="html", by="css selector", timeout=None) self.assert_text(text, selector="html", by="css selector", timeout=None) # Duplicates: # self.assert_text_visible(text, selector="html", by="css selector", timeout=None) self.assert_exact_text(text, selector="html", by="css selector", timeout=None) ############ self.wait_for_link_text_present(link_text, timeout=None) self.wait_for_partial_link_text_present(link_text, timeout=None) self.find_link_text(link_text, timeout=None) # Duplicates: # self.wait_for_link_text(link_text, timeout=None) # self.wait_for_link_text_visible(link_text, timeout=None) self.assert_link_text(link_text, timeout=None) # Duplicates: self.assert_link(link_text, timeout=None) ############ self.find_partial_link_text(partial_link_text, timeout=None) # Duplicates: # self.wait_for_partial_link_text(partial_link_text, timeout=None) self.assert_partial_link_text(partial_link_text, timeout=None) ############ self.wait_for_element_absent(selector, by="css selector", timeout=None) # Duplicates: # self.wait_for_element_not_present(selector, by="css selector", timeout=None) self.assert_element_absent(selector, by="css selector", timeout=None) # Duplicates: # self.assert_element_not_present(selector, by="css selector", timeout=None) ############ self.wait_for_element_clickable(selector, by="css selector", timeout=None) ############ self.wait_for_element_not_visible(selector, by="css selector", timeout=None) self.assert_element_not_visible(selector, by="css selector", timeout=None) ############ self.wait_for_text_not_visible(text, selector="html", by="css selector", timeout=None) self.wait_for_exact_text_not_visible(text, selector="html", by="css selector", timeout=None) self.assert_text_not_visible(text, selector="html", by="css selector", timeout=None) self.assert_exact_text_not_visible(text, selector="html", by="css selector", timeout=None) self.assert_non_empty_text(selector="html", by="css selector", timeout=None) ############ self.wait_for_attribute_not_present( selector, attribute, value=None, by="css selector", timeout=None) self.assert_attribute_not_present( selector, attribute, value=None, by="css selector", timeout=None) ############ self.accept_alert(timeout=None) # Duplicates: self.wait_for_and_accept_alert(timeout=None) self.dismiss_alert(timeout=None) # Duplicates: self.wait_for_and_dismiss_alert(timeout=None) self.switch_to_alert(timeout=None) # Duplicates: self.wait_for_and_switch_to_alert(timeout=None) ############ self.quit_extra_driver(driver=None) ############ self.check_window(name="default", level=0, baseline=False, check_domain=True, full_diff=False) ############ self.deferred_assert_element(selector, by="css selector", timeout=None, fs=False) # Duplicates: # self.delayed_assert_element( # selector, by="css selector", timeout=None, fs=False) self.deferred_assert_element_present(selector, by="css selector", timeout=None, fs=False) # Duplicates: # self.delayed_assert_element_present( # selector, by="css selector", timeout=None, fs=False) self.deferred_assert_text(text, selector="html", by="css selector", timeout=None, fs=False) # Duplicates: # self.delayed_assert_text( # text, selector="html", by="css selector", timeout=None, fs=False) self.deferred_assert_exact_text( text, selector="html", by="css selector", timeout=None, fs=False) # Duplicates: # self.delayed_assert_exact_text( # text, selector="html", by="css selector", timeout=None, fs=False) self.deferred_assert_non_empty_text( selector="html", by="css selector", timeout=None, fs=False) # Duplicates: # self.delayed_assert_non_empty_text( # selector="html", by="css selector", timeout=None, fs=False) self.deferred_check_window( name="default", level=0, baseline=False, check_domain=True, full_diff=False, fs=False) # Duplicates: # self.delayed_check_window( # name="default", level=0, baseline=False, # check_domain=True, full_diff=False, fs=False) self.process_deferred_asserts(print_only=False) # Duplicates: self.process_delayed_asserts(print_only=False) ############ self.fail(msg=None) # Inherited from "unittest" self._check_browser() # Fails test cleanly if the active window is closed self._print(TEXT) # Calls Python's print() / Allows for translations ############ # **** UC Mode methods. (uc=True / --uc) **** # (Mainly for CDP Mode) - (For all CDP methods, see the CDP Mode Docs) self.activate_cdp_mode(url=None, **kwargs) # Activate CDP Mode on URL self.reconnect(timeout=0.1) # disconnect() + sleep(timeout) + connect() self.disconnect() # Stops the webdriver service to prevent detection self.connect() # Starts the webdriver service to allow actions again # (For regular UC Mode) self.uc_open(url) # (Open in same tab with default reconnect_time) self.uc_open_with_tab(url) # (New tab with default reconnect_time) self.uc_open_with_reconnect(url, reconnect_time=None) # (New tab) self.uc_open_with_disconnect(url, timeout=None) # New tab + sleep() self.uc_click(selector) # A stealthy click for evading bot-detection self.uc_gui_press_key(key) # Use PyAutoGUI to press the keyboard key self.uc_gui_press_keys(keys) # Use PyAutoGUI to press a list of keys self.uc_gui_write(text) # Similar to uc_gui_press_keys(), but faster self.uc_gui_click_x_y(x, y, timeframe=0.25) # PyAutoGUI click screen self.uc_gui_click_captcha(frame="iframe", retry=False, blind=False) self.uc_gui_handle_captcha(frame="iframe") ############ # "driver"-specific methods added (or modified) by SeleniumBase driver.default_get(url) # Because driver.get(url) works differently in UC Mode driver.open(url) # Like driver.get(), but allows partial URLs without protocol driver.click(selector) driver.click_link(link_text) driver.click_if_visible(selector) driver.click_active_element() driver.send_keys(selector, text) driver.press_keys(selector, text) driver.type(selector, text) driver.submit(selector) driver.assert_element(selector) driver.assert_element_present(selector) driver.assert_element_not_visible(selector) driver.assert_text(text, selector) driver.assert_exact_text(text, selector) driver.find_element(selector) driver.find_elements(selector) driver.wait_for_element(selector) driver.wait_for_element_visible(selector) driver.wait_for_element_present(selector) driver.wait_for_selector(selector) driver.wait_for_text(text, selector) driver.wait_for_exact_text(text, selector) driver.wait_for_and_accept_alert() driver.wait_for_and_dismiss_alert() driver.is_element_present(selector) driver.is_element_visible(selector) driver.is_text_visible(text, selector) driver.is_exact_text_visible(text, selector) driver.is_attribute_present(selector, attribute) driver.get_text(selector) driver.js_click(selector) driver.get_active_element_css() driver.get_locale_code() driver.get_origin() driver.get_user_agent() driver.highlight(selector) driver.highlight_click(selector) driver.highlight_if_visible(selector) driver.sleep(seconds) driver.locator(selector) driver.get_attribute(selector, attribute) driver.get_parent(element) driver.get_current_url() driver.get_page_source() driver.get_title() driver.switch_to_frame(frame="iframe") driver.is_cdp_mode_active() driver.is_connected() # UC / CDP Mode can disconnect WebDriver ############ # "driver"-specific methods added (or modified) by SeleniumBase for UC Mode: driver.get(url) # If UC Mode and site detects bots, then uc_open_with_tab(url) driver.uc_open(url) # (Open in same tab with default reconnect_time) driver.uc_open_with_tab(url) # (New tab with default reconnect_time) driver.uc_open_with_reconnect(url, reconnect_time=None) # (New tab) driver.uc_open_with_disconnect(url, timeout=None) # New tab + sleep() driver.uc_activate_cdp_mode(url=None, **kwargs) # Activate CDP Mode on URL driver.reconnect(timeout=0.1) # disconnect() + sleep(timeout) + connect() driver.disconnect() # Stops the webdriver service to prevent detection driver.connect() # Starts the webdriver service to allow actions again driver.uc_click(selector) # A stealthy click for evading bot-detection driver.uc_gui_press_key(key) # Use PyAutoGUI to press the keyboard key driver.uc_gui_press_keys(keys) # Use PyAutoGUI to press a list of keys driver.uc_gui_write(text) # Similar to uc_gui_press_keys(), but faster driver.uc_gui_click_x_y(x, y, timeframe=0.25) # PyAutoGUI click screen driver.uc_gui_click_captcha(frame="iframe", retry=False, blind=False) driver.uc_gui_handle_captcha(frame="iframe") ``` --------

      🔵 Examples

      ✅ Test Folder: [SeleniumBase/examples](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) * [my_first_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) * [test_demo_site.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py) * [test_coffee_cart.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_coffee_cart.py) * [coffee_cart_tests.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/coffee_cart_tests.py) * [parameterized_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/parameterized_test.py) * [test_deferred_asserts.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_deferred_asserts.py) * [test_error_page.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_error_page.py) * [test_login.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_login.py) * [test_markers.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_markers.py) * [test_swag_labs.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_swag_labs.py) * [test_simple_login.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_simple_login.py) * [test_suite.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_suite.py) * [test_tinymce.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_tinymce.py) * And many more... [](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md) ================================================ FILE: help_docs/mobile_testing.md ================================================

      Mobile Mode / Mobile Testing

      Use ``--mobile`` to run SeleniumBase tests using Chrome's mobile device emulator with default values for Device Metrics and User-Agent. Here's an example mobile test: [SeleniumBase/examples/test_roblox_mobile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_roblox_mobile.py) ```zsh pytest test_roblox_mobile.py --mobile ``` To configure Device Metrics, use: ```zsh --metrics="CSS_Width,CSS_Height,Pixel_Ratio" ``` To configure the User-Agent, use: ```zsh --agent="USER-AGENT-STRING" ``` To find real values for Device Metrics, see: * [Device Metrics List](https://gist.github.com/sidferreira/3f5fad525e99b395d8bd882ee0fd9d00) To find real User-Agent strings, see: * [User Agent Strings List](https://developers.whatismybrowser.com/useragents/explore/) -------- Here's another example of a mobile test: [SeleniumBase/examples/test_swag_labs.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_swag_labs.py) ```zsh pytest test_swag_labs.py --mobile ``` SeleniumBase Mobile Testing Here's an example of configuring mobile settings for that test: ```zsh # Run tests using Chrome's mobile device emulator (default settings) pytest test_swag_labs.py --mobile # Run mobile tests specifying CSS Width, CSS Height, and Pixel-Ratio pytest test_swag_labs.py --mobile --metrics="360,640,2" # Run mobile tests specifying the user agent pytest test_swag_labs.py --mobile --agent="Mozilla/5.0 (Linux; Android 9; Pixel 3 XL)" ``` -------- For some [SeleniumBase Syntax Formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md), you can also use `mobile=True` to run tests in Mobile Mode: ```python from seleniumbase import Driver driver = Driver(mobile=True) try: driver.open("https://www.roblox.com/") driver.assert_element("#download-the-app-container") driver.assert_text("Roblox for Android") driver.highlight('span:contains("Roblox for Android")', loops=8) driver.highlight('span:contains("Continue in App")', loops=8) finally: driver.quit() ``` --------

      ================================================ FILE: help_docs/mysql_installation.md ================================================ ## MySQL Installation Instructions ### [MySQL](http://www.mysql.com/) (OPTIONAL) (NOTE: If you don't plan on using the SeleniumBase MySQL DB feature, then you can skip this section.) -------- ### GitHub Actions Ubuntu Linux MySQL Setup: ```zsh sudo /etc/init.d/mysql start mysql -e 'CREATE DATABASE IF NOT EXISTS test_db;' -uroot -proot wget https://raw.githubusercontent.com/seleniumbase/SeleniumBase/master/seleniumbase/core/create_db_tables.sql sudo mysql -h 127.0.0.1 -uroot -proot test_db < create_db_tables.sql sudo mysql -e 'ALTER USER "root"@"localhost" IDENTIFIED BY "test";' -uroot -proot sudo service mysql restart ``` Have SeleniumBase tests write to the MySQL DB: ```zsh pytest --with-db_reporting ``` Query MySQL DB Results: ```zsh mysql -e 'select test_address,browser,state,start_time,runtime from test_db.test_run_data;' -uroot -ptest ``` -------- ### Standard Ubuntu Linux MySQL Setup: ```zsh sudo apt update sudo apt install mysql-server sudo mysql_secure_installation sudo mysql -e 'CREATE DATABASE IF NOT EXISTS test_db;' sudo mysql -h 127.0.0.1 -u root test_db < seleniumbase/core/create_db_tables.sql sudo service mysql restart ``` To change the password from `root` to `test`: ```zsh mysqladmin -u root -p'root' password 'test' sudo service mysql restart ``` ### MacOS MySQL Setup: ```zsh brew install mysql ``` Then start the MySQL service: ```zsh brew services start mysql ``` (Continue with additional steps below to set up your DB.) ### Windows MySQL Setup: [Download MySQL here](http://dev.mysql.com/downloads/windows/) Follow the steps from the MySQL Downloads page. (Continue with additional steps below to set up your DB.) ### Access your MySQL DB: If you want a visual tool to help make your MySQL life easier, [try MySQL Workbench](http://dev.mysql.com/downloads/workbench/). ### Prepare your MySQL DB: Use the [create_db_tables.sql](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/core/create_db_tables.sql) file to create the necessary tables for storing test data. ### Configure your MySQL DB for SeleniumBase: Update your [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) file with your MySQL DB credentials so that tests can write to the database when they run. ### Have SeleniumBase tests write to your MySQL DB: Add the ``--with-db_reporting`` argument on the command-line when you want tests to write to your MySQL database. Example: ```zsh pytest --with-db_reporting ``` ================================================ FILE: help_docs/recorder_mode.md ================================================

      Recorder Mode 🔴/⏺️

      (Watch the tutorial on YouTube)

      🔴 SeleniumBase Recorder Mode lets you record & export browser actions into test automation scripts.
      ⏺️ Recorder Mode can be activated from the command-line interface or the Recorder Desktop App. ⏺️ To make a new recording from the command-line interface, use ``sbase mkrec``, ``sbase codegen``, or ``sbase record``: ```zsh sbase mkrec TEST_NAME.py --url=URL ``` If the file already exists, you'll get an error. If no URL is provided, you'll start on a blank page and will need to navigate somewhere for the Recorder to activate. (The Recorder captures events on URLs that start with ``https``, ``http``, or ``file``.) The command above runs an empty test that stops at a breakpoint so that you can perform manual browser actions for the Recorder. When you have finished recording, type "``c``" on the command-line and press ``[ENTER]`` to continue from the breakpoint. The test will complete and a file called ``TEST_NAME_rec.py`` will be automatically created in the ``./recordings`` folder. That file will get copied back to the original folder with the name you gave it. (You can run with Edge instead of Chrome by adding ``--edge`` to the command above. For headed Linux machines, add ``--gui`` to prevent the default headless mode on Linux.) Example: ```zsh sbase mkrec new_test.py --url=wikipedia.org * RECORDING initialized: new_test.py pytest new_test.py --rec -q -s --url=wikipedia.org >>>>>>>>>>>>>>>>>> PDB set_trace >>>>>>>>>>>>>>>>> > PATH_TO_YOUR_CURRENT_DIRECTORY/new_test.py(9) . 5 def test_recording(self): 6 if self.recorder_ext: 7 # When done recording actions, 8 # type "c", and press [Enter]. 9 -> import pdb; pdb.set_trace() return None (Pdb+) c >>>>>>>>>>>>>>>>>> PDB continue >>>>>>>>>>>>>>>>>> >>> RECORDING SAVED as: recordings/new_test_rec.py ************************************************** *** RECORDING COPIED to: new_test.py ``` 🔴 You can also activate Recorder Mode from the Recorder Desktop App: ```zsh sbase recorder * Starting the SeleniumBase Recorder Desktop App... ``` ⏺️ While a recording is in progress, you can press the ``[ESC]`` key to pause the Recorder. To resume the recording, you can hit the ``[~`]`` key, which is located directly below the ``[ESC]`` key on most keyboards. ⏺️ From within Recorder Mode there are two additional modes: "Assert Element Mode" and "Assert Text Mode". To switch into "Assert Element Mode", press the ``[^]-key (SHIFT+6 on standard QWERTY keyboards)``: The border will become purple, and you'll be able to click on elements to assert from your test. To switch into "Assert Text Mode", press the ``[&]-key (SHIFT+7 on standard QWERTY keyboards)``: The border will become teal, and you'll be able to click on elements for asserting text from your test. ⏺️ While using either of the two special Assertion Modes, certain actions such as clicking on links won't have any effect. This lets you make assertions on elements without navigating away from the page, etc. To add an assertion for buttons without triggering default "click" behavior, mouse-down on the button and then mouse-up somewhere else. (This prevents a detected click while still recording the assert.) To return back to the original Recorder Mode, press any key other than ``[SHIFT]`` or ``[BACKSPACE]`` (Eg: Press ``[CONTROL]``, etc.). Press ``[ESC]`` once to leave the Assertion Modes, but it'll stop the Recorder if you press it again. ⏺️ For extra flexibility, the ``sbase mkrec`` command can be split into four separate commands: ```zsh sbase mkfile TEST_NAME.py --rec pytest TEST_NAME.py --rec -q -s sbase print ./recordings/TEST_NAME_rec.py -n cp ./recordings/TEST_NAME_rec.py ./TEST_NAME.py ``` The first command creates a boilerplate test with a breakpoint; the second command runs the test with the Recorder activated; the third command prints the completed test to the console; and the fourth command replaces the initial boilerplate with the completed test. If you're just experimenting with the Recorder, you can run the second command as many times as you want, and it'll override previous recordings saved to ``./recordings/TEST_NAME_rec.py``. (Note that ``-s`` is needed to allow breakpoints, unless you already have a ``pytest.ini`` file present where you set it. The ``-q`` is optional, which shortens ``pytest`` console output.) ⏺️ You can also use the Recorder to add code to an existing test. To do that, you'll first need to create a breakpoint in your code to insert manual browser actions: ```python import pdb; pdb.set_trace() ``` Now you'll be able to run your test with ``pytest``, and it will stop at the breakpoint for you to add in actions: (Press ``c`` and ``ENTER`` on the command-line to continue from the breakpoint.) ```zsh pytest TEST_NAME.py --rec -s ``` ⏺️ You can also set a breakpoint at the start of your test by adding ``--trace`` as a ``pytest`` command-line option: (This is useful when running Recorder Mode without any ``pdb`` breakpoints.) ```zsh pytest TEST_NAME.py --trace --rec -s ``` ⏺️ After the test completes, a file called ``TEST_NAME_rec.py`` will be automatically created in the ``./recordings`` folder, which will include the actions performed by the test, and the manual actions that you added in. ⏺️ Here's a command-line notification for a completed recording: ```zsh >>> RECORDING SAVED as: recordings/TEST_NAME_rec.py *************************************************** ``` ⏺️ When running additional tests from the same Python module, Recordings will get added to the file that was created from the first test: ```zsh >>> RECORDING ADDED to: recordings/TEST_NAME_rec.py *************************************************** ``` ⏺️ Recorder Mode works by saving your recorded actions into the browser's sessionStorage. SeleniumBase then reads from the browser's sessionStorage to take the raw data and generate a full test from it. Keep in mind that sessionStorage is only present while the browser tab remains in the same domain/origin. (The sessionStorage of that tab goes away if you leave that domain/origin.) To compensate, links to web pages of different domain/origin will automatically open a new tab for you in Recorder Mode. ⏺️ Additionally, the SeleniumBase self.open(URL) method will also open a new tab for you in Recorder Mode if the domain/origin is different from the current URL. If you need to navigate to a different domain/origin from within the same tab, call self.save_recorded_actions() first, which saves the recorded data for later. When a recorded test completes, SeleniumBase scans the sessionStorage data of all open browser tabs for generating the completed script. ⏺️ As an alternative to activating Recorder Mode with the --rec command-line arg, you can also call self.activate_recorder() from your tests. Using the Recorder this way is only useful for tests that stay on the same URL. This is because the standard Recorder Mode functions as a Chrome extension and persists wherever the browser goes. (This version only stays on the page where called.) ⏺️ (Note that same domain/origin is not the same as same URL. Example: https://xkcd.com/353 and https://xkcd.com/1537 are two different URLs with the same domain/origin. That means both URLs share the same sessionStorage, and that changes persist to different URLs of the same domain/origin. If you want to find out a website's origin during a test, just call: self.get_origin(), which returns the value of window.location.origin from the browser's console.) ⏺️ Inside recorded tests, you might find the self.open_if_not_url(URL) method, which opens the URL given if the browser is not currently on that page. SeleniumBase uses this method in recorded scripts when the Recorder detects that a browser action changed the current URL. This method prevents an unnecessary page load and shows what page the test visited after a browser action. ⏺️ By launching the Recorder App with sbase recorder --ee, you can end the recording by pressing {SHIFT+ESC} instead of the usual way of ending the recording by typing c from a breakpoint() and pressing Enter. Those buttons don't need to be pressed at the same time, but SHIFT must be pressed directly before ESC. ⏺️ Use sbase recorder --uc to launch the Recorder App with UC Mode enabled. (The driver will be disconnected from Chrome, but the Recorder extension will still capture any browser actions.) --------
      To learn more about SeleniumBase, check out the Docs Site:
      SeleniumBase.io Docs
      All the code is on GitHub:
      SeleniumBase on GitHub ================================================ FILE: help_docs/shadow_dom.md ================================================

      SeleniumBase

      ## Shadow DOM support / Shadow-root interaction 🔵 SeleniumBase lets you pierce through open Shadow DOM selectors (to interact with elements inside) by adding ``::shadow`` to CSS fragments that include a shadow-root element. For multi-layered shadow-roots, you must individually pierce through each shadow-root element that you want to get through. 🔵 Here are some examples of Shadow DOM selectors: ```python css_1 = "downloads-manager::shadow #no-downloads" css_2 = "downloads-manager::shadow #downloadsList downloads-item::shadow #file-link" css_3 = "downloads-manager::shadow downloads-toolbar::shadow cr-toolbar::shadow cr-toolbar-search-field::shadow cr-icon-button" css_4 = "downloads-manager::shadow downloads-toolbar::shadow cr-toolbar::shadow cr-toolbar-search-field::shadow #searchInput" css_5 = "downloads-manager::shadow downloads-toolbar::shadow cr-toolbar::shadow cr-toolbar-search-field::shadow #clearSearch" ``` 🔵 The shadow-root (``::shadow``) elements are transitional, and therefore cannot be the final part of your CSS selectors. Complete your CSS selectors by including an element that's inside a shadow-root. 🔵 NOTE: ``::shadow`` selectors only exist within SeleniumBase. (They are not part of standard CSS.) 🔵 Here are some examples of tests that interact with Shadow DOM elements: * [examples/shadow_root_test.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/shadow_root_test.py) * [examples/test_shadow_dom.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_shadow_dom.py) * [examples/old_wordle_script.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/old_wordle_script.py) ================================================ FILE: help_docs/syntax_formats.md ================================================

      The 25 Syntax Formats / Design Patterns

      🔠 SeleniumBase supports multiple ways of structuring tests:

      --------

      1. BaseCase direct class inheritance

      In this format, (which is used by most of the tests in the SeleniumBase examples folder), BaseCase is imported at the top of a Python file, followed by a Python class inheriting BaseCase. Then, any test method defined in that class automatically gains access to SeleniumBase methods, including the setUp() and tearDown() methods that are automatically called for opening and closing web browsers at the start and end of tests. To run a test of this format, use **``pytest``** or ``pynose``. Adding ``BaseCase.main(__name__, __file__)`` enables ``python`` to run ``pytest`` on your file indirectly. Here's an example: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_demo_site(self): self.open("https://seleniumbase.io/demo_page") self.type("#myTextInput", "This is Automated") self.click("#myButton") self.assert_element("tbody#tbodyId") self.assert_text("Automation Practice", "h3") self.click_link("SeleniumBase Demo Page") self.assert_exact_text("Demo Page", "h1") self.assert_no_js_errors() ``` (See examples/test_demo_site.py for the full test.) Using ``BaseCase`` inheritance is a great starting point for anyone learning SeleniumBase, and it follows good object-oriented programming principles.

      2. BaseCase subclass inheritance

      There are situations where you may want to customize the setUp and tearDown of your tests. Maybe you want to have all your tests login to a specific web site first, or maybe you want to have your tests report results through an API call depending on whether a test passed or failed. This can be done by creating a subclass of BaseCase and then carefully creating custom setUp() and tearDown() methods that don't overwrite the critical functionality of the default SeleniumBase setUp() and tearDown() methods. Afterwards, your test classes will inherit the subclass of BaseCase with the added functionality, rather than directly inheriting BaseCase itself. Here's an example of that: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class BaseTestCase(BaseCase): def setUp(self): super().setUp() # <<< Run custom setUp() code for tests AFTER the super().setUp() >>> def tearDown(self): self.save_teardown_screenshot() # On failure or "--screenshot" if self.has_exception(): # <<< Run custom code if the test failed. >>> pass else: # <<< Run custom code if the test passed. >>> pass # (Wrap unreliable tearDown() code in a try/except block.) # <<< Run custom tearDown() code BEFORE the super().tearDown() >>> super().tearDown() def login(self): # <<< Placeholder. Add your code here. >>> # Reduce duplicate code in tests by having reusable methods like this. # If the UI changes, the fix can be applied in one place. pass def example_method(self): # <<< Placeholder. Add your code here. >>> pass class MyTests(BaseTestCase): def test_example(self): self.login() self.example_method() self.type("input", "Name") self.click("form button") # ... ``` (See examples/boilerplates/base_test_case.py for more info.)

      3. The "sb" pytest fixture (no class)

      The pytest framework comes with a unique system called fixtures, which replaces import statements at the top of Python files by importing libraries directly into test definitions. More than just being an import, a pytest fixture can also automatically call predefined setUp() and tearDown() methods at the beginning and end of test methods. To work, sb is added as an argument to each test method definition that needs SeleniumBase functionality. This means you no longer need import statements in your Python files to use SeleniumBase. If using other pytest fixtures in your tests, you may need to use the SeleniumBase fixture (instead of BaseCase class inheritance) for compatibility reasons. Here's an example of the sb fixture in a test that does not use Python classes: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) def test_sb_fixture_with_no_class(sb: BaseCase): sb.open("seleniumbase.io/help_docs/install/") sb.type('input[aria-label="Search"]', "GUI Commander") sb.click('mark:contains("Commander")') sb.assert_title_contains("GUI / Commander") ``` (See the top of examples/test_sb_fixture.py for the test.)

      4. The "sb" pytest fixture (in class)

      The sb pytest fixture can also be used inside of a class. There is a slight change to the syntax because that means test methods must also include self in their argument definitions when test methods are defined. (The self argument represents the class object, and is used in every test method that lives inside of a class.) Once again, no import statements are needed in your Python files for this to work. Here's an example of using the sb fixture in a test method that lives inside of a Python class: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class Test_SB_Fixture: def test_sb_fixture_inside_class(self, sb: BaseCase): sb.open("seleniumbase.io/help_docs/install/") sb.type('input[aria-label="Search"]', "GUI Commander") sb.click('mark:contains("Commander")') sb.assert_title_contains("GUI / Commander") ``` (See the bottom of examples/test_sb_fixture.py for the test.)

      5. Page Object Model with BaseCase

      With SeleniumBase, you can use Page Objects to break out code from tests, but remember, the self variable (from test methods that inherit BaseCase) contains the driver and all other framework-specific variable definitions. Therefore, that self must be passed as an arg into any outside class method in order to call SeleniumBase methods from there. In the example below, the self variable from the test method is passed into the sb arg of the Page Object class method because the self arg of the Page Object class method is already being used for its own class. Every Python class method definition must include the self as the first arg. ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class LoginPage: def login_to_swag_labs(self, sb: BaseCase, username): sb.open("https://www.saucedemo.com") sb.type("#user-name", username) sb.type("#password", "secret_sauce") sb.click('input[type="submit"]') class MyTests(BaseCase): def test_swag_labs_login(self): LoginPage().login_to_swag_labs(self, "standard_user") self.assert_element("div.inventory_list") self.assert_element('div:contains("Sauce Labs Backpack")') ``` (See examples/boilerplates/samples/swag_labs_test.py for the full test.)

      6. Page Object Model with the "sb" fixture

      This is similar to the classic Page Object Model with BaseCase inheritance, except that this time we pass the sb pytest fixture from the test into the sb arg of the page object class method, (instead of passing self). Now that you're using sb as a pytest fixture, you no longer need to import BaseCase anywhere in your code. See the example below: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class LoginPage: def login_to_swag_labs(self, sb: BaseCase, username): sb.open("https://www.saucedemo.com") sb.type("#user-name", username) sb.type("#password", "secret_sauce") sb.click('input[type="submit"]') class MyTests: def test_swag_labs_login(self, sb: BaseCase): LoginPage().login_to_swag_labs(sb, "standard_user") sb.assert_element("div.inventory_list") sb.assert_element('div:contains("Sauce Labs Backpack")') sb.js_click("a#logout_sidebar_link") sb.assert_element("div#login_button_container") ``` (See examples/boilerplates/samples/sb_swag_test.py for the full test.)

      7. Using "request" to get "sb" (no class)

      The pytest request fixture can be used to retrieve other pytest fixtures from within tests, such as the sb fixture. This allows you to have more control over when fixtures get initialized because the fixture no longer needs to be loaded at the very beginning of test methods. This is done by calling request.getfixturevalue('sb') from the test. Here's an example of using the pytest request fixture to load the sb fixture in a test method that does not use Python classes: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) def test_request_sb_fixture(request): sb: BaseCase = request.getfixturevalue("sb") sb.open("https://seleniumbase.io/demo_page") sb.assert_text("SeleniumBase", "#myForm h2") sb.assert_element("input#myTextInput") sb.type("#myTextarea", "This is me") sb.click("#myButton") sb.tearDown() ``` (See the top of examples/test_request_sb_fixture.py for the test.)

      8. Using "request" to get "sb" (in class)

      The pytest request fixture can also be used to get the sb fixture from inside a Python class. Here's an example of that: ```python from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class Test_Request_Fixture: def test_request_sb_fixture_in_class(self, request): sb: BaseCase = request.getfixturevalue("sb") sb.open("https://seleniumbase.io/demo_page") sb.assert_element("input#myTextInput") sb.type("#myTextarea", "Automated") sb.assert_text("This Text is Green", "#pText") sb.click("#myButton") sb.assert_text("This Text is Purple", "#pText") sb.tearDown() ``` (See the bottom of examples/test_request_sb_fixture.py for the test.)

      9. Overriding the driver via BaseCase

      When you want to use SeleniumBase methods via BaseCase, but you want total freedom to control how you spin up your web browsers, this is the format you want. Although SeleniumBase gives you plenty of command-line options to change how your browsers are launched, this format gives you more control when the existing options aren't enough. Here's an example of that: ```python from selenium import webdriver from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class OverrideDriverTest(BaseCase): def get_new_driver(self, *args, **kwargs): """This method overrides get_new_driver() from BaseCase.""" options = webdriver.ChromeOptions() options.add_argument("--disable-3d-apis") options.add_argument("--disable-notifications") if self.headless: options.add_argument("--headless=new") options.add_argument("--disable-gpu") options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"], ) prefs = { "credentials_enable_service": False, "profile.password_manager_enabled": False, } options.add_experimental_option("prefs", prefs) return webdriver.Chrome(options=options) def test_simple(self): self.open("https://seleniumbase.io/demo_page") self.assert_text("Demo Page", "h1") ``` (From examples/test_override_driver.py) The above format lets you customize [selenium-wire](https://github.com/wkeeling/selenium-wire) for intercepting and inspecting requests and responses during SeleniumBase tests. Here's how a ``selenium-wire`` integration may look: ```python from seleniumbase import BaseCase from seleniumwire import webdriver # Requires "pip install selenium-wire" BaseCase.main(__name__, __file__) class WireTestCase(BaseCase): def get_new_driver(self, *args, **kwargs): options = webdriver.ChromeOptions() options.add_experimental_option( "excludeSwitches", ["enable-automation"] ) options.add_experimental_option("useAutomationExtension", False) return webdriver.Chrome(options=options) def test_simple(self): self.open("https://seleniumbase.io/demo_page") for request in self.driver.requests: print(request.url) ``` (NOTE: The ``selenium-wire`` integration is now included with ``seleniumbase``: Add ``--wire`` as a ``pytest`` command-line option to activate.)

      10. Overriding the driver via "sb" fixture

      When you want to use SeleniumBase methods via the ``sb`` pytest fixture, but you want total freedom to control how you spin up your web browsers, this is the format you want. Although SeleniumBase gives you plenty of command-line options to change how your browsers are launched, this format gives you more control when the existing options aren't enough. ```python """Overriding the "sb" fixture to override the driver.""" import pytest from seleniumbase import BaseCase BaseCase.main(__name__, __file__) @pytest.fixture() def sb(request): from selenium import webdriver from seleniumbase import BaseCase from seleniumbase import config as sb_config from seleniumbase.core import session_helper class BaseClass(BaseCase): def get_new_driver(self, *args, **kwargs): """This method overrides get_new_driver() from BaseCase.""" options = webdriver.ChromeOptions() if self.headless: options.add_argument("--headless=new") options.add_argument("--disable-gpu") options.add_experimental_option( "excludeSwitches", ["enable-automation"], ) return webdriver.Chrome(options=options) def setUp(self): super().setUp() def base_method(self): pass def tearDown(self): self.save_teardown_screenshot() # On failure or "--screenshot" super().tearDown() if request.cls: if sb_config.reuse_class_session: the_class = str(request.cls).split(".")[-1].split("'")[0] if the_class != sb_config._sb_class: session_helper.end_reused_class_session_as_needed() sb_config._sb_class = the_class request.cls.sb = BaseClass("base_method") request.cls.sb.setUp() request.cls.sb._needs_tearDown = True request.cls.sb._using_sb_fixture = True request.cls.sb._using_sb_fixture_class = True sb_config._sb_node[request.node.nodeid] = request.cls.sb yield request.cls.sb if request.cls.sb._needs_tearDown: request.cls.sb.tearDown() request.cls.sb._needs_tearDown = False else: sb = BaseClass("base_method") sb.setUp() sb._needs_tearDown = True sb._using_sb_fixture = True sb._using_sb_fixture_no_class = True sb_config._sb_node[request.node.nodeid] = sb yield sb if sb._needs_tearDown: sb.tearDown() sb._needs_tearDown = False def test_override_fixture_no_class(sb: BaseCase): sb.open("https://seleniumbase.io/demo_page") sb.type("#myTextInput", "This is Automated") class TestOverride: def test_override_fixture_inside_class(self, sb: BaseCase): sb.open("https://seleniumbase.io/demo_page") sb.type("#myTextInput", "This is Automated") ``` (From examples/test_override_sb_fixture.py) Here's how the [selenium-wire](https://github.com/wkeeling/selenium-wire) integration may look when overriding the ``sb`` pytest fixture to override the driver: ```python import pytest @pytest.fixture() def sb(request): import sys from seleniumbase import BaseCase from seleniumbase import config as sb_config from seleniumwire import webdriver # Requires "pip install selenium-wire" class BaseClass(BaseCase): def get_new_driver(self, *args, **kwargs): options = webdriver.ChromeOptions() if "linux" in sys.platform: options.add_argument("--headless=new") options.add_experimental_option( "excludeSwitches", ["enable-automation"], ) return webdriver.Chrome(options=options) def setUp(self): super().setUp() def tearDown(self): self.save_teardown_screenshot() # On failure or "--screenshot" super().tearDown() def base_method(self): pass if request.cls: request.cls.sb = BaseClass("base_method") request.cls.sb.setUp() request.cls.sb._needs_tearDown = True request.cls.sb._using_sb_fixture = True request.cls.sb._using_sb_fixture_class = True sb_config._sb_node[request.node.nodeid] = request.cls.sb yield request.cls.sb if request.cls.sb._needs_tearDown: request.cls.sb.tearDown() request.cls.sb._needs_tearDown = False else: sb = BaseClass("base_method") sb.setUp() sb._needs_tearDown = True sb._using_sb_fixture = True sb._using_sb_fixture_no_class = True sb_config._sb_node[request.node.nodeid] = sb yield sb if sb._needs_tearDown: sb.tearDown() sb._needs_tearDown = False def test_wire_with_no_class(sb): sb.open("https://seleniumbase.io/demo_page") for request in sb.driver.requests: print(request.url) class TestWire: def test_wire_inside_class(self, sb): sb.open("https://seleniumbase.io/demo_page") for request in sb.driver.requests: print(request.url) ``` (NOTE: The ``selenium-wire`` integration is now included with ``seleniumbase``: Add ``--wire`` as a ``pytest`` command-line option to activate. If you need both ``--wire`` with ``--undetected`` modes together, you'll still need to override ``get_new_driver()``.)

      11. BaseCase with Chinese translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Chinese. Here's an example of that: ```python from seleniumbase.translate.chinese import 硒测试用例 硒测试用例.main(__name__, __file__) class 我的测试类(硒测试用例): def test_例子1(self): self.开启("https://zh.wikipedia.org/wiki/") self.断言标题("维基百科,自由的百科全书") self.断言元素('a[title="Wikipedia:关于"]') self.如果可见请单击('button[aria-label="关闭"]') self.如果可见请单击('button[aria-label="關閉"]') self.断言元素('span:contains("创建账号")') self.断言元素('span:contains("登录")') self.输入文本('input[name="search"]', "舞龍") self.单击('button:contains("搜索")') self.断言文本("舞龍", "#firstHeading") self.断言元素('img[src*="Chinese_draak.jpg"]') self.回去() self.输入文本('input[name="search"]', "麻婆豆腐") self.单击('button:contains("搜索")') self.断言文本("麻婆豆腐", "#firstHeading") self.断言元素('figure:contains("一家中餐館的麻婆豆腐")') self.回去() self.输入文本('input[name="search"]', "精武英雄") self.单击('button:contains("搜索")') self.断言元素('img[src*="Fist_of_legend.jpg"]') self.断言文本("李连杰", 'li a[title="李连杰"]') ``` (See examples/translations/chinese_test_1.py for the Chinese test.)

      12. BaseCase with Dutch translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Dutch. Here's an example of that: ```python from seleniumbase.translate.dutch import Testgeval Testgeval.main(__name__, __file__) class MijnTestklasse(Testgeval): def test_voorbeeld_1(self): self.openen("https://nl.wikipedia.org/wiki/Hoofdpagina") self.controleren_element('a[title*="Welkom voor nieuwkomers"]') self.controleren_tekst("Welkom op Wikipedia", "td.hp-welkom") self.typ("#searchform input", "Stroopwafel") self.klik("#searchform button") self.controleren_tekst("Stroopwafel", "#firstHeading") self.controleren_element('img[src*="Stroopwafels"]') self.typ("#searchform input", "Rijksmuseum Amsterdam") self.klik("#searchform button") self.controleren_tekst("Rijksmuseum", "#firstHeading") self.controleren_element('img[src*="Rijksmuseum"]') self.terug() self.controleren_url_bevat("Stroopwafel") self.vooruit() self.controleren_url_bevat("Rijksmuseum") ``` (See examples/translations/dutch_test_1.py for the Dutch test.)

      13. BaseCase with French translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into French. Here's an example of that: ```python from seleniumbase.translate.french import CasDeBase CasDeBase.main(__name__, __file__) class MaClasseDeTest(CasDeBase): def test_exemple_1(self): self.ouvrir("https://fr.wikipedia.org/wiki/") self.vérifier_texte("Wikipédia") self.vérifier_élément('[alt="Wikipédia"]') self.cliquer_si_affiché('button[aria-label="Fermer"]') self.js_taper("#searchform input", "Crème brûlée") self.cliquer("#searchform button") self.vérifier_texte("Crème brûlée", "#firstHeading") self.vérifier_élément('img[alt*="Crème brûlée"]') self.js_taper("#searchform input", "Jardin des Tuileries") self.cliquer("#searchform button") self.vérifier_texte("Jardin des Tuileries", "#firstHeading") self.vérifier_élément('img[alt*="Jardin des Tuileries"]') self.retour() self.vérifier_url_contient("brûlée") self.en_avant() self.vérifier_url_contient("Jardin") ``` (See examples/translations/french_test_1.py for the French test.)

      14. BaseCase with Italian translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Italian. Here's an example of that: ```python from seleniumbase.translate.italian import CasoDiProva CasoDiProva.main(__name__, __file__) class MiaClasseDiTest(CasoDiProva): def test_esempio_1(self): self.apri("https://it.wikipedia.org/wiki/") self.verificare_testo("Wikipedia") self.verificare_elemento('a[title="Lingua italiana"]') self.digitare('input[name="search"]', "Pizza") self.fare_clic("#searchform button") self.verificare_testo("Pizza", "#firstHeading") self.verificare_elemento('figure img[src*="pizza"]') self.digitare('input[name="search"]', "Colosseo") self.fare_clic("#searchform button") self.verificare_testo("Colosseo", "#firstHeading") self.verificare_elemento('figure img[src*="Colosseo"]') self.indietro() self.verificare_url_contiene("Pizza") self.avanti() self.verificare_url_contiene("Colosseo") ``` (See examples/translations/italian_test_1.py for the Italian test.)

      15. BaseCase with Japanese translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Japanese. Here's an example of that: ```python from seleniumbase.translate.japanese import セレニウムテストケース セレニウムテストケース.main(__name__, __file__) class 私のテストクラス(セレニウムテストケース): def test_例1(self): self.を開く("https://ja.wikipedia.org/wiki/") self.テキストを確認する("ウィキペディア") self.要素を確認する('[title*="ウィキペディアへようこそ"]') self.JS入力('input[name="search"]', "アニメ") self.クリックして("#searchform button") self.テキストを確認する("アニメ", "#firstHeading") self.JS入力('input[name="search"]', "寿司") self.クリックして("#searchform button") self.テキストを確認する("寿司", "#firstHeading") self.要素を確認する('img[src*="Various_sushi"]') self.JS入力("#searchInput", "レゴランド・ジャパン") self.クリックして("#searchform button") self.要素を確認する('img[src*="LEGOLAND_JAPAN"]') self.リンクテキストを確認する("名古屋城") self.リンクテキストをクリックします("テーマパーク") self.テキストを確認する("テーマパーク", "#firstHeading") ``` (See examples/translations/japanese_test_1.py for the Japanese test.)

      16. BaseCase with Korean translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Korean. Here's an example of that: ```python from seleniumbase.translate.korean import 셀레늄_테스트_케이스 셀레늄_테스트_케이스.main(__name__, __file__) class 테스트_클래스(셀레늄_테스트_케이스): def test_실시예_1(self): self.열기("https://ko.wikipedia.org/wiki/") self.텍스트_확인("위키백과") self.요소_확인('[title="위키백과:소개"]') self.JS_입력("#searchform input", "김치") self.클릭("#searchform button") self.텍스트_확인("김치", "#firstHeading") self.요소_확인('img[src*="Various_kimchi.jpg"]') self.링크_텍스트_확인("한국 요리") self.JS_입력("#searchform input", "비빔밥") self.클릭("#searchform button") self.텍스트_확인("비빔밥", "#firstHeading") self.요소_확인('img[src*="Dolsot-bibimbap.jpg"]') self.링크_텍스트를_클릭합니다("돌솥비빔밥") self.텍스트_확인("돌솥비빔밥", "#firstHeading") ``` (See examples/translations/korean_test_1.py for the Korean test.)

      17. BaseCase with Portuguese translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Portuguese. Here's an example of that: ```python from seleniumbase.translate.portuguese import CasoDeTeste CasoDeTeste.main(__name__, __file__) class MinhaClasseDeTeste(CasoDeTeste): def test_exemplo_1(self): self.abrir("https://pt.wikipedia.org/wiki/") self.verificar_texto("Wikipédia") self.verificar_elemento('[title="Língua portuguesa"]') self.digitar("#searchform input", "João Pessoa") self.clique("#searchform button") self.verificar_texto("João Pessoa", "#firstHeading") self.verificar_elemento('img[alt*="João Pessoa"]') self.digitar("#searchform input", "Florianópolis") self.clique("#searchform button") self.verificar_texto("Florianópolis", "h1#firstHeading") self.verificar_elemento('td:contains("Avenida Beira-Mar")') self.voltar() self.verificar_url_contém("João_Pessoa") self.atualizar_a_página() self.js_digitar("#searchform input", "Teatro Amazonas") self.clique("#searchform button") self.verificar_texto("Teatro Amazonas", "#firstHeading") self.verificar_texto_do_link("Festival Amazonas de Ópera") ``` (See examples/translations/portuguese_test_1.py for the Portuguese test.)

      18. BaseCase with Russian translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Russian. Here's an example of that: ```python from seleniumbase.translate.russian import ТестНаСелен ТестНаСелен.main(__name__, __file__) class МойТестовыйКласс(ТестНаСелен): def test_пример_1(self): self.открыть("https://ru.wikipedia.org/wiki/") self.подтвердить_элемент('[title="Русский язык"]') self.подтвердить_текст("Википедия", "div.main-wikimedia-header") self.введите("#searchInput", "МГУ") self.нажмите("#searchButton") self.подтвердить_текст("университет", "#firstHeading") self.подтвердить_элемент('img[alt*="Главное здание МГУ"]') self.введите("#searchInput", "приключения Шурика") self.нажмите("#searchButton") self.подтвердить_текст("Операция «Ы» и другие приключения Шурика") self.подтвердить_элемент('img[alt="Постер фильма"]') self.назад() self.подтвердить_URL_содержит("университет") self.вперед() self.подтвердить_URL_содержит("Шурика") ``` (See examples/translations/russian_test_1.py for the Russian test.)

      19. BaseCase with Spanish translations

      This format is similar to the English version with BaseCase inheritance, but there's a different import statement, and method names have been translated into Spanish. Here's an example of that: ```python from seleniumbase.translate.spanish import CasoDePrueba CasoDePrueba.main(__name__, __file__) class MiClaseDePrueba(CasoDePrueba): def test_ejemplo_1(self): self.abrir("https://es.wikipedia.org/wiki/") self.verificar_texto("Wikipedia") self.verificar_elemento('[title="Wikipedia:Bienvenidos"]') self.escriba('[name="search"]', "Parque de Atracciones Tibidabo") self.haga_clic('button:contains("Buscar")') self.verificar_texto("Tibidabo", "#firstHeading") self.verificar_elemento('img[src*="Tibidabo"]') self.escriba('input[name="search"]', "Palma de Mallorca") self.haga_clic('button:contains("Buscar")') self.verificar_texto("Palma de Mallorca", "#firstHeading") self.verificar_elemento('img[src*="Palma"]') self.volver() self.verificar_url_contiene("Tibidabo") self.adelante() self.verificar_url_contiene("Mallorca") ``` (See examples/translations/spanish_test_1.py for the Spanish test.)

      20. Gherkin syntax with "behave" BDD runner

      With [Behave's BDD Gherkin format](https://behave.readthedocs.io/en/stable/gherkin.html), you can use natural language to write tests that work with SeleniumBase methods. Behave tests are run by calling ``behave`` on the command-line. This requires some special files in a specific directory structure. Here's an example of that structure: ```zsh features/ ├── __init__.py ├── behave.ini ├── environment.py ├── feature_file.feature └── steps/ ├── __init__.py ├── imported.py └── step_file.py ``` A ``*.feature`` file might look like this: ```gherkin Feature: SeleniumBase scenarios for the RealWorld App Scenario: Verify RealWorld App (log in / sign out) Given Open "seleniumbase.io/realworld/login" And Clear Session Storage When Type "demo_user" into "#username" And Type "secret_pass" into "#password" And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" Then Assert exact text "Welcome!" in "h1" And Highlight "img#image1" And Click 'a:contains("This Page")' And Save screenshot to logs When Click link "Sign out" Then Assert element 'a:contains("Sign in")' And Assert text "You have been signed out!" ``` (From examples/behave_bdd/features/realworld.feature) You'll need the ``environment.py`` file for tests to work. Here it is: ```python from seleniumbase import BaseCase from seleniumbase.behave import behave_sb behave_sb.set_base_class(BaseCase) # Accepts a BaseCase subclass from seleniumbase.behave.behave_sb import before_all # noqa from seleniumbase.behave.behave_sb import before_feature # noqa from seleniumbase.behave.behave_sb import before_scenario # noqa from seleniumbase.behave.behave_sb import before_step # noqa from seleniumbase.behave.behave_sb import after_step # noqa from seleniumbase.behave.behave_sb import after_scenario # noqa from seleniumbase.behave.behave_sb import after_feature # noqa from seleniumbase.behave.behave_sb import after_all # noqa ``` (From examples/behave_bdd/features/environment.py) Inside that file, you can use ``BaseCase`` (or a subclass) for the inherited class. For your ``behave`` tests to have access to SeleniumBase Behave steps, you can create an ``imported.py`` file with the following line: ```python from seleniumbase.behave import steps # noqa ``` That will allow you to use lines like this in your ``*.feature`` files: ```gherkin Feature: SeleniumBase scenarios for the RealWorld App Scenario: Verify RealWorld App (log in / sign out) Given Open "seleniumbase.io/realworld/login" And Clear Session Storage When Type "demo_user" into "#username" And Type "secret_pass" into "#password" And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" Then Assert exact text "Welcome!" in "h1" And Highlight "img#image1" And Click 'a:contains("This Page")' And Save screenshot to logs ``` You can also create your own step files (Eg. ``step_file.py``): ```python from behave import step @step("Open the Swag Labs Login Page") def go_to_swag_labs(context): sb = context.sb sb.open("https://www.saucedemo.com") sb.clear_local_storage() @step("Login to Swag Labs with {user}") def login_to_swag_labs(context, user): sb = context.sb sb.type("#user-name", user) sb.type("#password", "secret_sauce\n") ``` (For more information, see the SeleniumBase Behave BDD ReadMe.)

      21. SeleniumBase SB (Python context manager)

      This format provides a pure Python way of using SeleniumBase without a test runner. Options can be passed via method instantiation or from the command-line. When setting the test option to True (or calling python --test), then standard test logging will occur, such as screenshots and reports for failing tests. All the usual SeleniumBase options are available, such as customizing the browser settings, etc. Here are some examples: ```python from seleniumbase import SB with SB() as sb: sb.open("seleniumbase.io/simple/login") sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") ``` (See examples/raw_login_sb.py for the test.) Here's another example, which uses test mode: ```python from seleniumbase import SB with SB(uc=True, test=True) as sb: sb.open("https://google.com/ncr") sb.type('[name="q"]', "SeleniumBase on GitHub\n") sb.highlight('a[href*="github.com/seleniumbase"]') sb.sleep(0.5) with SB(test=True, rtf=True, demo=True) as sb: sb.open("seleniumbase.github.io/demo_page") sb.type("#myTextInput", "This is Automated") sb.assert_text("This is Automated", "#myTextInput") sb.assert_text("This Text is Green", "#pText") sb.click('button:contains("Click Me")') sb.assert_text("This Text is Purple", "#pText") sb.click("#checkBox1") sb.assert_element_not_visible("div#drop2 img#logo") sb.drag_and_drop("img#logo", "div#drop2") sb.assert_element("div#drop2 img#logo") ``` (See examples/raw_test_scripts.py for the test.) Here's another example, which uses [CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md) from the SeleniumBase SB format: ```python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "www.planetminecraft.com/account/sign_in/" sb.activate_cdp_mode(url) sb.sleep(2) sb.solve_captcha() sb.wait_for_element_absent("input[disabled]") sb.sleep(2) ``` (See examples/cdp_mode/raw_planetmc.py for the test.)

      22. The driver manager (via context manager)

      This pure Python format gives you a raw webdriver instance in a with block. The SeleniumBase Driver Manager will automatically make sure that your driver is compatible with your browser version. It gives you full access to customize driver options via method args or via the command-line. The driver will automatically call quit() after the code leaves the with block. Here are some examples: ```python """DriverContext() example. (Runs with "python").""" from seleniumbase import DriverContext with DriverContext() as driver: driver.open("seleniumbase.io/") driver.highlight('img[alt="SeleniumBase"]', loops=6) with DriverContext(browser="chrome", incognito=True) as driver: driver.open("seleniumbase.io/apps/calculator") driver.click('[id="4"]') driver.click('[id="2"]') driver.assert_text("42", "#output") driver.highlight("#output", loops=6) with DriverContext() as driver: driver.open("seleniumbase.io/demo_page") driver.highlight("h2") driver.type("#myTextInput", "Automation") driver.click("#checkBox1") driver.highlight("img", loops=6) ``` (See examples/raw_driver_context.py for an example.)

      23. The driver manager (via direct import)

      Another way of running Selenium tests with pure ``python`` (as opposed to using ``pytest`` or ``pynose``) is by using this format, which bypasses [BaseCase](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py) methods while still giving you a flexible driver with a manager. SeleniumBase includes helper files such as [page_actions.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/page_actions.py), which may help you get around some of the limitations of bypassing ``BaseCase``. Here's an example: ```python """Driver() example. (Runs with "python").""" from seleniumbase import Driver driver = Driver() try: driver.open("seleniumbase.io/demo_page") driver.highlight("h2") driver.type("#myTextInput", "Automation") driver.click("#checkBox1") driver.highlight("img", loops=6) finally: driver.quit() driver = Driver(browser="chrome", headless=False) try: driver.open("seleniumbase.io/apps/calculator") driver.click('[id="4"]') driver.click('[id="2"]') driver.assert_text("42", "#output") driver.highlight("#output", loops=6) finally: driver.quit() ``` (From examples/raw_driver_manager.py) Here's how the [selenium-wire](https://github.com/wkeeling/selenium-wire) integration may look when using the ``Driver()`` format: ```python from seleniumbase import Driver driver = Driver(wire=True, headless=True) try: driver.get("https://wikipedia.org") for request in driver.requests: print(request.url) finally: driver.quit() ``` Here's another `selenium-wire` example with the `Driver()` format: ```python from seleniumbase import Driver def intercept_response(request, response): print(request.headers) driver = Driver(wire=True) try: driver.response_interceptor = intercept_response driver.get("https://wikipedia.org") finally: driver.quit() ``` Here's an example of basic login with the ``Driver()`` format: ```python from seleniumbase import Driver driver = Driver() try: driver.open("seleniumbase.io/simple/login") driver.type("#username", "demo_user") driver.type("#password", "secret_pass") driver.click('a:contains("Sign in")') driver.assert_exact_text("Welcome!", "h1") driver.assert_element("img#image1") driver.highlight("#image1") driver.click_link("Sign out") driver.assert_text("signed out", "#top_message") finally: driver.quit() ``` (From examples/raw_login_driver.py) The ``Driver()`` manager format can be used as a drop-in replacement for virtually every Python/selenium framework, as it uses the raw ``driver`` instance for handling commands. The ``Driver()`` method simplifies the work of managing drivers with optimal settings, and it can be configured with multiple args. The ``Driver()`` also accepts command-line options (such as ``python --headless``) so that you don't need to modify your tests directly to use different settings. These command-line options only take effect if the associated method args remain unset (or set to ``None``) for the specified options. When using the ``Driver()`` format, you may need to activate a Virtual Display on your own if you want to run headed tests in a headless Linux environment. (See https://github.com/mdmintz/sbVirtualDisplay for details.) One such example of this is using an authenticated proxy, which is configured via a Chrome extension that is generated at runtime. (Note that regular headless mode in Chrome doesn't support extensions.)

      24. Pure CDP Mode (Async API. No Selenium)

      This format provides a pure CDP way of using SeleniumBase (without Selenium/WebDriver or a test runner). The async/await API is used. Here's an example: ```python import asyncio from seleniumbase import cdp_driver async def main(): url = "https://seleniumbase.io/simple/login" driver = await cdp_driver.start_async() page = await driver.get(url, lang="en") print(await page.get_title()) await page.type("#username", "demo_user") await page.type("#password", "secret_pass") await page.click("#log-in") print(await page.get_title()) element = await page.select("h1") assert element.text == "Welcome!" top_nav = await page.select("div.topnav") links = await top_nav.query_selector_all_async("a") for nav_item in links: print(nav_item.text) driver.stop() if __name__ == "__main__": loop = asyncio.new_event_loop() loop.run_until_complete(main()) ``` (See examples/cdp_mode/raw_basic_async.py for the test.)

      25. Pure CDP Mode (Sync API. No Selenium)

      This format provides a pure CDP way of using SeleniumBase (without Selenium/WebDriver or a test runner). The expanded sb_cdp Sync API is used. Here's an example: ```python from seleniumbase import sb_cdp url = "https://seleniumbase.io/simple/login" sb = sb_cdp.Chrome(url) sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.click('a:contains("Sign in")') sb.assert_exact_text("Welcome!", "h1") sb.assert_element("img#image1") sb.highlight("#image1") top_nav = sb.find_element("div.topnav") links = top_nav.query_selector_all("a") for nav_item in links: print(nav_item.text) sb.click_link("Sign out") sb.assert_text("signed out", "#top_message") sb.driver.stop() ``` (See examples/cdp_mode/raw_basic_cdp.py for the test.) Here's a Pure CDP Mode example that bypasses bot-detection to scrape data from a website: ```python from seleniumbase import sb_cdp def main(): url = "https://www.priceline.com/" sb = sb_cdp.Chrome(url, lang="en") sb.sleep(2) sb.internalize_links() # Don't open links in a new tab sb.click("#link_header_nav_experiences") sb.sleep(3) sb.remove_elements("msm-cookie-banner") sb.sleep(1) location = "Amsterdam" where_to = 'div[data-automation*="experiences"] input' button = 'button[data-automation*="experiences-search"]' sb.wait_for_text("Where to?") sb.click(where_to) sb.press_keys(where_to, location) sb.sleep(1) sb.click(button) sb.sleep(2) sb.click_if_visible('button[aria-label="Close"]') sb.sleep(1) print(sb.get_title()) print("************") for i in range(8): sb.scroll_down(50) sb.sleep(0.2) cards = sb.select_all('span[data-automation*="product-list-card"]') for card in cards: print("* %s" % card.text) sb.driver.stop() if __name__ == "__main__": main() ``` (See examples/cdp_mode/raw_cdp.py for the test.) --------

      ================================================ FILE: help_docs/thank_you.md ================================================

      🛬🛫 Thank you for flying with SeleniumBase! 🦅🚀

      --------

      🛩️ SeleniumBase Playlist on YouTube:

      SeleniumBase Playlist on YouTube

      🛩️ SeleniumBase GitHub Home Page:

      SeleniumBase on GitHub

      🛩️ SeleniumBase Discord Channel:

      SeleniumBase on Discord

      🛩️ SeleniumBase Gitter Chat:

      SeleniumBase on Gitter

      Other Social Media Links:

      SeleniumBase on Facebook SeleniumBase on Twitter SeleniumBase on Instagram

      --------

      SeleniumBase

      ================================================ FILE: help_docs/translations.md ================================================

      🌏 Translated Tests 🈺

      SeleniumBase supports the following 10 languages: English, Chinese, Dutch, French, Italian, Japanese, Korean, Portuguese, Russian, and Spanish. (Examples can be found in SeleniumBase/examples/translations) Multi-language tests run with **pytest** like other tests. Test methods have a one-to-one mapping to supported languages. Here's an example of a translated test: ```python # Chinese Translation from seleniumbase.translate.chinese import 硒测试用例 class 我的测试类(硒测试用例): def test_例子1(self): self.开启("https://zh.wikipedia.org/wiki/") self.断言标题("维基百科,自由的百科全书") self.断言元素('a[title="Wikipedia:关于"]') self.如果可见请单击('button[aria-label="关闭"]') self.如果可见请单击('button[aria-label="關閉"]') self.断言元素('span:contains("创建账号")') self.断言元素('span:contains("登录")') self.输入文本('input[name="search"]', "舞龍") self.单击('button:contains("搜索")') self.断言文本("舞龍", "#firstHeading") self.断言元素('img[src*="Chinese_draak.jpg"]') ``` Here's another example: ```python # Japanese Translation from seleniumbase.translate.japanese import セレニウムテストケース class 私のテストクラス(セレニウムテストケース): def test_例1(self): self.を開く("https://ja.wikipedia.org/wiki/") self.テキストを確認する("ウィキペディア") self.要素を確認する('[title*="ウィキペディアへようこそ"]') self.JS入力('input[name="search"]', "アニメ") self.クリックして("#searchform button") self.テキストを確認する("アニメ", "#firstHeading") self.JS入力('input[name="search"]', "寿司") self.クリックして("#searchform button") self.テキストを確認する("寿司", "#firstHeading") self.要素を確認する('img[src*="Various_sushi"]') ```

      Translation API 🈺

      You can use SeleniumBase to selectively translate the method names of any test from one language to another with the console scripts interface. Additionally, the ``import`` line at the top of the Python file will change to import the new ``BaseCase``. Example: ``BaseCase`` becomes ``CasoDeTeste`` when a test is translated into Portuguese. ```zsh seleniumbase translate ``` ```zsh * Usage: seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION] * Languages: ``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` ``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` ``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` ``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` ``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` * Actions: ``-p`` / ``--print`` (Print translation output to the screen) ``-o`` / ``--overwrite`` (Overwrite the file being translated) ``-c`` / ``--copy`` (Copy the translation to a new ``.py`` file) * Options: ``-n`` (include line Numbers when using the Print action) * Examples: Translate test_1.py into Chinese and only print the output: >>> seleniumbase translate test_1.py --zh -p Translate test_2.py into Portuguese and overwrite the file: >>> seleniumbase translate test_2.py --pt -o Translate test_3.py into Dutch and make a copy of the file: >>> seleniumbase translate test_3.py --nl -c * Output: Translates a SeleniumBase Python file into the language specified. Method calls and ``import`` lines get swapped. Both a language and an action must be specified. The ``-p`` action can be paired with one other action. When running with ``-c`` (or ``--copy``) the new file name will be the original name appended with an underscore plus the 2-letter language code of the new language. (Example: Translating ``test_1.py`` into Japanese with ``-c`` will create a new file called ``test_1_ja.py``.) ``` --------

      ================================================ FILE: help_docs/uc_mode.md ================================================

      UC Mode 👤

      👤 SeleniumBase UC Mode (Undetected-Chromedriver Mode) allows bots to appear human, which lets them evade detection from anti-bot services that try to block them or trigger CAPTCHAs on various websites. > ### (For the successor to plain UC Mode, see **[CDP Mode 🐙](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md)**) ---

      (Watch the 1st UC Mode tutorial on YouTube! ▶️)

      ----

      (Watch the 2nd UC Mode tutorial on YouTube! ▶️)

      ----

      (Watch the 3rd UC Mode tutorial on YouTube! ▶️)

      ----

      (Watch the 4th UC Mode tutorial on YouTube! ▶️)

      ---- 👤 UC Mode is based on [undetected-chromedriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver). UC Mode includes multiple updates, fixes, and improvements, such as having special uc_*() methods for bypassing CAPTCHAs. 👤 Here's a simple example with the Driver manager: ```python from seleniumbase import Driver driver = Driver(uc=True) url = "https://gitlab.com/users/sign_in" driver.uc_open_with_reconnect(url, 4) driver.uc_gui_click_captcha() driver.quit() ``` 👤 Here's an example with the SB manager (which has more methods and functionality than the Driver format): ```python from seleniumbase import SB with SB(uc=True) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.uc_gui_click_captcha() ``` (Note: If running UC Mode scripts on headless Linux machines, then you'll need to use the SB manager instead of the Driver manager because the SB manager includes a special virtual display that allows for PyAutoGUI actions.) 👤 Here's a longer example: (Note that sb.uc_gui_click_captcha() performs a special click using PyAutoGUI if a CAPTCHA is detected.) ```python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://gitlab.com/users/sign_in" sb.uc_open_with_reconnect(url, 4) sb.uc_gui_click_captcha() sb.assert_text("Username", '[for="user_login"]', timeout=3) sb.assert_element('label[for="user_login"]') sb.highlight('button:contains("Sign in")') sb.highlight('h1:contains("GitLab")') sb.post_message("SeleniumBase wasn't detected", duration=4) ``` 👤 Here's an example where clicking the checkbox is required, even for humans:
      (Commonly seen on forms that are CAPTCHA-protected.) ```python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://seleniumbase.io/apps/turnstile" sb.uc_open_with_reconnect(url, reconnect_time=2) sb.uc_gui_handle_captcha() sb.assert_element("img#captcha-success", timeout=3) sb.set_messenger_theme(location="top_left") sb.post_message("SeleniumBase wasn't detected", duration=3) ``` If running on a Linux server, `uc_gui_handle_captcha()` might not be good enough. Switch to `uc_gui_click_captcha()` to be more stealthy. Note that these methods auto-detect between CF Turnstile and Google reCAPTCHA. Sometimes you need to add incognito=True with uc=True to maximize your anti-detection abilities. (Some websites can detect you if you don't do that.) 👤 Here's an example where the CAPTCHA appears after submitting a form: ```python from seleniumbase import SB with SB(uc=True, test=True, incognito=True, locale="en") as sb: url = "https://ahrefs.com/website-authority-checker" input_field = 'input[placeholder="Enter domain"]' submit_button = 'span:contains("Check Authority")' sb.uc_open_with_reconnect(url) # The bot-check is later sb.type(input_field, "github.com/seleniumbase/SeleniumBase") sb.uc_click(submit_button, reconnect_time=3.25) sb.uc_gui_click_captcha() sb.wait_for_text_not_visible("Checking", timeout=15) sb.click_if_visible('button[data-cky-tag="close-button"]') sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")') sb.highlight('a:contains("Top 100 backlinks")') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") ``` -------- 👤 On Linux, use `sb.uc_gui_click_captcha()` to handle CAPTCHAs (Cloudflare Turnstiles): ```python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://www.virtualmanager.com/en/login" sb.uc_open_with_reconnect(url, 4) print(sb.get_page_title()) sb.uc_gui_click_captcha() # Only used if needed print(sb.get_page_title()) sb.assert_element('input[name*="email"]') sb.assert_element('input[name*="login"]') sb.set_messenger_theme(location="bottom_center") sb.post_message("SeleniumBase wasn't detected!") ``` uc_gui_click_captcha on Linux The 2nd print() should output Virtual Manager, which means that the automation successfully passed the Turnstile. (Note: UC Mode is detectable in Headless Mode, so don't combine those options. Instead, use xvfb=True / `--xvfb`on Linux for the special virtual display, which is enabled by default when not changing headed/headless settings.) -------- 👤 In UC Mode, driver.get(url) has been modified from its original version: If anti-bot services are detected from a requests.get(url) call that's made before navigating to the website, then driver.uc_open_with_reconnect(url) will be used instead. To open a URL normally in UC Mode, use driver.default_get(url). -------- ### 👤 Here are some examples that use UC Mode: * [SeleniumBase/examples/verify_undetected.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/verify_undetected.py) * [SeleniumBase/examples/raw_turnstile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_turnstile.py) * [SeleniumBase/examples/raw_form_turnstile.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_form_turnstile.py) * [SeleniumBase/examples/raw_uc_mode.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_uc_mode.py) -------- 👤 Here's an example where incognito=True is needed for bypassing detection: ```python from seleniumbase import SB with SB(uc=True, incognito=True, test=True) as sb: sb.driver.uc_open_with_reconnect("https://pixelscan.net/", 10) sb.sleep(2) ``` -------- ### 👤 Here are the SeleniumBase UC Mode methods: (**`--uc`** / **`uc=True`**) ```python driver.uc_open(url) driver.uc_open_with_tab(url) driver.uc_open_with_reconnect(url, reconnect_time=None) driver.uc_open_with_disconnect(url, timeout=None) driver.reconnect(timeout) driver.disconnect() driver.connect() driver.uc_click( selector, by="css selector", timeout=settings.SMALL_TIMEOUT, reconnect_time=None) driver.uc_gui_press_key(key) driver.uc_gui_press_keys(keys) driver.uc_gui_write(text) driver.uc_gui_click_x_y(x, y, timeframe=0.25) driver.uc_gui_click_captcha(frame="iframe", retry=False, blind=False) # driver.uc_gui_click_cf(frame="iframe", retry=False, blind=False) # driver.uc_gui_click_rc(frame="iframe", retry=False, blind=False) driver.uc_gui_handle_captcha(frame="iframe") # driver.uc_gui_handle_cf(frame="iframe") # driver.uc_gui_handle_rc(frame="iframe") ``` (Note that the reconnect_time is used to specify how long the driver should be disconnected from Chrome to prevent detection before reconnecting again.) 👤 Since driver.get(url) is slower in UC Mode for bypassing detection, use driver.default_get(url) for a standard page load instead: ```python driver.default_get(url) # Faster, but Selenium can be detected ``` 👤 Here are some examples of using those special UC Mode methods: (Use self.driver for BaseCase formats. Use sb.driver for SB() formats): ```python url = "https://gitlab.com/users/sign_in" driver.uc_open_with_reconnect(url, reconnect_time=3) driver.uc_open_with_reconnect(url, 3) driver.reconnect(5) driver.reconnect(timeout=5) ``` 👤 You can also set the reconnect_time / timeout to "breakpoint" as a valid option. This allows the user to perform manual actions (until typing c and pressing ENTER to continue from the breakpoint): ```python url = "https://gitlab.com/users/sign_in" driver.uc_open_with_reconnect(url, reconnect_time="breakpoint") driver.uc_open_with_reconnect(url, "breakpoint") driver.reconnect(timeout="breakpoint") driver.reconnect("breakpoint") ``` (Note that while the special UC Mode breakpoint is active, you can't use Selenium commands in the browser, and the browser can't detect Selenium.) -------- 👤 On Linux, use xvfb=True / `--xvfb` to activate a special virtual display. This allows you to run a regular browser in an environment that has no GUI. This is important for two reasons: One: UC Mode is detectable in headless mode. Two: pyautogui doesn't work in headless mode. (Note that some methods such as uc_gui_click_captcha() require pyautogui for performing special actions.) -------- 👤 uc_gui_click_captcha() auto-detects the CAPTCHA type before trying to click it. This is a generic method for both CF Turnstile and Google reCAPTCHA. It will use the code from uc_gui_click_cf() and uc_gui_click_rc() as needed. 👤 uc_gui_click_cf(frame="iframe", retry=False, blind=False) has three args. (All optional). The first one, frame, lets you specify the selector above the iframe in case the CAPTCHA is not located in the first iframe on the page. (In the case of Shadow-DOM, specify the selector of an element that's above the Shadow-DOM.) The second one, retry, lets you retry the click after reloading the page if the first one didn't work (and a CAPTCHA is still present after the page reload). The third arg, blind, (if True), will retry after a page reload (if the first click failed) by clicking at the last known coordinates of the CAPTCHA checkbox without confirming first with Selenium that a CAPTCHA is still on the page. 👤 uc_gui_click_rc(frame="iframe", retry=False, blind=False) is for reCAPTCHA. This may only work a few times before not working anymore... not because Selenium was detected, but because reCAPTCHA uses advanced AI to detect unusual activity, unlike the CF Turnstile, which only uses basic detection. -------- 👤 To find out if UC Mode will work at all on a specific site (before adjusting for timing), load your site with the following script: ```python from seleniumbase import SB with SB(uc=True) as sb: sb.uc_open_with_reconnect(URL, reconnect_time="breakpoint") ``` (If you remain undetected while loading the page and performing manual actions, then you know you can create a working script once you swap the breakpoint with a time and add special methods like sb.uc_click as needed.) -------- 👤 Multithreaded UC Mode: If you're using pytest for multithreaded UC Mode (which requires using one of the pytest [syntax formats](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md)), then all you have to do is set the number of threads when your script runs. (-n NUM) Eg: ```zsh pytest --uc -n 4 ``` (Then pytest-xdist is automatically used to spin up and process the threads.) If you don't want to use pytest for multithreading, then you'll need to do a little more work. That involves using a different multithreading library, (eg. concurrent.futures), and making sure that thread-locking is done correctly for processes that share resources. To handle that thread-locking, include sys.argv.append("-n") in your SeleniumBase file. Here's a sample script that uses concurrent.futures for spinning up multiple processes: ```python import sys from concurrent.futures import ThreadPoolExecutor from seleniumbase import Driver sys.argv.append("-n") # Tell SeleniumBase to do thread-locking as needed def launch_driver(url): driver = Driver(uc=True) try: driver.get(url=url) driver.sleep(2) finally: driver.quit() urls = ['https://seleniumbase.io/demo_page' for i in range(3)] with ThreadPoolExecutor(max_workers=len(urls)) as executor: for url in urls: executor.submit(launch_driver, url) ``` -------- 👤 What makes UC Mode work? Here are the 3 primary things that UC Mode does to make bots appear human:
      • Modifies chromedriver to rename Chrome DevTools Console variables.
      • Launches Chrome browsers before attaching chromedriver to them.
      • Disconnects chromedriver from Chrome during stealthy actions.
      For example, if the Chrome DevTools Console variables aren't renamed, you can expect to find them easily when using selenium for browser automation: (If those variables are still there, then websites can easily detect your bots.) If you launch Chrome using chromedriver, then there will be settings that make your browser look like a bot. (Instead, UC Mode connects chromedriver to Chrome after the browser is launched, which makes Chrome look like a normal, human-controlled web browser.) While chromedriver is connected to Chrome, website services can detect it. Thankfully, raw selenium already includes driver.service.stop() for stopping the chromedriver service, driver.service.start() for starting the chromedriver service, and driver.start_session(capabilities) for reviving the active browser session with the given capabilities. (SeleniumBase UC Mode methods automatically use those raw selenium methods as needed.) Links to those raw Selenium method definitions have been provided for reference (but you don't need to call those methods directly): Also note that chromedriver isn't detectable in a browser tab if it never touches that tab. Here's a JS command that lets you open a URL in a new tab (from your current tab): The above JS method is used within SeleniumBase UC Mode methods for opening URLs in a stealthy way. Since some websites try to detect if your browser is a bot on the initial page load, this allows you to bypass detection in those situations. After a few seconds (customizable), UC Mode tells chromedriver to connect to that tab so that automated commands can now be issued. At that point, chromedriver could be detected if websites are looking for it (but generally websites only look for it during specific events, such as page loads, form submissions, and button clicks). Avoiding detection while clicking is easy if you schedule your clicks to happen at a future point when the chromedriver service has been stopped. Here's a JS command that lets you schedule events (such as clicks) to happen in the future:
    • window.setTimeout(function() { SCRIPT }, MS); --> (Info: W3Schools)
    • The above JS method is used within the SeleniumBase UC Mode method: sb.uc_click(selector) so that clicking can be done in a stealthy way. UC Mode schedules your click, disconnects chromedriver from Chrome, waits a little (customizable), and reconnects. -------- 🏆 Choosing the right CAPTCHA service for your business / website: As an ethical hacker / cybersecurity researcher who builds bots that bypass CAPTCHAs for sport, the CAPTCHA service that I personally recommend for keeping bots out is Google reCAPTCHA: Since Google makes Chrome, Google's own reCAPTCHA service has access to more data than other CAPTCHA services, and can therefore use that data to make better decisions about whether or not web activity is coming from real humans or automated bots. -------- ⚖️ Legal implications of web-scraping: Based on the following article, https://nubela.co/blog/meta-lost-the-scraping-legal-battle-to-bright-data/, (which outlines a court case where social-networking company: Meta lost the legal battle to data-scraping company: Bright Data), it was determined that web scraping is 100% legal in the eyes of the courts as long as: 1. The scraping is only done with public data and not private data. 2. The scraping isn’t done while logged in on the site being scraped. If the above criteria are met, then scrape away! (According to the article) (Note: I'm not a lawyer, so I can't officially offer legal advice, but I can direct people to existing articles online where people can find their own answers.) -------- SeleniumBase
      SeleniumBase
      ================================================ FILE: help_docs/useful_grep_commands.md ================================================ ## Useful grep commands There are several useful **grep** commands for helping you find and/or replace text in multiple files. Examples: #### List all files containing ``self.get_new_driver(``, ignoring ".pyc" files, from the current directory: ``grep -rl "self.get_new_driver(" * --exclude=\*.pyc`` OR ``grep -rl * -e "self.get_new_driver(" --exclude=\*.pyc`` To only search ``.py`` files, use ``--include=\*.py``: ``grep -rl "self.get_new_driver(" * --include=\*.py`` -------- #### Replace all occurrences of "foo_abc" with "bar_xyz" on Linux, for Python files from the current directory: ``sed -i 's/foo_abc/bar_xyz/g' *.py`` #### Replace all occurrences of "foo_abc" with "bar_xyz" on macOS, for Python files from the current directory: ``sed -i '' 's/foo_abc/bar_xyz/g' *.py`` -------- #### Find all chromedriver processes (this combines ``ps`` with ``grep``): ``ps -ef |grep chromedriver`` -------- #### References: * https://stackoverflow.com/questions/16956810/how-do-i-find-all-files-containing-specific-text-on-linux * https://stackoverflow.com/questions/11392478/how-to-replace-a-string-in-multiple-files-in-linux-command-line/20721292#20721292 ================================================ FILE: help_docs/using_safari_driver.md ================================================ ## Using Safari's WebDriver for running browser tests on macOS *(NOTE: Safari's WebDriver requires macOS 10.13 "High Sierra" or later.)* You can find the official Apple documentation regarding "Testing with WebDriver in Safari" on the following page: [https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari](https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari) Run ``safaridriver --enable`` once in a terminal to enable Safari's WebDriver. (If you’re upgrading from a previous macOS release, you may need to prefix the command with ``sudo``.) Now you can use ``--safari`` to run your **SeleniumBase** tests on Safari. ================================================ FILE: help_docs/verify_webdriver.md ================================================

      Verifying that web drivers are installed

      On newer versions of SeleniumBase, the driver is automatically downloaded to the ``seleniumbase/drivers`` folder as needed, and does not need to be on the System Path when running tests. Drivers can be manually downloaded to the ``seleniumbase/drivers`` folder with commands such as: ```zsh sbase get chromedriver sbase get geckodriver sbase get edgedriver ``` -------- If you want to check that you have the correct driver installed on your System PATH (which is no longer necessary unless using the Selenium Grid), then continue reading below: *This assumes you've already downloaded a driver to your **System PATH** with a command such as:* ```zsh sbase get chromedriver --path ``` (The above ``--path`` addition is for Linux/Mac only, which uses ``/usr/local/bin/``. The "Path" is different on Windows, and you'll need to manually copy the driver to your System Path, which is defined in the Control Panel's System Environment Variables.) *You can verify that the correct drivers exist on your System Path by checking inside a Python command prompt.* #### Verifying ChromeDriver ```zsh python ``` ```python >>> from seleniumbase import get_driver >>> driver = get_driver("chrome", headless=False) >>> driver.get("https://www.google.com/chrome") >>> driver.quit() >>> exit() ``` #### Verifying Geckodriver (Firefox WebDriver) ```zsh python ``` ```python >>> from seleniumbase import get_driver >>> driver = get_driver("firefox", headless=False) >>> driver.get("https://www.mozilla.org/firefox") >>> driver.quit() >>> exit() ``` #### Verifying WebDriver for Safari ```zsh python ``` ```python >>> from seleniumbase import get_driver >>> driver = get_driver("safari", headless=False) >>> driver.get("https://www.apple.com/safari") >>> driver.quit() >>> exit() ``` ================================================ FILE: help_docs/virtualenv_instructions.md ================================================ ## Virtual Environment Tutorial There are multiple ways of creating a **[Python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)**. This tutorial covers two of those: * The ``venv`` command (included with Python 3+). * The virtualenvwrapper ``mkvirtualenv`` command. ``venv`` creates virtual environments in the location where run (generally with Python projects). ``mkvirtualenv`` creates virtual environments in one place (generally in your home directory). (The [Python Software Foundation](https://www.python.org/psf/) recommends ``venv`` for creating virtual environments.)

      Option 1: Using "venv"

      > macOS/Linux terminal (``python3 -m venv ENV``) ```zsh python3 -m venv sbase_env source sbase_env/bin/activate ``` > Windows CMD prompt (``py -m venv ENV``): ```zsh py -m venv sbase_env call sbase_env\\Scripts\\activate ``` To exit a virtual env, type ``deactivate``. --------

      Option 2: Using virtualenvwrapper

      > macOS/Linux terminal: ```zsh python3 -m pip install virtualenvwrapper --force-reinstall export WORKON_HOME=$HOME/.virtualenvs source `which virtualenvwrapper.sh` ``` (*Shortcut*: Run ``source virtualenv_install.sh`` from the top-level SeleniumBase folder to perform the above steps.) (If you add ``source `which virtualenvwrapper.sh` `` to your local bash file (``~/.bash_profile`` on macOS, or ``~/.bashrc`` on Linux), virtualenvwrapper commands such as ``mkvirtualenv`` will be available whenever you open a new command prompt.) > Windows CMD prompt: ```zsh py -m pip install virtualenvwrapper-win --force-reinstall --user ``` (*Shortcut*: Run ``win_virtualenv.bat`` from the top-level SeleniumBase folder to perform the above step.)

      Create a virtual environment:

      * ``mkvirtualenv ENV``: ```zsh mkvirtualenv sbase_env ``` (If you have multiple versions of Python installed on your machine, and you want your virtual environment to use a specific Python version, add ``--python=PATH_TO_PYTHON_EXE`` to your ``mkvirtualenv`` command with the Python executable to use.)

      virtualenvwrapper commands:

      Creating a virtual environment: ```zsh mkvirtualenv sbase_env ``` Leaving your virtual environment: ```zsh deactivate ``` Returning to a virtual environment: ```zsh workon sbase_env ``` Listing all virtual environments: ```zsh workon ``` Deleting a virtual environment: ```zsh rmvirtualenv sbase_env ``` -------- If the ``python`` and ``python3`` versions don't match (*while in a virtualenv on macOS or Linux*), the following command will sync the versions: ```zsh alias python=python3 ``` (To remove an alias, use: ``unalias NAME``) -------- To verify the ``python`` version, use: ```zsh python --version ``` To see the PATH of your ``python`` (macOS/Linux), use: ```zsh which python ``` -------- > [python-guide.org/en/latest/dev/virtualenvs](http://docs.python-guide.org/en/latest/dev/virtualenvs/) has more information about Python virtual environments. For specific details about VirtualEnv and VirtualEnvWrapper, see [http://virtualenv.readthedocs.org/en/latest/](http://virtualenv.readthedocs.org/en/latest/) and [http://virtualenvwrapper.readthedocs.org/en/latest/](http://virtualenvwrapper.readthedocs.org/en/latest/). ================================================ FILE: help_docs/webdriver_installation.md ================================================

      Installing webdrivers

      To run web automation, you need webdrivers for each browser you plan on using. With SeleniumBase, drivers are downloaded automatically (as needed) into the SeleniumBase `drivers/` folder. 🎛️ You can also download drivers manually with these commands: ```zsh seleniumbase get chromedriver seleniumbase get geckodriver seleniumbase get edgedriver ``` After running the commands above, web drivers will get downloaded into the `seleniumbase/drivers/` folder. SeleniumBase uses those drivers during tests. (The drivers don't come with SeleniumBase by default.) If the necessary driver is not found in this location while running tests, SeleniumBase will instead look for the driver on the System PATH. If the necessary driver is not on the System PATH either, SeleniumBase will automatically attempt to download the required driver. 🎛️ You can also download specific versions of drivers. Examples: ```zsh sbase get chromedriver 114 sbase get chromedriver 114.0.5735.90 sbase get chromedriver stable sbase get chromedriver beta sbase get chromedriver dev sbase get chromedriver canary sbase get chromedriver previous # One major version before the stable version sbase get chromedriver mlatest # Milestone latest version for detected browser sbase get edgedriver 115.0.1901.183 ``` (NOTE: `sbase` is a shortcut for `seleniumbase`) -------- If you plan on using the [Selenium Grid integration](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid/ReadMe.md) (which allows for ``remote`` webdriver), you'll need to put the drivers on your System PATH. On macOS and Linux, ``/usr/local/bin`` is a good PATH spot. On Windows, you may need to set the System PATH under Environment Variables to include the location where you placed the driver files. As a shortcut, you could place the driver files into your Python ``Scripts/`` folder in the location where you have Python installed, which should already be on your System PATH. Here's where you can go to manually get web drivers from the source: * For Chrome, get [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) on your System PATH. * For Edge, get [Edge Driver (Microsoft WebDriver)](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) on your System PATH. * For Firefox, get [Geckodriver](https://github.com/mozilla/geckodriver/releases) on your System PATH. * For Safari, get [Safari Driver](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/using_safari_driver.md) on your System PATH. **macOS shortcuts**: 🎛️ You can also install drivers by using ``brew`` (aka ``homebrew``): ```zsh brew install --cask chromedriver brew install geckodriver ``` 🎛️ You can also upgrade existing webdrivers: ```zsh brew upgrade --cask chromedriver brew upgrade geckodriver ``` **Linux shortcuts**: 🎛️ If you still need drivers, these scripts download `chromedriver` and `geckodriver` to a Linux machine: ```zsh wget https://chromedriver.storage.googleapis.com/114.0.5735.90/chromedriver_linux64.zip unzip chromedriver_linux64.zip mv chromedriver /usr/local/bin/ chmod +x /usr/local/bin/chromedriver ``` ```zsh wget https://github.com/mozilla/geckodriver/releases/download/v0.35.0/geckodriver-v0.35.0-linux64.tar.gz tar xvfz geckodriver-v0.35.0-linux64.tar.gz mv geckodriver /usr/local/bin/ chmod +x /usr/local/bin/geckodriver ``` To verify that web drivers are working, **[follow these instructions](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/verify_webdriver.md)**. -------- **Browser Binaries**: 🎛️ Use the `sbase get` command to download the `Chrome for Testing` and `Chrome-Headless-Shell` browser binaries. Example: ```zsh sbase get cft # (For `Chrome for Testing`) sbase get chs # (For `Chrome-Headless-Shell`) ``` Those commands download those binaries into the `seleniumbase/drivers` folder. To use the binaries from there in SeleniumBase scripts, set the `binary_location` to `cft` or `chs`. (Source: https://googlechromelabs.github.io/chrome-for-testing/) -------- ================================================ FILE: install.sh ================================================ #!/usr/bin/env bash pip install -e . --use-pep517 --config-settings="editable_mode=compat" ================================================ FILE: integrations/__init__.py ================================================ ================================================ FILE: integrations/azure/azure_pipelines/ReadMe.md ================================================ ### Running browser-based test automation with [Azure Pipelines](https://dev.azure.com/seleniumbase/seleniumbase/_build?definitionId=1&_a=summary) by using [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) ---------- ### Step 0. Fork the [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) repo on GitHub to get started quickly. * **(You'll be using your own repository eventually.)** ### Step 1. Get Azure Pipelines from the GitHub Marketplace #### Navigate to [https://github.com/marketplace/azure-pipelines](https://github.com/marketplace/azure-pipelines) * **Set up a new plan (it's free) and follow the steps...** ![](https://seleniumbase.github.io/cdn/img/azure/github_azure_pipelines_1.png "GitHub Azure Pipelines") ---------- ![](https://seleniumbase.github.io/cdn/img/azure/github_azure_pipelines_2.png "GitHub Azure Pipelines") ---------- ![](https://seleniumbase.github.io/cdn/img/azure/github_azure_pipelines_3.png "GitHub Azure Pipelines") ---------- ### Step 2. Go to Microsoft Azure DevOps to set up your environment * **Navigate to [https://azure.microsoft.com/en-us/services/devops/?nav=min](https://azure.microsoft.com/en-us/services/devops/?nav=min)** * **Follow the steps...** #### Select "Start free with GitHub >": ![](https://seleniumbase.github.io/cdn/img/azure/azure_devops_1a.png "Azure DevOps") ---------- #### Give your new project a name and set visibility to public (for your SeleniumBase fork): ![](https://seleniumbase.github.io/cdn/img/azure/azure_devops_2.png "Azure DevOps") ---------- #### Select that your code is hosted on GitHub: ![](https://seleniumbase.github.io/cdn/img/azure/azure_devops_3.png "Azure DevOps") ---------- #### Select your fork of SeleniumBase as your repository: ![](https://seleniumbase.github.io/cdn/img/azure/azure_devops_4.png "Azure DevOps") ---------- #### Copy the [azure-pipelines.yml](https://github.com/seleniumbase/SeleniumBase/blob/master/azure-pipelines.yml) file from SeleniumBase into the azure-pipelines.yml box to create your new pipeline: ![](https://seleniumbase.github.io/cdn/img/azure/azure_devops_5.png "Azure DevOps") #### When you're done copying, click "Run". ---------- ### Step 3. Congratulations! Your browser tests are now running! * **Here's what a SeleniumBase sample run in Azure Pipelines may look like:** [https://dev.azure.com/seleniumbase/seleniumbase/\_build/results?buildId=234](https://dev.azure.com/seleniumbase/seleniumbase/_build/results?buildId=234) ![](https://seleniumbase.github.io/cdn/img/azure/azure_devops_6.png "Azure DevOps") ---------- #### Every time you create a pull request now, Azure Pipelines will run your tests automatically. **To learn more, study [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) and see how the [azure-pipelines.yml](https://github.com/seleniumbase/SeleniumBase/blob/master/azure-pipelines.yml) file works.** ================================================ FILE: integrations/azure/jenkins/ReadMe.md ================================================ ### Building a browser-based test automation server with Jenkins on Azure by using SeleniumBase (For the official Microsoft tutorial, see [Get Started: Install Jenkins on an Azure Linux VM](https://docs.microsoft.com/en-us/azure/developer/jenkins/configure-on-linux-vm), and then continue with [Step 4](#step4) below to resume SeleniumBase setup after you've created your Jenkins instance.) ---------- ### Step 0. Fork the [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) repo on GitHub to get started quickly. * **(You'll be using your own repository eventually.)** ### Step 1. Find Jenkins in the Azure Marketplace #### Search for ["Jenkins" in the Azure Marketplace](https://portal.azure.com/#blade/Microsoft_Azure_Marketplace/GalleryFeaturedMenuItemBlade/selectedMenuItemId/home/searchQuery/jenkins/resetMenuId/) and select the ``Jenkins (Publisher: Microsoft)`` result to get to the Jenkins Start page. ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_01.png "Jenkins on Azure") ---------- ### Step 2. Launch a Jenkins instance #### Click "Create" and follow the steps... ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_02.png "Jenkins on Azure") ---------- #### Continue to "Additional Settings" when you're done with "Basics". ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_03.png "Jenkins on Azure") ---------- #### On the "Additional Settings" section, set the Size to "B2s": ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_04.png "Jenkins on Azure") ---------- #### Once you've reached Step 5, click "Create" to complete the setup. ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_05.png "Jenkins on Azure") ---------- ### Step 3. Inspect your new Jenkins instance to SSH into the new machine #### Once your new Jenkins instance has finished launching, you should be able to see the main page: ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_06.png "Jenkins on Azure") ---------- #### On the main page, you should be able to find the Public IP Address. * **Use that IP Address to SSH into the machine:** ```zsh ssh USERNAME@IP_ADDRESS ``` ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_07.png "Jenkins on Azure") ---------- ### Step 4. Clone the SeleniumBase repository from the root ("/") directory. ```zsh cd / sudo git clone https://github.com/seleniumbase/SeleniumBase.git ``` ### Step 5. Enter the "linux" folder ```zsh cd SeleniumBase/integrations/linux/ ``` ### Step 6. Give the "jenkins" user sudo access (See [jenkins_permissions.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/jenkins_permissions.sh) for details) ```zsh ./jenkins_permissions.sh ``` ### Step 7. Become the "jenkins" user and enter a "bash" shell ```zsh sudo su jenkins bash ``` ### Step 8. Install dependencies (See [Linuxfile.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/Linuxfile.sh) for details) ```zsh ./Linuxfile.sh ``` ### Step 9. Start up the headless browser display mechanism: Xvfb (See [Xvfb_launcher.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/Xvfb_launcher.sh) for details) ```zsh ./Xvfb_launcher.sh ``` ### Step 10. Go to the SeleniumBase directory ```zsh cd /SeleniumBase ``` ### Step 11. Install the [requirements](https://github.com/seleniumbase/SeleniumBase/blob/master/requirements.txt) for SeleniumBase ```zsh sudo pip install -r requirements.txt --upgrade ``` ### Step 12. Install SeleniumBase (Make sure you already installed the requirements above) ```zsh sudo python setup.py develop ``` ### Step 13. Install chromedriver ```zsh sudo seleniumbase install chromedriver ``` ### Step 14. Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) in Chrome to verify installation (May take up to 10 seconds) ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_08.png "Jenkins on Azure") ---------- ```zsh pytest examples/my_first_test.py --headless --browser=chrome ``` ### Step 15. Secure your Jenkins machine #### Navigate to http://JENKINS_IP_ADDRESS/jenkins-on-azure/ (Depending on your version of Jenkins, you may see the following screen, or nothing at all.) ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_09.png "Jenkins on Azure") ---------- #### Initially, Jenkins uses only ``http``, which makes it less secure. #### You'll need to set up SSH Port Forwarding in order to secure it. * **To do this, copy/paste the string and run it in a NEW command prompt on your local machine (NOT from an SSH terminal session), swapping out the username and DNS name with the ones you set up when creating the Jenkins instance in Azure.** ``ssh -L 127.0.0.1:8080:localhost:8080 USERNAME@DNS_NAME`` ### Step 16. Login to Jenkins #### If you've correctly set up SSH Port Forwarding, the url will be ``http://127.0.0.1:8080/`` ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_10.png "Jenkins on Azure") ---------- #### You'll need to get the password from the SSH terminal on the Linux machine to log in: ```zsh sudo cat /var/lib/jenkins/secrets/initialAdminPassword ``` ### Step 17. Customize Jenkins ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_11.png "Jenkins on Azure") ---------- ### Step 18. Create an Admin user ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_12.png "Jenkins on Azure") ---------- #### Once Jenkins has finished loading, the top left of the page should look like this: ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_13.png "Jenkins on Azure") ---------- ### Step 19. Create a new Jenkins job * **Click on "New Item"** * **Give your new Jenkins job a name (ex: "Test1")** * **Select "Freestyle project"** * **Click "OK"** ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_14.png "Jenkins on Azure") ---------- ### Step 20. Setup your new Jenkins job * **Under "Source Code Management", select "Git".** * **For the "Repository URL", put: ``https://github.com/seleniumbase/SeleniumBase.git``. (You'll eventually be using your own clone of the repository here.)** ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_15.png "Jenkins on Azure") ---------- * **Under "Build", click the "Add build step" dropdown.** * **Select "Execute shell".** * **For the "Command", paste:** ```zsh cd examples pytest my_first_test.py --headless ``` ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_16.png "Jenkins on Azure") ---------- #### Click "Save" when you're done. * **You'll see the following page after that:** ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_18.png "Jenkins on Azure") ---------- ### Step 21. Run your new Jenkins job * **Click on "Build Now"** * **(If everything was done correctly, you'll see a blue dot appear after a few seconds, indicating that the test job passed.)** ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_19.png "Jenkins on Azure") ---------- ### Step 22. See the top Jenkins page for an overview of all jobs ![](https://seleniumbase.github.io/cdn/img/azure/jenkins_on_azure_17.png "Jenkins on Azure") ---------- ### Step 23. Future Work If you have a web application that you want to test, you'll be able to create SeleniumBase tests and add them to Jenkins as you saw here. You may want to create a Deploy job, which downloads the latest version of your repository, and then kicks off all tests to run after that. You could then tell that Deploy job to auto-run whenever a change is pushed to your repository by using: "Poll SCM". All your tests would then be able to run by using: "Build after other projects are built". #### Congratulations! You're now well on your way to becoming a build & release / automation engineer! ================================================ FILE: integrations/behave/ReadMe.md ================================================

      🐝 Behave test runner for SeleniumBase 🐝

      🐝 (Utilizes the [Behave BDD Python library](https://github.com/behave/behave). For more info, see the [Behave tutorial](https://behave.readthedocs.io/en/stable/tutorial.html) and read about [Behave's Gherkin model](https://behave.readthedocs.io/en/stable/gherkin.html).) 🐝 Behave examples with SeleniumBase: [SeleniumBase/examples/behave_bdd](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd) ```zsh > cd examples/behave_bdd/ > behave features/realworld.feature -T -D dashboard -k Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html ******************************************************************************** Feature: SeleniumBase scenarios for the RealWorld App # features/realworld.feature:1 Scenario: Verify RealWorld App (log in / sign out) # features/realworld.feature:3 Given Open "seleniumbase.io/realworld/login" # ../../sbase/steps.py:10 And Clear Session Storage # ../../sbase/steps.py:669 When Type "demo_user" into "#username" # ../../sbase/steps.py:40 And Type "secret_pass" into "#password" # ../../sbase/steps.py:40 And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" # ../../sbase/steps.py:322 Then Assert exact text "Welcome!" in "h1" # ../../sbase/steps.py:157 And Highlight "img#image1" # ../../sbase/steps.py:184 And Click 'a:contains("This Page")' # ../../sbase/steps.py:27 And Save screenshot to logs # ../../sbase/steps.py:239 When Click link "Sign out" # ../../sbase/steps.py:195 Then Assert element 'a:contains("Sign in")' # ../../sbase/steps.py:120 And Assert text "You have been signed out!" # ../../sbase/steps.py:145 ✅ Scenario Passed! - Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html --- LogPath: /Users/michael/github/SeleniumBase/examples/behave_bdd/latest_logs/ ================================================================================== 1 feature passed, 0 failed, 0 skipped 1 scenario passed, 0 failed, 0 skipped 12 steps passed, 0 failed, 0 skipped, 0 undefined Took 0m4.682s ``` 🐝 Another example, which uses higher-level Behave steps to simplify the ``.feature`` file: ```zsh > cd examples/behave_bdd/ > behave features/calculator.feature:61 -T -D dashboard -k Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html ******************************************************************************** Feature: SeleniumBase scenarios for the Calculator App # features/calculator.feature:1 Background: # features/calculator.feature:3 Scenario: 7.0 × (3 + 3) = 42 # features/calculator.feature:49 Given Open the Calculator App # features/steps/calculator.py:4 When Press C # features/steps/calculator.py:9 And Press 7 # features/steps/calculator.py:79 And Press . # features/steps/calculator.py:104 And Press 0 # features/steps/calculator.py:94 And Press × # features/steps/calculator.py:29 And Press ( # features/steps/calculator.py:14 And Press 3 # features/steps/calculator.py:59 And Press + # features/steps/calculator.py:39 And Press 3 # features/steps/calculator.py:59 And Press ) # features/steps/calculator.py:19 Then Verify output is "7.0×(3+3)" # features/steps/calculator.py:135 When Press = # features/steps/calculator.py:44 Then Verify output is "42" # features/steps/calculator.py:135 ✅ Scenario Passed! - Dashboard: /Users/michael/github/SeleniumBase/examples/behave_bdd/dashboard.html --- LogPath: /Users/michael/github/SeleniumBase/examples/behave_bdd/latest_logs/ ================================================================================== 1 feature passed, 0 failed, 0 skipped 1 scenario passed, 0 failed, 8 skipped 14 steps passed, 0 failed, 60 skipped, 0 undefined Took 0m1.672s ``` 🐝⚪ With the Dashboard enabled, you'll get one of these: ### 🐝 Behave-Gherkin files: 🐝 The ``*.feature`` files can use any step seen from: ```zsh behave --steps-catalog ``` 🐝 SeleniumBase includes several pre-made Behave steps, which you can use by creating a Python file with the following line in your ``features/steps/`` directory: ```python from seleniumbase.behave import steps # noqa ``` 🐝 Inside your ``features/environment.py`` file, you should have the following: ```python from seleniumbase import BaseCase from seleniumbase.behave import behave_sb behave_sb.set_base_class(BaseCase) # Accepts a BaseCase subclass from seleniumbase.behave.behave_sb import before_all # noqa from seleniumbase.behave.behave_sb import before_feature # noqa from seleniumbase.behave.behave_sb import before_scenario # noqa from seleniumbase.behave.behave_sb import before_step # noqa from seleniumbase.behave.behave_sb import after_step # noqa from seleniumbase.behave.behave_sb import after_scenario # noqa from seleniumbase.behave.behave_sb import after_feature # noqa from seleniumbase.behave.behave_sb import after_all # noqa ``` 🐝 If you've already created a subclass of ``BaseCase`` with custom methods, you can swap ``BaseCase`` in with your own subclass, which will allow you to easily use your own custom methods in your Behave step definitions. 🐝 Here's an example Python file in the ``features/steps/`` folder: ```python from behave import step @step("Open the Swag Labs Login Page") def go_to_swag_labs(context): sb = context.sb sb.open("https://www.saucedemo.com") sb.clear_local_storage() @step("Login to Swag Labs with {user}") def login_to_swag_labs(context, user): sb = context.sb sb.type("#user-name", user) sb.type("#password", "secret_sauce\n") @step("Verify that the current user is logged in") def verify_logged_in(context): sb = context.sb sb.assert_element("#header_container") sb.assert_element("#react-burger-menu-btn") sb.assert_element("#shopping_cart_container") @step('Add "{item}" to cart') def add_item_to_cart(context, item): sb = context.sb sb.click('div.inventory_item:contains("%s") button[name*="add"]' % item) ``` 🐝 A ``*.feature`` file could look like this: ```gherkin Feature: SeleniumBase scenarios for the Swag Labs App Background: Given Open the Swag Labs Login Page Scenario: User can order a backpack from the store When Login to Swag Labs with standard_user Then Verify that the current user is logged in And Save price of "Backpack" to When Add "Backpack" to Cart Then Verify shopping cart badge shows 1 item(s) When Click on shopping cart icon And Click Checkout And Enter checkout info: First, Last, 12345 And Click Continue Then Verify 1 "Backpack"(s) in cart And Verify cost of "Backpack" is And Verify item total is $29.99 And Verify tax amount is $2.40 And Verify total cost is $32.39 When Click Finish Then Verify order complete When Logout from Swag Labs Then Verify on Login page ``` 🐝 Here's another example of a ``*.feature`` file: ```gherkin Feature: SeleniumBase scenarios for the RealWorld App Scenario: Verify RealWorld App (log in / sign out) Given Open "seleniumbase.io/realworld/login" And Clear Session Storage When Type "demo_user" into "#username" And Type "secret_pass" into "#password" And Do MFA "GAXG2MTEOR3DMMDG" into "#totpcode" Then Assert text "Welcome!" in "h1" And Highlight element "img#image1" And Click 'a:contains("This Page")' And Save screenshot to logs When Click link "Sign out" Then Assert element 'a:contains("Sign in")' And Assert text "You have been signed out!" ``` 🐝 If there's a test failure, that's easy to spot: ```zsh Feature: SeleniumBase scenarios for the Fail Page # features/fail_page.feature:1 Scenario: Fail test on purpose to see what happens # features/fail_page.feature:3 When Open the Fail Page # features/steps/fail_page.py:4 Then Fail test on purpose # features/steps/fail_page.py:9 Assertion Failed: This test fails on purpose! Captured stdout: >>> STEP FAILED: (#2) Fail test on purpose Class / Feature: SeleniumBase scenarios for the Fail Page Test / Scenario: Fail test on purpose to see what happens ❌ Scenario Failed! ``` 🐝🎖️ For convenience, the [SeleniumBase Behave GUI](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/behave_gui.md) lets you run ``behave`` scripts from a Desktop app. 🐝🎖️ To launch it, call ``sbase behave-gui`` or ``sbase gui-behave``: ```zsh sbase behave-gui * Starting the SeleniumBase Behave Commander GUI App... ``` 🐝🎖️ You can customize the tests that show up there: ```zsh sbase behave-gui # all tests sbase behave-gui -i=calculator # tests with "calculator" in the name sbase behave-gui features/ # tests located in the "features/" folder sbase behave-gui features/calculator.feature # tests in that feature ``` --------
      To learn more about SeleniumBase, check out the Docs Site:
      SeleniumBase.io Docs
      All the code is on GitHub:
      SeleniumBase on GitHub ================================================ FILE: integrations/behave/behave.ini ================================================ [behave] show_skipped=false show_timings=false ================================================ FILE: integrations/behave/features/__init__.py ================================================ ================================================ FILE: integrations/behave/features/behave.ini ================================================ [behave] show_skipped=false show_timings=false ================================================ FILE: integrations/behave/features/calculator.feature ================================================ Feature: SeleniumBase scenarios for the Calculator App Background: Given Open the Calculator App Scenario: Pressing "C" outputs "0" When Press C Then Verify output is "0" Scenario: 1 + 2 + 3 + 4 + 5 = "15" When Press C And Press 1 And Press + And Press 2 And Press + And Press 3 And Press + And Press 4 And Press + And Press 5 Then Verify output is "1+2+3+4+5" When Press = Then Verify output is "15" Scenario: 6 × 7 × 8 × 9 = "3024" When Press C And Press 6 And Press × And Press 7 And Press × And Press 8 And Press × And Press 9 Then Verify output is "6×7×8×9" When Press = Then Verify output is "3024" Scenario: 44 - 11 = "33" When Press C And Press 4 And Press 4 And Press - And Press 1 And Press 1 Then Verify output is "44-11" When Press = Then Verify output is "33" Scenario: 7.0 × (3 + 3) = "42" When Press C And Press 7 And Press . And Press 0 And Press × And Press ( And Press 3 And Press + And Press 3 And Press ) Then Verify output is "7.0×(3+3)" When Press = Then Verify output is "42" Scenario: 4.5 × 68 = "306" When Press C And Evaluate [4.5 × 68] Then Verify output is "306" Scenario Outline: ÷ = When Press C And Press [] And Press ÷ And Press [] And Press = Then Verify output is "" Examples: | First | Second | Result | | 1948 | 4 | 487 | | 21 | 0 | Error | Scenario: Save calculator screenshot to logs Given Press [1337] Given Save calculator screenshot to logs ================================================ FILE: integrations/behave/features/environment.py ================================================ from seleniumbase import BaseCase from seleniumbase.behave import behave_sb behave_sb.set_base_class(BaseCase) # Accepts a BaseCase subclass from seleniumbase.behave.behave_sb import before_all # noqa from seleniumbase.behave.behave_sb import before_feature # noqa from seleniumbase.behave.behave_sb import before_scenario # noqa from seleniumbase.behave.behave_sb import before_step # noqa from seleniumbase.behave.behave_sb import after_step # noqa from seleniumbase.behave.behave_sb import after_scenario # noqa from seleniumbase.behave.behave_sb import after_feature # noqa from seleniumbase.behave.behave_sb import after_all # noqa ================================================ FILE: integrations/behave/features/fail_page.feature ================================================ Feature: SeleniumBase scenarios for the Fail Page Scenario: Fail test on purpose to see what happens When Open the Fail Page Then Fail test on purpose ================================================ FILE: integrations/behave/features/realworld.feature ================================================ Feature: SeleniumBase scenarios for the RealWorld App Scenario: Verify RealWorld App (log in / sign out) Given Open the RealWorld Login Page When Login to the RealWorld App Then Assert exact text "Welcome!" in "h1" When Highlight element "img#image1" And Click element 'a:contains("This Page")' Then Save a screenshot to the logs When Click link "Sign out" Then Assert element 'a:contains("Sign in")' And Assert text "You have been signed out!" ================================================ FILE: integrations/behave/features/steps/__init__.py ================================================ ================================================ FILE: integrations/behave/features/steps/calculator.py ================================================ from behave import step @step("Open the Calculator App") def go_to_calculator(context): context.sb.open("https://seleniumbase.io/apps/calculator") @step("Press C") def press_c(context): context.sb.click("button#clear") @step("Press (") def press_open_paren(context): context.sb.click('button[id="("]') @step("Press )") def press_close_paren(context): context.sb.click('button[id=")"]') @step("Press ÷") def press_divide(context): context.sb.click("button#divide") @step("Press ×") def press_multiply(context): context.sb.click("button#multiply") @step("Press -") def press_subtract(context): context.sb.click("button#subtract") @step("Press +") def press_add(context): context.sb.click("button#add") @step("Press =") def press_equal(context): context.sb.click("button#equal") @step("Press 1") def press_1(context): context.sb.click('button[id="1"]') @step("Press 2") def press_2(context): context.sb.click('button[id="2"]') @step("Press 3") def press_3(context): context.sb.click('button[id="3"]') @step("Press 4") def press_4(context): context.sb.click('button[id="4"]') @step("Press 5") def press_5(context): context.sb.click('button[id="5"]') @step("Press 6") def press_6(context): context.sb.click('button[id="6"]') @step("Press 7") def press_7(context): context.sb.click('button[id="7"]') @step("Press 8") def press_8(context): context.sb.click('button[id="8"]') @step("Press 9") def press_9(context): context.sb.click('button[id="9"]') @step("Press 0") def press_0(context): context.sb.click('button[id="0"]') @step("Press ←") def press_delete(context): context.sb.click("button#delete") @step("Press .") def press_dot(context): context.sb.click('button[id="."]') @step("Press [{number}]") def enter_number_into_calc(context, number): sb = context.sb for digit in number: sb.click('button[id="%s"]' % digit) @step("Evaluate [{equation}]") def evaluate_equation(context, equation): sb = context.sb for key in equation: if key == " ": continue elif key == "÷": sb.click("button#divide") elif key == "×": sb.click("button#multiply") elif key == "-": sb.click("button#subtract") elif key == "+": sb.click("button#add") else: sb.click('button[id="%s"]' % key) sb.click("button#equal") @step('Verify output is "{output}"') def verify_output(context, output): sb = context.sb sb.assert_exact_text(output, "#output") @step("Save calculator screenshot to logs") def save_calculator_screenshot_to_logs(context): sb = context.sb sb.save_screenshot_to_logs() ================================================ FILE: integrations/behave/features/steps/fail_page.py ================================================ from behave import step @step("Open the Fail Page") def go_to_error_page(context): context.sb.open("https://seleniumbase.io/error_page/") @step("Fail test on purpose") def fail_on_purpose(context): context.sb.fail("This test fails on purpose!") ================================================ FILE: integrations/behave/features/steps/real_world.py ================================================ from behave import step @step("Open the RealWorld Login Page") def go_to_realworld(context): sb = context.sb context.sb.open("https://seleniumbase.io/realworld/login") sb.clear_session_storage() @step("Login to the RealWorld App") def login_to_realworld(context): sb = context.sb sb.type("#username", "demo_user") sb.type("#password", "secret_pass") sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit @step('Highlight element {selector}') def highlight(context, selector): if selector.startswith('"') or selector.startswith("'"): selector = selector[1:] if selector.endswith('"') or selector.endswith("'"): selector = selector[:-1] sb = context.sb sb.highlight(selector) @step('Click element {selector}') def click(context, selector): if selector.startswith('"') or selector.startswith("'"): selector = selector[1:] if selector.endswith('"') or selector.endswith("'"): selector = selector[:-1] sb = context.sb sb.click(selector) @step('Click link {link}') def click_link(context, link): if link.startswith('"') or link.startswith("'"): link = link[1:] if link.endswith('"') or link.endswith("'"): link = link[:-1] sb = context.sb sb.click_link(link) @step('Save a screenshot to the logs') def save_screenshot_to_logs(context): sb = context.sb sb.save_screenshot_to_logs() @step('Assert element {selector}') def assert_element(context, selector): if selector.startswith('"') or selector.startswith("'"): selector = selector[1:] if selector.endswith('"') or selector.endswith("'"): selector = selector[:-1] sb = context.sb sb.assert_element(selector) @step('Assert text {text} in {selector}') def assert_text_in_selector(context, text, selector): if text.startswith('"') or text.startswith("'"): text = text[1:] if text.endswith('"') or text.endswith("'"): text = text[:-1] if selector.startswith('"') or selector.startswith("'"): selector = selector[1:] if selector.endswith('"') or selector.endswith("'"): selector = selector[:-1] sb = context.sb sb.assert_text(text, selector) @step('Assert text {text}') def assert_text(context, text): if text.startswith('"') or text.startswith("'"): text = text[1:] if text.endswith('"') or text.endswith("'"): text = text[:-1] sb = context.sb sb.assert_text(text) @step('Assert exact text {text} in {selector}') def assert_exact_text(context, text, selector): if text.startswith('"') or text.startswith("'"): text = text[1:] if text.endswith('"') or text.endswith("'"): text = text[:-1] if selector.startswith('"') or selector.startswith("'"): selector = selector[1:] if selector.endswith('"') or selector.endswith("'"): selector = selector[:-1] sb = context.sb sb.assert_exact_text(text, selector) ================================================ FILE: integrations/behave/features/steps/swag_labs.py ================================================ from behave import step @step("Open the Swag Labs Login Page") def go_to_swag_labs(context): sb = context.sb sb.open("https://www.saucedemo.com") sb.clear_local_storage() @step("Login to Swag Labs with {user}") def login_to_swag_labs(context, user): sb = context.sb sb.type("#user-name", user) sb.type("#password", "secret_sauce\n") @step("Verify that the current user is logged in") def verify_logged_in(context): sb = context.sb sb.assert_element("#header_container") sb.assert_element("#react-burger-menu-btn") sb.assert_element("#shopping_cart_container") @step('Add "{item}" to cart') def add_item_to_cart(context, item): sb = context.sb sb.click('div.inventory_item:contains("%s") button[name*="add"]' % item) @step('Save price of "{item}" to <{var}>') def save_price_of_item(context, item, var): sb = context.sb price = sb.get_text( 'div.inventory_item:contains("%s") .inventory_item_price' % item ) sb.variables[var] = price @step('Remove "{item}" from cart') def remove_item_to_cart(context, item): sb = context.sb sb.click('div.inventory_item:contains("%s") button[name*="remove"]' % item) @step("Verify shopping cart badge shows {number} item(s)") def verify_badge_number(context, number): sb = context.sb sb.assert_exact_text(number, "span.shopping_cart_badge") @step("Verify shopping cart badge is missing") def verify_badge_missing(context): sb = context.sb sb.assert_element_not_visible("span.shopping_cart_badge") @step("Click on shopping cart icon") def click_shopping_cart(context): sb = context.sb sb.click("#shopping_cart_container a") @step("Click Checkout") def click_checkout(context): sb = context.sb sb.click("#checkout") @step("Enter checkout info: {first_name}, {last_name}, {zip_code}") def enter_checkout_info(context, first_name, last_name, zip_code): sb = context.sb sb.type("#first-name", first_name) sb.type("#last-name", last_name) sb.type("#postal-code", zip_code) @step("Click Continue") def click_continue(context): sb = context.sb sb.click("input#continue") @step('Verify {quantity} "{item}"(s) in cart') def verify_item_in_cart(context, quantity, item): sb = context.sb sb.assert_exact_text( quantity, 'div.cart_item:contains("%s") div.cart_quantity' % item ) @step('Verify cost of "{item}" is <{var}>') def verify_cost_of_item(context, item, var): sb = context.sb earlier_price = sb.variables[var] sb.assert_exact_text( earlier_price, 'div.cart_item_label:contains("%s") .inventory_item_price' % item ) @step("Verify item total is {item_total}") def verify_item_total(context, item_total): sb = context.sb sb.assert_exact_text( "Item total: %s" % item_total, "div.summary_subtotal_label", timeout=1 ) @step("Verify tax amount is {tax_amount}") def verify_tax_amount(context, tax_amount): sb = context.sb sb.assert_exact_text( "Tax: %s" % tax_amount, "div.summary_tax_label", timeout=1 ) @step("Verify total cost is {total_cost}") def verify_total_cost(context, total_cost): sb = context.sb sb.assert_exact_text( "Total: %s" % total_cost, "div.summary_total_label", timeout=1 ) @step("Click Finish") def click_finish(context): sb = context.sb sb.click("button#finish") @step("Verify order complete") def verify_order_complete(context): sb = context.sb sb.assert_exact_text("Thank you for your order!", "h2") sb.assert_element('img[alt="Pony Express"]') @step("Logout from Swag Labs") def logout_from_swag_labs(context): sb = context.sb sb.js_click("a#logout_sidebar_link") @step("Verify on Login page") def verify_on_login_page(context): sb = context.sb sb.assert_element("#login-button") @step("Sort items from Z to A") def sort_items_from_z_to_a(context): sb = context.sb sb.select_option_by_text("select.product_sort_container", "Name (Z to A)") @step('Verify "{item}" on top') def verify_item_on_top(context, item): sb = context.sb sb.assert_text(item, "div.inventory_item_name") ================================================ FILE: integrations/behave/features/swag_labs.feature ================================================ Feature: SeleniumBase scenarios for the Swag Labs App Background: Given Open the Swag Labs Login Page Scenario: User can log in and log out successfully When Login to Swag Labs with standard_user Then Verify that the current user is logged in When Logout from Swag Labs Then Verify on Login page Scenario: User can order a backpack from the store When Login to Swag Labs with standard_user Then Verify that the current user is logged in And Save price of "Backpack" to When Add "Backpack" to Cart Then Verify shopping cart badge shows 1 item(s) When Click on shopping cart icon And Click Checkout And Enter checkout info: First, Last, 12345 And Click Continue Then Verify 1 "Backpack"(s) in cart And Verify cost of "Backpack" is And Verify item total is $29.99 And Verify tax amount is $2.40 And Verify total cost is $32.39 When Click Finish Then Verify order complete When Logout from Swag Labs Then Verify on Login page Scenario: User can order two items from the store When Login to Swag Labs with standard_user And Add "Bike Light" to Cart And Add "Fleece Jacket" to Cart Then Verify shopping cart badge shows 2 item(s) When Click on shopping cart icon And Click Checkout And Enter checkout info: First, Last, 54321 And Click Continue Then Verify 1 "Bike Light"(s) in cart Then Verify 1 "Fleece Jacket"(s) in cart And Verify item total is $59.98 And Verify tax amount is $4.80 And Verify total cost is $64.78 When Click Finish Then Verify order complete When Logout from Swag Labs Then Verify on Login page Scenario: User can sort items by name from Z to A When Login to Swag Labs with standard_user And Sort items from Z to A Then Verify "Test.allTheThings() T-Shirt" on top When Logout from Swag Labs Then Verify on Login page Scenario: User can add & remove 6 items to/from cart When Login to Swag Labs with standard_user And Add "Backpack" to Cart And Add "Bike Light" to Cart And Add "Bolt T-Shirt" to Cart And Add "Fleece Jacket" to Cart And Add "Onesie" to Cart And Add "Test.allTheThings() T-Shirt" to Cart Then Verify shopping cart badge shows 6 item(s) When Remove "Backpack" from Cart And Remove "Bike Light" from Cart And Remove "Bolt T-Shirt" from Cart And Remove "Fleece Jacket" from Cart And Remove "Onesie" from Cart And Remove "Test.allTheThings() T-Shirt" from Cart Then Verify shopping cart badge is missing When Logout from Swag Labs Then Verify on Login page ================================================ FILE: integrations/brython/ReadMe.md ================================================ ## Getting Started with Brython * Brython was designed for replacing JavaScript with Python. * This tutorial will show you how to get started quickly. ### Here's the web app you'll be creating: Brython Demo ### 0. Install ``brython``: ```zsh pip install brython ``` ### 1. Get a web server up and running: Run from [SeleniumBase/integrations/brython](https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/brython): ```zsh python -m http.server ``` (You can stop the server by using Ctrl+C) ### 2. Navigate to [http://localhost:8000/](http://localhost:8000/) Now click on the examples to see Brython in action. ### 3. For more info, see the following: * https://brython.info/ * https://pypi.org/project/brython/ * https://github.com/brython-dev/brython ================================================ FILE: integrations/brython/index.html ================================================

      Library / Indexed DB Example

      ================================================ FILE: integrations/brython/index.py ================================================ from browser import document def setup_page(): document["topHeader"].textContent = "Brython Examples:" ================================================ FILE: integrations/brython/library.html ================================================

      Library

      TitleAuthorISBNActions
      ================================================ FILE: integrations/docker/ReadMe.md ================================================ ## Docker setup instructions for SeleniumBase #### 1. Install the Docker Desktop: You can get that from here: https://www.docker.com/products/docker-desktop/ You might also want to install the Docker Engine: https://docs.docker.com/engine/install/ #### 2. Go to the SeleniumBase home directory on the command line, which is where [Dockerfile](https://github.com/seleniumbase/SeleniumBase/blob/master/Dockerfile) is located. (This assumes you've already cloned the SeleniumBase repo.) #### 3. Create your Docker image from your Dockerfile: (Get ready to wait awhile) **(Windows / Linux / Intel macOS)** docker build -t seleniumbase . **(Apple Silicon macOS, eg. M1/M2/M3/M4):** Users should first [Enable Rosetta in Docker Desktop](https://stackoverflow.com/a/76586216/7058266). (Otherwise Chrome will crash on launch with errors such as: `"InvalidSessionIdException"` and `"Unable to receive message from renderer"`) Enable Rosetta Then you can run these commands: export DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build --platform linux/amd64 -t seleniumbase . #### 4. Run [the example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) with Chrome inside your Docker: (Once the test completes after a few seconds, you'll automatically exit the Docker shell) docker run seleniumbase ./run_docker_test_in_chrome.sh #### 5. You can also enter Docker and stay inside the shell: docker run -i -t seleniumbase #### 6. Now you can run the example test from inside the Docker shell: ./run_docker_test_in_chrome.sh #### 7. When you're satisfied, you may exit the Docker shell: exit #### 8. (Optional) Since Docker images and containers take up a lot of space, you may want to clean up your machine from time to time when they’re not being used: Details on that can be found here: http://stackoverflow.com/questions/17236796/how-to-remove-old-docker-containers Here are a few of those cleanup commands: docker container prune docker system prune docker images | grep "" | awk '{print $3}' | xargs docker rmi docker rm 'docker ps --no-trunc -aq' If you want to completely remove all of your Docker containers and images, use these commands: (If there's nothing to delete, those commands will return an error.) docker rm -f $(docker ps -a -q) docker rmi -f $(docker images -q) For more cleanup commands, check out: https://codefresh.io/blog/everyday-hacks-docker/ #### 9. (Optional) More reading on Docker can be found here: * https://docs.docker.com * https://docs.docker.com/get-started/ * https://docs.docker.com/docker-for-mac/ ================================================ FILE: integrations/docker/docker-entrypoint.sh ================================================ #!/bin/bash set -e echo "***** SeleniumBase Docker Machine *****" exec "$@" ================================================ FILE: integrations/docker/run_docker_test_in_chrome.sh ================================================ #!/bin/bash set -e # Run example test from inside Docker image echo "Running example SeleniumBase test from Docker with headless Chrome..." cd /SeleniumBase/examples/ && pytest my_first_test.py --browser=chrome --headless exec "$@" ================================================ FILE: integrations/github/ReadMe.md ================================================ ### GitHub Integrations > **Table of Contents / Navigation:** > - [**Actions/Workflows**](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/github/workflows/ReadMe.md) > - [**Extras/Action-Integrations**](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/github/workflows/extras.md) ================================================ FILE: integrations/github/workflows/ReadMe.md ================================================ ## Running browser tests on [GitHub Actions](https://github.com/seleniumbase/SeleniumBase/actions) with [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_7.png "GitHub Actions") ---------- ### Step 0. Create a fork of [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) on GitHub to help you get started. * **(You'll be using your own repo eventually.)** ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_2.png "Create a fork of SeleniumBase") ---------- ### Step 1. From the GitHub Actions tab, choose to set up a Python package Workflow. ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_1.png "GitHub Actions") ---------- ### Step 2. Add your workflow ``.yml`` script. * **(If using a SeleniumBase fork, the script from https://github.com/seleniumbase/SeleniumBase/blob/master/.github/workflows/python-package.yml already exists to help guide you.)** ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_9.png "GitHub Actions") ### Step 3. Commit your changes to GitHub. ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_4.png "GitHub Actions") ---------- ### Step 4. Your tests will now run on every pull request and on every commit to the ``master`` branch. * **(See https://github.com/seleniumbase/SeleniumBase/actions for the SeleniumBase example.)** ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_5.png "GitHub Actions") * **(You can click inside each build for more details.)** ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_6.png "GitHub Actions") * **(You can also see the specific steps being performed by each command.)** ![](https://seleniumbase.github.io/cdn/img/gha/github_workflows_7.png "GitHub Actions") * **(You'll notice that web browsers such as Chrome and Firefox get installed for tests to use. SeleniumBase uses pytest for running tests while using Selenium to interact with web browsers.)** ---------- ### Congratulations! You now know how to create and run browser tests with GitHub Actions! ### **Study [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) to learn more!** ================================================ FILE: integrations/github/workflows/extras.md ================================================

      Integrations for GitHub Actions:

      ### Uploading Artifacts: * Here's an example using [upload-artifact@v6](https://github.com/actions/upload-artifact) to push up a SeleniumBase-generated artifact. ```yml - uses: actions/upload-artifact@v6 with: name: Click to download the presentation path: saved_presentations/my_presentation.html ``` ### Slack Notifications - [rtCamp/action-slack-notify](https://github.com/rtCamp/action-slack-notify) can be used to send notifications to Slack. Usage: * Create a slack integration webhook if you don't have one already. * Create a ``SLACK_WEBHOOK`` secret on your repository with the webhook token value. * For this particular action, ``SLACK_CHANNEL`` is an optional environment variable that defaults to the webhook token channel if not specified. * The following example shows how to put a link to your workflow as the ``SLACK_MESSAGE`` (Lets you see artifacts pushed up, such as from the SeleniumBase Presenter feature!): ```yml - name: Slack notification uses: rtCamp/action-slack-notify@master env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_CHANNEL: general SLACK_ICON_EMOJI: rocket SLACK_USERNAME: SeleniumBase SLACK_MESSAGE: 'Actions workflow completed successful! :tada: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' ``` ================================================ FILE: integrations/google_cloud/ReadMe.md ================================================ ### Building a browser-based test automation server on the [Google Cloud Platform](https://cloud.google.com/) by using [SeleniumBase](https://github.com/seleniumbase/SeleniumBase) (This tutorial, [from a previous Google Cloud Meetup](https://www.meetup.com/Boston-Google-Cloud-Meetup/events/230839686/?showDescription=true), will teach you how to setup a Linux server for running automated browser tests. The cost of running this server is about [$13.60/month on Google Cloud](https://console.cloud.google.com/launcher/details/bitnami-launchpad/jenkins) (enough to handle **5 parallel tests**). This is less expensive than using other platforms.)

      (SeleniumBase Google Cloud Video)

      #### Step 1. Open the Google Cloud Platform Cloud Launcher * Navigate to [https://console.cloud.google.com/launcher](https://console.cloud.google.com/launcher) * (If you already have an active Google Cloud project, the Google Cloud Launcher will probably default to using that. If you don't, [sign up for the free trial of Google Cloud Platform here](https://console.cloud.google.com/freetrial) to get started.) #### Step 2. Launch a Jenkins instance ![](https://seleniumbase.github.io/cdn/img/gcp/gcp_cloud_launcher_jenkins.png "Finding Jenkins") * Under "Cloud Launcher", Click on "Jenkins Certified by Bitnami" * Click on "Launch on Compute Engine" * Give the instance a name * Give the instance a zone * Click "Create" #### Step 3. Connect with your new Jenkins instance ![](https://seleniumbase.github.io/cdn/img/gcp/gcp_ssh.png "SSH into your Jenkins instance") * SSH into your new instance by selecting: "SSH" => "Open in browser window" from the instance page. #### Step 4. Clone the SeleniumBase repository from the root ("/") directory. ```zsh cd / sudo git clone https://github.com/seleniumbase/SeleniumBase.git ``` #### Step 5. Enter the "linux" folder ```zsh cd SeleniumBase/integrations/linux/ ``` #### Step 6. Give Jenkins (aka "tomcat" user) sudo access (See [tomcat_permissions.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/tomcat_permissions.sh) for details) ```zsh ./tomcat_permissions.sh ``` #### Step 7. Become "tomcat" (the Jenkins user) and enter a "bash" shell ```zsh sudo su tomcat bash ``` #### Step 8. Install dependencies (See [Linuxfile.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/Linuxfile.sh) for details) ```zsh ./Linuxfile.sh ``` #### Step 9. Start up the headless browser display mechanism: Xvfb (See [Xvfb_launcher.sh](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/linux/Xvfb_launcher.sh) for details) ```zsh ./Xvfb_launcher.sh ``` #### Step 10. Go to the SeleniumBase directory ```zsh cd /SeleniumBase ``` #### Step 11. Install the [requirements](https://github.com/seleniumbase/SeleniumBase/blob/master/requirements.txt) for SeleniumBase ```zsh sudo pip install -r requirements.txt --upgrade ``` #### Step 12. Install SeleniumBase ```zsh sudo python setup.py develop ``` #### Step 13. Run an [example test](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py) on Chrome to verify installation (May take up to 10 seconds) ![](https://seleniumbase.github.io/cdn/img/gcp/gcp_bitnami.png "Linux SSH Terminal") ```zsh pytest examples/my_first_test.py --headless ``` #### Step 14. If you prefer using nosetests, that works too ```zsh nosetests examples/my_first_test.py --headless ``` #### Step 15. You can also verify that the example test runs on Firefox ```zsh pytest examples/my_first_test.py --headless --browser=firefox ``` #### Step 16. Login to Jenkins * (The url, as well as username and password, should be accessible from your Google Cloud Platform VM instance page.) #### Step 17. Create a new Jenkins job ![](https://seleniumbase.github.io/cdn/img/gcp/gcp_jenkins_new_job.png "Create a Jenkins job") * Click on "New Item" * Give your new Jenkins job a name (ex: "My_First_Test") * Select "Freestyle project" * Click "OK" #### Step 18. Setup your new Jenkins job * Under "Source Code Management", select "Git". * For the "Repository URL", put: ``https://github.com/seleniumbase/SeleniumBase.git``. (You'll eventually be using your own clone of the repository here.) * Under "Build", click the "Add build step" dropdown and then select "Execute shell". * For the "Command", put: ```zsh pytest examples/my_first_test.py --headless ``` * Click "Save" when you're done. #### Step 19. Run your new Jenkins job * Click on "Build Now" * (If all the setup was done correctly, you should see a blue dot appear after a few seconds, indicating that the test job passed.) #### Step 20. Future Work If you have a web application that you want to test, you'll be able to create SeleniumBase tests and add them to Jenkins as you saw here. You may want to create a Deploy job, which downloads the latest version of your repository, and then kicks off all tests to run after that. You could then tell that Deploy job to auto-run whenever a change is pushed to your repository by using: "Poll SCM". All your tests would then be able to run by using: "Build after other projects are built". You can also use MySQL to save test results in the DB so that you can query the data at any time. #### Congratulations! You're now well on your way to becoming a build & release / automation engineer! ### MySQL DB setup instructions #### Step 21. Return to the Google Cloud Launcher and launch a MySQL Instance ![](https://seleniumbase.github.io/cdn/img/gcp/gcp_mysql.png "Finding MySQL") * Under "Featured Solutions", Click on "MySQL" * Click on "Launch on Compute Engine" * Give the instance a name * Give the instance a zone * Click "Create" #### Step 22. Get the Connection credentials for your new MySQL Instance * Under the Google Cloud Platform menu, go to "Compute Engine" * Find your new MySQL instance and then write down the value written in the "External IP" section. * Under the Google Cloud Platform menu, go to "Deployment Manager" * Find your new MySQL instance and then click on it. * Write down the values for Admin username and password. (Username should be "root") #### Step 23. Get a MySQL GUI tool so that you can connect to your MySQL Instance * You can download [MySQL Workbench](https://dev.mysql.com/downloads/tools/workbench/) for this. #### Step 24. Create a new connection to your MySQL Instance * Use the MySQL DB credentials that you saved in Step 21 for this. #### Step 25. Create a new database/schema in your MySQL Instance * You can name your database/schema ``test_db``. #### Step 26. Create the necessary tables in your MySQL database/schema * Run the [create_db_tables.sql](https://raw.githubusercontent.com/seleniumbase/SeleniumBase/master/seleniumbase/core/create_db_tables.sql) script in your MySQL database/schema to create all the required DB tables. #### Step 27. Have your local clone of SeleniumBase connect to your MySQL DB Instance * Update the MySQL connection details in your [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) file to use the credentials that you saved in Step 21. #### Step 28. Have your SeleniumBase Jenkins jobs use your MySQL DB Instance * For the "Execute shell", use the following as your updated "Command": ```zsh pytest examples/test_suite.py --headless --with-db_reporting ``` * Click "Save" when you're done. #### Step 29. Run your new Jenkins job * Click on "Build Now" * If all goes well, you should be seeing new rows appear in your MySQL DB tables. #### Step 30. Congratulations! You've successfully completed this tutorial! ================================================ FILE: integrations/katalon/ReadMe.md ================================================ ### Converting Katalon recordings into SeleniumBase test scripts ### (NOTE: **[SeleniumBase now has Recorder Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md)**, which is recommended over other record & playback tools.) -------- Katalon Recorder (Selenium IDE) is a tool that allows you to record and playback actions performed inside a web browser. It's available as a [downloadable Chrome extension](https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid) and a [downloadable Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/katalon-automation-record/). The Katalon Recorder comes with an option to export recordings as various WebDriver test scripts, one of which is ``Python 2 (WebDriver + unittest)``. Unfortunately, these natively-exported scripts can be very messy and don't always run reliably. The purpose of this converter is to clean up and improve the scripts so that they can be used in production-level environments. #### Step 1: Make a recording with the Katalon Recorder ![](https://seleniumbase.io/cdn/img/katalon_recorder_2.png "Katalon Recorder example") #### Step 2: Export your recording as a Python 2 Webdriver script * ``{} Export`` => ``Python 2 (WebDriver + unittest)`` => ``Save As File`` #### Step 3: Run ``seleniumbase convert`` on your exported Python file ```zsh seleniumbase convert MY_TEST.py ``` * You should see a [MY_TEST_SB.py] file appear in the folder. (``_SB`` is added to the file name so that the original file stays intact in case you still need it.) This new clean & reliable SeleniumBase test script is ready to be added into your test suite for running. -------- -------- The following is an example of a Katalon Recorder exported file (**WebDriver + unittest format**). It is **messy** and has **unnecessary lines of code** to do the task that was recorded: ```python # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import NoAlertPresentException import unittest, time, re class Swag(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "https://www.google.com/" self.verificationErrors = [] self.accept_next_alert = True def test_swag(self): driver = self.driver driver.get("https://www.saucedemo.com/") driver.find_element_by_id("user-name").click() driver.find_element_by_id("user-name").clear() driver.find_element_by_id("user-name").send_keys("standard_user") driver.find_element_by_id("password").click() driver.find_element_by_id("password").clear() driver.find_element_by_id("password").send_keys("secret_sauce") driver.find_element_by_id("login-button").click() def is_element_present(self, how, what): try: self.driver.find_element(by=how, value=what) except NoSuchElementException as e: return False return True def is_alert_present(self): try: self.driver.switch_to_alert() except NoAlertPresentException as e: return False return True def close_alert_and_get_its_text(self): try: alert = self.driver.switch_to_alert() alert_text = alert.text if self.accept_next_alert: alert.accept() else: alert.dismiss() return alert_text finally: self.accept_next_alert = True def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) if __name__ == "__main__": unittest.main() ```
      This can be improved on...
      After running seleniumbase convert [FILE.py] on it, here is the new result: ```python # -*- coding: utf-8 -*- from seleniumbase import BaseCase class Swag(BaseCase): def test_swag(self): self.open('https://www.saucedemo.com/') self.type('#user-name', 'standard_user') self.type('#password', 'secret_sauce') self.click('#login-button') ``` This is much cleaner than the original version. It also uses the more reliable SeleniumBase methods. ================================================ FILE: integrations/linux/Linuxfile.sh ================================================ # SeleniumBase Debian Linux Dependency Installation # (Installs all required dependencies on Linux) # Make sure this script is only run on Linux value="$(uname)" if [ $value == "Linux" ] then echo "Initializing Requirements Setup..." else echo "Not on a Linux machine. Exiting..." exit fi # Go home cd ~ # Configure apt-get resources sudo sh -c "echo \"deb http://packages.linuxmint.com debian import\" >> /etc/apt/sources.list" sudo sh -c "echo \"deb http://downloads.sourceforge.net/project/ubuntuzilla/mozilla/apt all main\" >> /etc/apt/sources.list" # Update aptitude sudo aptitude update # Install core dependencies sudo aptitude install -y --force-yes xserver-xorg-core sudo aptitude install -y --force-yes x11-xkb-utils # Install Xvfb (headless display system) sudo aptitude install -y --force-yes xvfb # Install fonts for web browsers sudo aptitude install -y --force-yes xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic # Install Python core dependencies sudo apt-get update sudo apt-get install -y --force-yes python-setuptools # Install Firefox sudo gpg --keyserver pgp.mit.edu --recv-keys 3EE67F3D0FF405B2 sudo gpg --export 3EE67F3D0FF405B2 > 3EE67F3D0FF405B2.gpg sudo apt-key add ./3EE67F3D0FF405B2.gpg sudo rm ./3EE67F3D0FF405B2.gpg sudo apt-get -qy --no-install-recommends install -y --force-yes firefox sudo apt-get -qy --no-install-recommends install -y --force-yes $(apt-cache depends firefox | grep Depends | sed "s/.*ends:\ //" | tr '\n' ' ') cd /tmp sudo wget --no-check-certificate -O firefox-esr.tar.bz2 'https://download.mozilla.org/?product=firefox-esr-latest&os=linux32&lang=en-US' sudo tar -xjf firefox-esr.tar.bz2 -C /opt/ sudo rm -rf /usr/bin/firefox sudo ln -s /opt/firefox/firefox /usr/bin/firefox sudo rm -f /tmp/firefox-esr.tar.bz2 sudo apt-get -f install -y --force-yes firefox # Install more dependencies sudo apt-get update sudo apt-get install -y --force-yes curl sudo apt-get install -y --force-yes xvfb sudo apt-get install -y --force-yes build-essential chrpath libssl-dev libxft-dev sudo apt-get install -y --force-yes libfreetype6 libfreetype6-dev sudo apt-get install -y --force-yes libfontconfig1 libfontconfig1-dev sudo apt-get install -y --force-yes libmysqlclient-dev sudo apt-get install -y --force-yes python-dev sudo apt-get install -y --force-yes python-MySQLdb # Install Chrome cd /tmp sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo apt-get -f install -y --force-yes sudo dpkg -i google-chrome-stable_current_amd64.deb # Install Chromedriver CHROMEDRIVER_VERSION=`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE` sudo wget -N http://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip -P ~/Downloads sudo unzip -o ~/Downloads/chromedriver_linux64.zip -d ~/Downloads sudo chmod +x ~/Downloads/chromedriver sudo rm -f /usr/local/share/chromedriver sudo rm -f /usr/local/bin/chromedriver sudo rm -f /usr/bin/chromedriver sudo mv -f ~/Downloads/chromedriver /usr/local/share/chromedriver sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver # Finalize apt-get dependancies sudo apt-get -f install -y --force-yes # Get pip sudo easy_install pip ================================================ FILE: integrations/linux/ReadMe.md ================================================ ## Running SeleniumBase on Debian GNU/Linux Files in this folder are currently used with the [Jenkins on Microsoft Azure setup instructions for SeleniumBase](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/azure/jenkins/ReadMe.md), as well as with the [Jenkins on Google Cloud setup instructions for SeleniumBase](https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/google_cloud/ReadMe.md). You can also use these files standalone with any Ubuntu/Debian GNU/Linux machine. ================================================ FILE: integrations/linux/Xvfb_launcher.sh ================================================ # Activate Headless Display (Xvfb) sudo Xvfb -ac :99 -screen 0 1280x1024x16 > /dev/null 2>&1 & export DISPLAY=:99 exec "$@" ================================================ FILE: integrations/linux/jenkins_permissions.sh ================================================ # This file will add "jenkins" to the sudoers file. # To become jenkins from a different user, use the following: # sudo su jenkins # bash sudo sh -c "echo \"%jenkins ALL=(ALL:ALL) ALL\" >> /etc/sudoers" sudo sh -c "echo \"%jenkins ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers" sudo sh -c "echo \"jenkins ALL=NOPASSWD: ALL\" >> /etc/sudoers" ================================================ FILE: integrations/linux/tomcat_permissions.sh ================================================ # This file will add "tomcat" to the sudoers file. # "tomcat" is the Jenkins user name by default on Bitnami Jenkins machines # To become tomcat from a different user, use the following: # sudo su tomcat # bash sudo sh -c "echo \"%tomcat ALL=(ALL:ALL) ALL\" >> /etc/sudoers" sudo sh -c "echo \"%tomcat ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers" sudo sh -c "echo \"tomcat ALL=NOPASSWD: ALL\" >> /etc/sudoers" ================================================ FILE: integrations/node_js/ReadMe.md ================================================

      Creating a Test Runner with NodeJS + Express

      You can create a customized web app for running SeleniumBase tests by using NodeJS and Express. (This tutorial assumes that you've already installed [SeleniumBase](https://github.com/seleniumbase/SeleniumBase). #### 0. Clone SeleniumBase from GitHub * You'll need to work with the files located in the [integrations/node_js](https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js) folder. #### 1. Install NodeJS (if not installed) * Navigate to [https://nodejs.org/en/](https://nodejs.org/en/) * Click to download and install NodeJS #### 2. Upgrade NodeJS (if using an older version) ```zsh npm install -g npm@latest ``` #### 3. Install the example Test Runner for SeleniumBase from [integrations/node_js](https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js). (If dependencies were already installed, you can use `npm ci` for a speed improvement over `npm i` / `npm install` because `npm ci` uses `npm-shrinkwrap.json`, which is generated via ``npm shrinkwrap``.) ```zsh npm install ``` (You should see a `node_modules` folder appear in your `node_js` folder.) #### 4. Run the NodeJS server for your SeleniumBase Test Runner web app ```zsh node server.js ``` (You can stop the server by using Ctrl+C) #### 5. Open the SeleniumBase Test Runner web app * Navigate to [http://127.0.0.1:3000/](http://127.0.0.1:3000/) #### 6. Run an example test Click on a button to run a SeleniumBase example test. #### 7. Expand your web app Now that you have a web app for running SeleniumBase tests, you can expand it to run any script that you want after pressing a button. ================================================ FILE: integrations/node_js/__init__.py ================================================ ================================================ FILE: integrations/node_js/index.html ================================================

      Select a script to run:

      ================================================ FILE: integrations/node_js/my_first_test.py ================================================ from seleniumbase import BaseCase class MyTestClass(BaseCase): def test_swag_labs(self): self.open("https://www.saucedemo.com") self.type("#user-name", "standard_user") self.type("#password", "secret_sauce\n") self.assert_element("#inventory_container") self.assert_text("Products", "span.title") self.click('button[name*="backpack"]') self.click("#shopping_cart_container a") self.assert_text("Your Cart", "span.title") self.assert_text("Backpack", "div.cart_item") self.click("button#checkout") self.type("#first-name", "SeleniumBase") self.type("#last-name", "Automation") self.type("#postal-code", "77123") self.click("input#continue") self.assert_text("Checkout: Overview") self.assert_text("Backpack", "div.cart_item") self.click("button#finish") self.assert_exact_text("Thank you for your order!", "h2") self.assert_element('img[alt="Pony Express"]') self.js_click("a#logout_sidebar_link") ================================================ FILE: integrations/node_js/npm-shrinkwrap.json ================================================ { "name": "app", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "app", "version": "0.0.0", "dependencies": { "express": "~4.21.0" } }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" } }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dependencies": { "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { "node": ">= 0.4" } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } }, "node_modules/express": { "version": "4.21.0", "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.10.0" } }, "node_modules/finalhandler": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { "get-intrinsic": "^1.1.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { "node": ">= 0.10" } }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "bin": { "mime": "cli.js" }, "engines": { "node": ">=4" } }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "engines": { "node": ">= 0.8" } }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ] }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/send/node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "engines": { "node": ">= 0.8" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" }, "engines": { "node": ">= 0.6" } }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { "node": ">= 0.8" } }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { "node": ">= 0.4.0" } }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { "node": ">= 0.8" } } } } ================================================ FILE: integrations/node_js/package.json ================================================ { "name": "app", "version": "0.0.0", "private": true, "scripts": { "start": "node ./bin/www" }, "dependencies": { "express": "~4.21.0" } } ================================================ FILE: integrations/node_js/server.js ================================================ const http = require('http'); const express = require('express'); const path = require('path'); const app = express(); const exec = require('child_process').exec; var server_info = '\nServer running at http://127.0.0.1:3000/ (CTRL+C to stop)'; function run_command(command) { console.log("\n" + command); exec(command, (err, stdout, stderr) => console.log(stdout, server_info)); } app.get('/', function(req, res) { res.sendFile(path.join(__dirname + '/index.html')); }); app.get('/run_my_first_test', function(req, res) { res.sendFile(path.join(__dirname + '/index.html')); res.redirect('/'); run_command("pytest my_first_test.py"); }); app.get('/run_test_demo_site', function(req, res) { res.sendFile(path.join(__dirname + '/index.html')); res.redirect('/'); run_command("pytest test_demo_site.py"); }); app.get('/run_my_first_test_with_demo_mode', function(req, res) { res.sendFile(path.join(__dirname + '/index.html')); res.redirect('/'); run_command("pytest my_first_test.py --demo_mode"); }); app.get('/run_test_demo_site_with_demo_mode', function(req, res) { res.sendFile(path.join(__dirname + '/index.html')); res.redirect('/'); run_command("pytest test_demo_site.py --demo_mode"); }); app.listen(3000, "127.0.0.1", function() { console.log(server_info); }); ================================================ FILE: integrations/node_js/test_demo_site.py ================================================ from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class DemoSiteTests(BaseCase): def test_demo_site(self): # Open a web page in the active browser window self.open("https://seleniumbase.io/demo_page") # Assert the title of the current web page self.assert_title("Web Testing Page") # Assert that an element is visible on the page self.assert_element("tbody#tbodyId") # Assert that a text substring appears in an element self.assert_text("Demo Page", "h1") # Type text into various text fields and then assert self.type("#myTextInput", "This is Automated") self.type("textarea.area1", "Testing Time!\n") self.type('[name="preText2"]', "Typing Text!") self.assert_text("This is Automated", "#myTextInput") self.assert_text("Testing Time!\n", "textarea.area1") self.assert_text("Typing Text!", '[name="preText2"]') # Hover & click a dropdown element and assert results self.assert_text("Automation Practice", "h3") try: self.hover_and_click("#myDropdown", "#dropOption2", timeout=1) except Exception: # Someone moved the mouse while the test ran self.js_click("#dropOption2") self.assert_text("Link Two Selected", "h3") # Click a button and then verify the expected results self.assert_text("This Text is Green", "#pText") self.click('button:contains("Click Me")') self.assert_text("This Text is Purple", "#pText") # Assert that the given SVG is visible on the page self.assert_element('svg[name="svgName"]') # Verify that a slider control updates a progress bar self.assert_element('progress[value="50"]') self.set_value("input#mySlider", "100") self.assert_element('progress[value="100"]') # Verify that a "select" option updates a meter bar self.assert_element('meter[value="0.25"]') self.select_option_by_text("#mySelect", "Set to 75%") self.assert_element('meter[value="0.75"]') # Assert an element located inside an iframe self.assert_false(self.is_element_visible("img")) self.switch_to_frame("#myFrame1") self.assert_true(self.is_element_visible("img")) self.switch_to_default_content() # Assert text located inside an iframe self.assert_false(self.is_text_visible("iFrame Text")) self.switch_to_frame("#myFrame2") self.assert_true(self.is_text_visible("iFrame Text")) self.switch_to_default_content() # Verify that clicking a radio button selects it self.assert_false(self.is_selected("#radioButton2")) self.click("#radioButton2") self.assert_true(self.is_selected("#radioButton2")) # Verify that clicking a checkbox makes it selected self.assert_element_not_visible("img#logo") self.assert_false(self.is_selected("#checkBox1")) self.click("#checkBox1") self.assert_true(self.is_selected("#checkBox1")) self.assert_element("img#logo") # Verify clicking on multiple elements with one call self.assert_false(self.is_selected("#checkBox2")) self.assert_false(self.is_selected("#checkBox3")) self.assert_false(self.is_selected("#checkBox4")) self.click_visible_elements("input.checkBoxClassB") self.assert_true(self.is_selected("#checkBox2")) self.assert_true(self.is_selected("#checkBox3")) self.assert_true(self.is_selected("#checkBox4")) # Verify that clicking an iframe checkbox selects it self.assert_false(self.is_element_visible(".fBox")) self.switch_to_frame("#myFrame3") self.assert_true(self.is_element_visible(".fBox")) self.assert_false(self.is_selected(".fBox")) self.click(".fBox") self.assert_true(self.is_selected(".fBox")) self.switch_to_default_content() # Verify Drag and Drop self.assert_element_not_visible("div#drop2 img#logo") self.drag_and_drop("img#logo", "div#drop2") self.assert_element("div#drop2 img#logo") # Assert link text self.assert_link_text("seleniumbase.com") self.assert_link_text("SeleniumBase on GitHub") self.assert_link_text("seleniumbase.io") # Click link text self.click_link("SeleniumBase Demo Page") # Assert exact text self.assert_exact_text("Demo Page", "h1") # Highlight a page element (Also asserts visibility) self.highlight("h2") # Actions with Demo Mode enabled if self.headed: self.activate_demo_mode() self.type("input", "Have a Nice Day!") self.assert_text("SeleniumBase", "h2") ================================================ FILE: integrations/selenium_grid/ReadMe.md ================================================ ### The ReadMe for the Selenium Grid Hub Launcher has been moved to: [seleniumbase/utilities/selenium_grid/ReadMe.md](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid/ReadMe.md) and all related code has been moved to [seleniumbase/utilities/selenium_grid](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/utilities/selenium_grid) ================================================ FILE: integrations/selenium_ide/ReadMe.md ================================================ ### Converting Katalon recordings into SeleniumBase test scripts ### (NOTE: **[SeleniumBase now has Recorder Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md)**, which is recommended over other record & playback tools.) -------- Katalon Recorder (Selenium IDE) is a tool that allows you to record and playback actions performed inside a web browser. It's available as a [downloadable Chrome extension](https://chrome.google.com/webstore/detail/katalon-recorder-selenium/ljdobmomdgdljniojadhoplhkpialdid) and a [downloadable Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/katalon-automation-record/). The Katalon Recorder comes with an option to export recordings as various WebDriver test scripts, one of which is `Python 2 (WebDriver + unittest)`. Unfortunately, these natively-exported scripts can be very messy and don't always run reliably. The purpose of this converter is to clean up and improve the scripts so that they can be used in production-level environments. #### Step 1: Make a recording with the Katalon Recorder ![](https://seleniumbase.io/cdn/img/katalon_recorder_2.png "Katalon Recorder example") #### Step 2: Export your recording as a Python 2 Webdriver script * `{} Export` => `Python 2 (WebDriver + unittest)` => `Save As File` #### Step 3: Run `seleniumbase convert` on your exported Python file ```zsh seleniumbase convert MY_TEST.py ``` * You should see a [MY_TEST_SB.py] file appear in the folder. (`_SB` is added to the file name so that the original file stays intact in case you still need it.) This new clean & reliable SeleniumBase test script is ready to be added into your test suite for running. -------- -------- The following is an example of a Katalon Recorder exported file (**WebDriver + unittest format**). It is **messy** and has **unnecessary lines of code** to do the task that was recorded: ```python # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import NoAlertPresentException import unittest, time, re class Swag(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "https://www.google.com/" self.verificationErrors = [] self.accept_next_alert = True def test_swag(self): driver = self.driver driver.get("https://www.saucedemo.com/") driver.find_element_by_id("user-name").click() driver.find_element_by_id("user-name").clear() driver.find_element_by_id("user-name").send_keys("standard_user") driver.find_element_by_id("password").click() driver.find_element_by_id("password").clear() driver.find_element_by_id("password").send_keys("secret_sauce") driver.find_element_by_id("login-button").click() def is_element_present(self, how, what): try: self.driver.find_element(by=how, value=what) except NoSuchElementException as e: return False return True def is_alert_present(self): try: self.driver.switch_to_alert() except NoAlertPresentException as e: return False return True def close_alert_and_get_its_text(self): try: alert = self.driver.switch_to_alert() alert_text = alert.text if self.accept_next_alert: alert.accept() else: alert.dismiss() return alert_text finally: self.accept_next_alert = True def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) if __name__ == "__main__": unittest.main() ```
      This can be improved on...
      After running seleniumbase convert [FILE.py] on it, here is the new result: ```python # -*- coding: utf-8 -*- from seleniumbase import BaseCase class Swag(BaseCase): def test_swag(self): self.open('https://www.saucedemo.com/') self.type('#user-name', 'standard_user') self.type('#password', 'secret_sauce') self.click('#login-button') ``` This is much cleaner than the original version. It also uses the more reliable SeleniumBase methods. ================================================ FILE: mkdocs.yml ================================================ # Project information site_name: SeleniumBase Docs site_url: https://seleniumbase.io site_author: Michael Mintz site_description: A complete framework for end-to-end testing with Python, pytest, behave-BDD, and WebDriver. # Repository information repo_name: seleniumbase/SeleniumBase repo_url: https://github.com/seleniumbase/SeleniumBase edit_uri: "" site_dir: "site" docs_dir: "mkdocs_build" # Copyright copyright: Copyright © 2014 - 2026 Michael Mintz # Extensions markdown_extensions: - admonition - md_in_html - tables - toc: permalink: true - pymdownx.highlight: linenums: false - pymdownx.tabbed: alternate_style: true - pymdownx.highlight - pymdownx.superfences - pymdownx.inlinehilite # - pymdownx.details # - pymdownx.snippets # Configuration theme: name: material logo: img/logo3c.png favicon: img/green_logo3.png language: en include_homepage_in_sidebar: true sticky_navigation: true collapse_navigation: true # titles_only: false include_search_page: false search_index_only: true static_templates: - 404.html features: # - search.highlight # - toc.integrate - toc.hide # - navigation.indexes - toc.follow - navigation.sections # - navigation.expand # - navigation.tabs - navigation.top - navigation.tracking - navigation.instant palette: scheme: default primary: blue accent: green font: text: Roboto code: Roboto Mono icon: logo: img/sb_logo_10.png # Plugins plugins: - search: separator: '[\s]+' lang: en - exclude-search: exclude: - CODE_OF_CONDUCT.md - CONTRIBUTING.md - SECURITY.md - examples/case_summary.md - examples/migration/raw_selenium/ReadMe.md - integrations/katalon/ReadMe.md - help_docs/ReadMe.md - help_docs/verify_webdriver.md - help_docs/webdriver_installation.md - seleniumbase/masterqa/ReadMe.md - seleniumbase/utilities/selenium_ide/ReadMe.md - seleniumbase/examples/chart_maker/ReadMe.md # - minify: # minify_html: true # minify_css: true # minify_js: true - mkdocs-simple-hooks: hooks: on_pre_build: mkdocs_build.prepare:main # Page tree nav: - ✅ SeleniumBase README: README.md - 🏰 List of Features: help_docs/features_list.md - 📚 Running Example Tests: examples/ReadMe.md - 🎛️ Command Line Options: help_docs/customizing_test_runs.md - 🪄 Console Scripts: seleniumbase/console_scripts/ReadMe.md - 📊 Dashboard / Reports: examples/example_logs/ReadMe.md - 🔡 Syntax Formats: help_docs/syntax_formats.md - 🎖️ GUI / Commander: help_docs/commander.md - 🔴 Recorder Mode: help_docs/recorder_mode.md - 📘 API Reference: help_docs/method_summary.md - 📗 CDP Mode APIs: help_docs/cdp_mode_methods.md - Python Setup / Install: - 🐉 Get Python, pip, & git: help_docs/install_python_pip_git.md - ⚙️ Virtualenv Instructions: help_docs/virtualenv_instructions.md - 🏄 Install SeleniumBase: help_docs/install.md - 👁️ How it Works: help_docs/how_it_works.md - JS Manager / JS Tools: - ❇️ JS Package Manager: help_docs/js_package_manager.md - 🎦 Demo Mode: help_docs/demo_mode.md - 🚎 Tour Maker: examples/tour_examples/ReadMe.md - 🛂 Dialog Boxes: examples/dialog_boxes/ReadMe.md - 🛂 MasterQA Mode: examples/master_qa/ReadMe.md - 📶 Chart Maker: examples/chart_maker/ReadMe.md - 🎞️ Presentation Maker: examples/presenter/ReadMe.md - Integrations: - 👤 UC Mode: help_docs/uc_mode.md - 🐙 CDP Mode: examples/cdp_mode/ReadMe.md - 🎭 Stealthy Playwright: examples/cdp_mode/playwright/ReadMe.md - 🤖 GitHub CI: integrations/github/workflows/ReadMe.md - 🛂 MasterQA: seleniumbase/masterqa/ReadMe.md - 🗂️ Case Plans: help_docs/case_plans.md - 📱 Mobile Mode: help_docs/mobile_testing.md - 🌐 Selenium Grid: seleniumbase/utilities/selenium_grid/ReadMe.md - 🖼️ Visual Testing: examples/visual_testing/ReadMe.md - 🕵️ The HTML Inspector: help_docs/html_inspector.md - 🤖 Azure Pipelines: integrations/azure/azure_pipelines/ReadMe.md - 🤖 Jenkins on Azure: integrations/azure/jenkins/ReadMe.md - 🤖 Jenkins on Google Cloud: integrations/google_cloud/ReadMe.md - 🤖 NodeJS Test Runner: https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js - Presentations: - ✅ Core Presentation: https://seleniumbase.io/other/core_presentation.html - 🎞️ Presenter Demo: https://seleniumbase.io/other/presenter.html - 📊 Chart Maker Demo: https://seleniumbase.io/other/chart_presentation.html - ⚙️ Python Virtual Envs: https://seleniumbase.io/other/py_virtual_envs.html - 🔰 Fundamentals Demo: https://seleniumbase.io/other/fundamentals.html - Demo Pages: - 🍵 Coffee Cart (Test App): https://seleniumbase.io/coffee/ - 📑 Demo Page (Test Page): https://seleniumbase.io/demo_page - 🔑 Simple App (Test Page): https://seleniumbase.io/simple/login - 🔑 MFA Login (Test App): https://seleniumbase.io/realworld/login - 📝 TinyMCE (Test Page): https://seleniumbase.io/tinymce/ - 🔢 Calculator (Test App): https://seleniumbase.io/apps/calculator - 📱 Device Farm (Virtual): https://seleniumbase.io/devices/ - ⚠️ Error Page (Test Page): https://seleniumbase.io/error_page/ - ⚠️ Page with broken links: https://seleniumbase.io/other/broken_page - ↔️ Drag & Drop (Test Page): https://seleniumbase.io/other/drag_and_drop - 🖼️ Canvas Test Page One: https://seleniumbase.io/canvas/ - 🖼️ Canvas Test Page Two: https://seleniumbase.io/other/canvas - 👤 Shadow DOM/Root Page: https://seleniumbase.io/other/shadow_dom - 🖥️ SeleniumBase in iframe: https://seleniumbase.io/w3schools/sbase - 🖥️ W3Schools Playground: https://seleniumbase.io/w3schools/ - 🖥️ W3Schools iframes: https://seleniumbase.io/w3schools/iframes - 🗃️ W3Schools file upload: https://seleniumbase.io/w3schools/file_upload - 🖲️ W3Schools doubleclick: https://seleniumbase.io/w3schools/double_click - ↔️ W3Schools drag & drop: https://seleniumbase.io/w3schools/drag_drop - ☑️ W3Schools checkboxes: https://seleniumbase.io/w3schools/checkboxes - 🔘 W3Schools radio buttons: https://seleniumbase.io/w3schools/radio_buttons - Pages with CAPTCHAs: - 🔑 CF Turnstile Test: https://seleniumbase.io/apps/turnstile - 🔑 CF Turnstile on Form: https://seleniumbase.io/apps/form_turnstile - 🔐 reCAPTCHA v2 Test: https://seleniumbase.io/apps/recaptcha - 🔐 reCAPTCHA v2 on Form: https://seleniumbase.io/apps/form_recaptcha - 🔐 reCAPTCHA, invisible: https://seleniumbase.io/apps/invisible_recaptcha - Additional Help Docs: - 📑 Table of Contents: help_docs/ReadMe.md - 🖼️ How to handle iframes: help_docs/handling_iframes.md - 🔐 Decorators / Security: seleniumbase/common/ReadMe.md - 🗂️ Case Plans (examples): examples/case_summary.md - 🧭 Using Safari Driver: help_docs/using_safari_driver.md - 🐳 Docker Start Guide: integrations/docker/ReadMe.md - 👤 Shadow DOM Support: help_docs/shadow_dom.md - 👥 macOS Hidden Files: help_docs/hidden_files_info.md - 🗄️ MySQL Instructions: help_docs/mysql_installation.md - 📃 Desired Capabilities: help_docs/desired_capabilities.md - 📜 Useful grep commands: help_docs/useful_grep_commands.md - ⚙️ Downloading drivers: help_docs/webdriver_installation.md - ✅ Selenium Migration: examples/migration/raw_selenium/ReadMe.md - ✔️ Verifying drivers: help_docs/verify_webdriver.md - Behave-BDD Integration: - 🐝 Behave-BDD ReadMe: examples/behave_bdd/ReadMe.md - 🐝 Behave-BDD GUI App: help_docs/behave_gui.md - Languages: - 🌏 Translations: help_docs/translations.md - 🗾 Locale Codes: help_docs/locale_codes.md - Other: - 📺 YouTube Link: https://www.youtube.com/playlist?list=PLp9uKicxkBc5UIlGi2BuE3aWC7JyXpD3m - 📋 Case Studies: help_docs/happy_customers.md - 🙏 Thank You: help_docs/thank_you.md # Google Analytics extra: analytics: provider: google property: G-P5KFWRNLRN # property: UA-167313767-1 # google_analytics: ['G-P5KFWRNLRN', 'seleniumbase.io'] ================================================ FILE: mkdocs_build/ReadMe.txt ================================================ Help Docs Home Page: https://seleniumbase.io ================================================ FILE: mkdocs_build/docs_instructions.txt ================================================ Preparing the SeleniumBase Docs website with "mkdocs" Usage: (from the top-level SeleniumBase folder) pip install -r mkdocs_build/requirements.txt mkdocs build (OR "mkdocs build --strict" to fail on warnings) ================================================ FILE: mkdocs_build/index.txt ================================================ SeleniumBase

      SeleniumBase

      Docs Home Page:

      https://seleniumbase.io

      ================================================ FILE: mkdocs_build/prepare.py ================================================ """ For preparing the mkdocs-generated seleniumbase.io website. """ import os import re from pathlib import Path GITHUB_URL = r"https://github.com/seleniumbase/SeleniumBase/blob/master/" ROOT_DIR = Path(__file__).parents[1] URL_PATTERN = re.compile( r"(?:\(|{}[\w/.]+\.md)(?:\)|\")".format(GITHUB_URL) ) MD_PATH_PATTERN = re.compile(r"\[.*\]\((?P[\w\\._/]+\.md)\)") HEADER_PATTERN = re.compile( r"^(?P#+)\s*(<[\w\s=\":/.]+>)?\s*\**(?P
      .*[\w`]):?\**\s*$", flags=re.MULTILINE, ) PROCESSED_PATHS = set() def normalize_path(path): path = Path(path).absolute().relative_to(ROOT_DIR) return str(path).replace("\\", "/") def read_file(file_name): path = ROOT_DIR / file_name with path.open() as file_handle: content = file_handle.read() return content def process_file(file_name): content = read_file(file_name) urls = URL_PATTERN.findall(content) # content = content.replace("
      ", " \n") content = re.sub(HEADER_PATTERN, r"\g \g
      ", content) directory = "/".join(normalize_path(file_name).split("/")[:-1]) paths = set() md_paths = MD_PATH_PATTERN.findall(content) for md_path in md_paths: path = md_path.lstrip("/") if (ROOT_DIR / directory / path).exists(): path = ROOT_DIR / directory / path else: path = ROOT_DIR / path path = path.resolve().relative_to(ROOT_DIR) paths.add(normalize_path(path)) content = content.replace("(" + md_path + ")", normalize_path(path)) for url in urls: path = url[len(GITHUB_URL) :] # noqa: E203 paths.add(path) content = content.replace( url, normalize_path(os.path.relpath(path, directory)) ) output_path = ROOT_DIR / "mkdocs_build" / file_name if not output_path.parent.is_dir(): os.makedirs(output_path.parent) with output_path.open("w+") as output_file: output_file.write(content) PROCESSED_PATHS.add(normalize_path(file_name)) for path in paths: if path not in PROCESSED_PATHS: process_file(normalize_path(path)) def main(*args, **kwargs): files_to_process = ["README.md"] scanned_dir_list = [] scanned_dir_list.append("help_docs") scanned_dir_list.append("examples") scanned_dir_list.append("examples/cdp_mode") scanned_dir_list.append("examples/cdp_mode/playwright") scanned_dir_list.append("examples/master_qa") scanned_dir_list.append("examples/presenter") scanned_dir_list.append("examples/behave_bdd") scanned_dir_list.append("examples/chart_maker") scanned_dir_list.append("examples/example_logs") scanned_dir_list.append("examples/tour_examples") scanned_dir_list.append("examples/visual_testing") scanned_dir_list.append("integrations/google_cloud") scanned_dir_list.append("seleniumbase/console_scripts") scanned_dir_list.append("examples/migration/raw_selenium") for scanned_dir in scanned_dir_list: for dir_ in os.listdir(ROOT_DIR / scanned_dir): files_to_process.append(os.path.join(scanned_dir, dir_)) video_embed = ( '
      ' '
      " ) updated_files_to_process = [] for file_ in files_to_process: if file_.endswith(".md"): updated_files_to_process.append(file_) for file_ in updated_files_to_process: process_file(file_) for file_ in updated_files_to_process: readme_file = "./mkdocs_build/" + file_ with open(readme_file, mode="r", encoding="utf-8") as f: all_code = f.read() code_lines = all_code.split("\n") changed = False seleniumbase_lines = [] for line in code_lines: if ' href="' in line and '.md"' in line: changed = True line = line.replace('.md"', '/"') if "" in line: changed = True new_lines = [] new_lines.append("---") new_lines.append("hide:") new_lines.append(" - toc") new_lines.append("---") for line in new_lines: seleniumbase_lines.append(line) continue if "" in line: changed = True line = ( r'

      " ) alt_link_badge = ( '' 'SeleniumBase Docs' ) back_to_gh = ( r'' r'SeleniumBase on GitHub' r"" ) if alt_link_badge in line: line = line.replace(alt_link_badge, back_to_gh) changed = True if "/help_docs/uc_mode/" in line and file_.count("/") >= 2: line = line.replace( "/help_docs/uc_mode/", "/../help_docs/uc_mode/" ) changed = True if "" in line: changed = True continue if "" in line and "watch?v=" in line: start_pt = line.find("watch?v=") + len("watch?v=") end_pt = line.find('"', start_pt + 1) yt_code = line[start_pt:end_pt] changed = True line = video_embed.replace("yt_code", yt_code) if "" in line: changed = True line = ( '
      ' "

      ✅ Reliable Browser Testing

      " "
      " ) if "" in line: changed = True line = ( '

      SeleniumBase Docs ' '

      ' ) seleniumbase_lines.append(line) if changed: out_file = open(readme_file, mode="w+", encoding="utf-8") out_file.writelines("\r\n".join(seleniumbase_lines)) out_file.close() ================================================ FILE: mkdocs_build/requirements.txt ================================================ # mkdocs dependencies for generating the seleniumbase.io website # Minimum Python version: 3.10 (for generating docs only) regex>=2026.2.28 pymdown-extensions>=10.21 pipdeptree>=2.31.0 python-dateutil>=2.8.2 Markdown==3.10.2 click==8.3.1 ghp-import==2.1.0 watchdog==6.0.0 cairocffi==1.7.1 pathspec==1.0.4 Babel==2.18.0 paginate==0.5.7 mkdocs==1.6.1 mkdocs-material==9.6.23 mkdocs-exclude-search==0.6.6 mkdocs-simple-hooks==0.1.5 mkdocs-get-deps==0.2.0 mkdocs-material-extensions==1.3.1 ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["setuptools>=70.2.0", "wheel>=0.44.0"] build-backend = "setuptools.build_meta" [project] name = "seleniumbase" readme = "README.md" dynamic = [ "version", "license", "authors", "scripts", "keywords", "classifiers", "description", "maintainers", "entry-points", "dependencies", "requires-python", "optional-dependencies", ] [project.urls] "Homepage" = "https://github.com/seleniumbase/SeleniumBase" "Changelog" = "https://github.com/seleniumbase/SeleniumBase/releases" "Download" = "https://pypi.org/project/seleniumbase/#files" "Blog" = "https://seleniumbase.com/" "Discord" = "https://discord.gg/EdhQTn3EyE" "PyPI" = "https://pypi.org/project/seleniumbase/" "Source" = "https://github.com/seleniumbase/SeleniumBase" "Repository" = "https://github.com/seleniumbase/SeleniumBase" "Documentation" = "https://seleniumbase.io/" [tool.setuptools] packages = [ "seleniumbase", "sbase", "seleniumbase.behave", "seleniumbase.common", "seleniumbase.config", "seleniumbase.console_scripts", "seleniumbase.core", "seleniumbase.drivers", "seleniumbase.drivers.cft_drivers", "seleniumbase.drivers.chs_drivers", "seleniumbase.drivers.opera_drivers", "seleniumbase.drivers.brave_drivers", "seleniumbase.drivers.comet_drivers", "seleniumbase.drivers.atlas_drivers", "seleniumbase.drivers.chromium_drivers", "seleniumbase.extensions", "seleniumbase.fixtures", "seleniumbase.js_code", "seleniumbase.masterqa", "seleniumbase.plugins", "seleniumbase.resources", "seleniumbase.translate", "seleniumbase.undetected", "seleniumbase.undetected.cdp_driver", "seleniumbase.utilities", "seleniumbase.utilities.selenium_grid", "seleniumbase.utilities.selenium_ide", ] [tool.pytest.ini_options] addopts = ["--capture=tee-sys", "-p no:cacheprovider"] norecursedirs = [".*", "build", "dist", "recordings", "temp", "assets"] filterwarnings = [ "ignore::pytest.PytestWarning", "ignore:.*U.*mode is deprecated:DeprecationWarning", ] junit_family = ["legacy"] python_files = ["test_*.py", "*_test.py", "*_tests.py", "*_suite.py"] python_classes = ["Test*", "*Test*", "*Test", "*Tests", "*Suite"] python_functions = ["test_*"] markers = [ "marker1", "marker2", "marker3", "marker_test_suite", "local", "remote", "offline", "expected_failure", "qa", "ci", "e2e", "smoke", "ready", "master", "deploy", "develop", "staging", "production", "release", "active", ] ================================================ FILE: pytest.ini ================================================ [pytest] # Display console output. Disable cacheprovider: addopts = --capture=tee-sys -p no:cacheprovider # Skip these directories during test collection: norecursedirs = .* build dist recordings temp assets # Ignore DeprecationWarning, PytestUnknownMarkWarning filterwarnings = ignore::pytest.PytestWarning ignore:.*U.*mode is deprecated:DeprecationWarning # Configure the junit_family option explicitly: junit_family = legacy # Set pytest discovery rules: # (Most of the rules here are similar to the default rules.) # (Inheriting unittest.TestCase could override these rules.) python_files = test_*.py *_test.py *_tests.py *_suite.py python_classes = Test* *Test* *Test *Tests *Suite python_functions = test_* # Common pytest markers used in examples: # (pytest may require marker registration to prevent warnings.) # (Future versions may turn those marker warnings into errors.) markers = marker1: custom marker marker2: custom marker marker3: custom marker marker_test_suite: custom marker expected_failure: custom marker local: custom marker remote: custom marker offline: custom marker develop: custom marker qa: custom marker ci: custom marker e2e: custom marker ready: custom marker smoke: custom marker deploy: custom marker active: custom marker master: custom marker release: custom marker staging: custom marker production: custom marker ================================================ FILE: requirements.txt ================================================ pip>=26.0.1 packaging>=26.0 setuptools~=70.2;python_version<"3.10" setuptools>=82.0.1;python_version>="3.10" wheel>=0.46.3 attrs>=25.4.0 certifi>=2026.2.25 exceptiongroup>=1.3.1 websockets~=15.0.1;python_version<"3.10" websockets>=16.0;python_version>="3.10" filelock~=3.19.1;python_version<"3.10" filelock>=3.25.2;python_version>="3.10" fasteners>=0.20 mycdp>=1.3.6 pynose>=1.5.5 platformdirs~=4.4.0;python_version<"3.10" platformdirs>=4.9.4;python_version>="3.10" typing-extensions>=4.15.0 sbvirtualdisplay>=1.4.0 MarkupSafe>=3.0.3 Jinja2>=3.1.6 six>=1.17.0 parse>=1.21.1 parse-type>=0.6.6 colorama>=0.4.6 pyyaml>=6.0.3 pygments>=2.19.2 pyreadline3>=3.5.4;platform_system=="Windows" tabcompleter>=1.4.0 pdbp>=1.8.2 idna>=3.11 charset-normalizer>=3.4.6,<4 urllib3>=1.26.20,<2;python_version<"3.10" urllib3>=1.26.20,<3;python_version>="3.10" requests~=2.32.5 sniffio==1.3.1 h11==0.16.0 outcome==1.3.0.post0 trio>=0.31.0,<1;python_version<"3.10" trio>=0.33.0,<1;python_version>="3.10" trio-websocket~=0.12.2 wsproto==1.2.0;python_version<"3.10" wsproto~=1.3.2;python_version>="3.10" websocket-client~=1.9.0 selenium==4.32.0;python_version<"3.10" selenium==4.41.0;python_version>="3.10" cssselect==1.3.0;python_version<"3.10" cssselect>=1.4.0,<2;python_version>="3.10" nest-asyncio==1.6.0 sortedcontainers==2.4.0 execnet==2.1.1;python_version<"3.10" execnet==2.1.2;python_version>="3.10" iniconfig==2.1.0;python_version<"3.10" iniconfig==2.3.0;python_version>="3.10" pluggy==1.6.0 pytest==8.4.2;python_version<"3.11" pytest==9.0.2;python_version>="3.11" pytest-html==4.0.2 pytest-metadata==3.1.1 pytest-ordering==0.6 pytest-rerunfailures==16.0.1;python_version<"3.10" pytest-rerunfailures==16.1;python_version>="3.10" pytest-xdist==3.8.0 parameterized==0.9.0 behave==1.2.6 soupsieve~=2.8.3 beautifulsoup4~=4.14.3 pyotp==2.9.0 python-xlib==0.33;platform_system=="Linux" PyAutoGUI>=0.9.54;platform_system=="Linux" markdown-it-py==3.0.0;python_version<"3.10" markdown-it-py==4.0.0;python_version>="3.10" mdurl==0.1.2 rich>=14.3.3,<15 # --- Testing Requirements --- # # ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.) coverage>=7.10.7;python_version<"3.10" coverage>=7.13.5;python_version>="3.10" pytest-cov>=7.0.0 flake8==7.3.0 mccabe==0.7.0 pyflakes==3.4.0 pycodestyle==2.14.0 ================================================ FILE: sbase/ReadMe.txt ================================================ "SBase" is the short name of "SeleniumBase". Use with console scripts: "python -m sbase". ================================================ FILE: sbase/__init__.py ================================================ from seleniumbase import __version__ # noqa from seleniumbase import BaseCase # noqa from seleniumbase import decorators # noqa from seleniumbase import Driver # noqa from seleniumbase import DriverContext # noqa from seleniumbase import encryption # noqa from seleniumbase import get_driver # noqa from seleniumbase import js_utils # noqa from seleniumbase import shared_utils # noqa from seleniumbase import MasterQA # noqa from seleniumbase import page_actions # noqa from seleniumbase import page_utils # noqa from seleniumbase import SB # noqa from seleniumbase import translate # noqa ================================================ FILE: sbase/__main__.py ================================================ import os import sys # Remove "" and current working directory from the first entry # of sys.path (if present) to avoid using the current directory # in SeleniumBase commands when invoked as "python -m sbase " if sys.path[0] in ("", os.getcwd()): sys.path.pop(0) if __package__ == "": path = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, path) if __name__ == "__main__": import warnings from seleniumbase.console_scripts.run import main warnings.filterwarnings( "ignore", category=DeprecationWarning, module=".*packaging\\.version" ) main() sys.exit() ================================================ FILE: sbase/steps.py ================================================ from behave import step def normalize_text(text): text = text.replace("\\\\", "\\").replace("\\t", "\t").replace("\\n", "\n") text = text.replace('\\"', '"').replace("\\'", "'") return text @step("Open '{url}'") @step('Open "{url}"') @step("Open URL '{url}'") @step('Open URL "{url}"') @step("User opens '{url}'") @step('User opens "{url}"') @step("User opens URL '{url}'") @step('User opens URL "{url}"') @step("User goes to '{url}'") @step('User goes to "{url}"') @step("User goes to URL '{url}'") @step('User goes to URL "{url}"') def open_url(context, url): sb = context.sb sb.open(url) @step("Click '{selector}'") @step('Click "{selector}"') @step("Click element '{selector}'") @step('Click element "{selector}"') @step("User clicks '{selector}'") @step('User clicks "{selector}"') @step("User clicks element '{selector}'") @step('User clicks element "{selector}"') def click_element(context, selector): sb = context.sb sb.click(selector) @step("Type text '{text}' into '{selector}'") @step('Type text "{text}" into "{selector}"') @step("Type text '{text}' into \"{selector}\"") @step('Type text "{text}" into \'{selector}\'') @step("Type text '{text}' in '{selector}'") @step('Type text "{text}" in "{selector}"') @step("Type text '{text}' in \"{selector}\"") @step('Type text "{text}" in \'{selector}\'') @step("Type '{text}' into '{selector}'") @step('Type "{text}" into "{selector}"') @step("Type '{text}' into \"{selector}\"") @step('Type "{text}" into \'{selector}\'') @step("Type '{text}' in '{selector}'") @step('Type "{text}" in "{selector}"') @step("Type '{text}' in \"{selector}\"") @step('Type "{text}" in \'{selector}\'') @step("In '{selector}' type '{text}'") @step('In "{selector}" type "{text}"') @step("In '{selector}' type \"{text}\"") @step('In "{selector}" type \'{text}\'') @step("Into '{selector}' type '{text}'") @step('Into "{selector}" type "{text}"') @step("Into '{selector}' type \"{text}\"") @step('Into "{selector}" type \'{text}\'') @step("Find '{selector}' and type '{text}'") @step('Find "{selector}" and type "{text}"') @step("Find '{selector}' and type \"{text}\"") @step('Find "{selector}" and type \'{text}\'') @step("User types '{text}' in '{selector}'") @step('User types "{text}" in "{selector}"') @step("User types '{text}' in \"{selector}\"") @step('User types "{text}" in \'{selector}\'') @step("User types '{text}' into '{selector}'") @step('User types "{text}" into "{selector}"') @step("User types '{text}' into \"{selector}\"") @step('User types "{text}" into \'{selector}\'') def type_text(context, selector, text): sb = context.sb text = normalize_text(text) sb.type(selector, text) @step("Add text '{text}' into '{selector}'") @step('Add text "{text}" into "{selector}"') @step("Add text '{text}' into \"{selector}\"") @step('Add text "{text}" into \'{selector}\'') @step("Add text '{text}' in '{selector}'") @step('Add text "{text}" in "{selector}"') @step("Add text '{text}' in \"{selector}\"") @step('Add text "{text}" in \'{selector}\'') @step("Add '{text}' into '{selector}'") @step('Add "{text}" into "{selector}"') @step("Add '{text}' into \"{selector}\"") @step('Add "{text}" into \'{selector}\'') @step("Add '{text}' in '{selector}'") @step('Add "{text}" in "{selector}"') @step("Add '{text}' in \"{selector}\"") @step('Add "{text}" in \'{selector}\'') @step("Into '{selector}' add '{text}'") @step('Into "{selector}" add "{text}"') @step("Into '{selector}' add \"{text}\"") @step('Into "{selector}" add \'{text}\'') @step("In '{selector}' add '{text}'") @step('In "{selector}" add "{text}"') @step("In '{selector}' add \"{text}\"") @step('In "{selector}" add \'{text}\'') @step("User adds '{text}' in '{selector}'") @step('User adds "{text}" in "{selector}"') @step("User adds '{text}' in \"{selector}\"") @step('User adds "{text}" in \'{selector}\'') @step("User adds '{text}' into '{selector}'") @step('User adds "{text}" into "{selector}"') @step("User adds '{text}' into \"{selector}\"") @step('User adds "{text}" into \'{selector}\'') def add_text(context, text, selector): sb = context.sb text = normalize_text(text) sb.add_text(selector, text) @step("Assert element '{selector}'") @step('Assert element "{selector}"') @step("Assert element '{selector}' is visible") @step('Assert element "{selector}" is visible') @step("Element '{selector}' should be visible") @step('Element "{selector}" should be visible') def assert_element(context, selector): sb = context.sb sb.assert_element(selector) @step("Assert text '{text}' in '{selector}'") @step('Assert text "{text}" in "{selector}"') @step("Assert text '{text}' in \"{selector}\"") @step('Assert text "{text}" in \'{selector}\'') @step("Text in '{selector}' should contain '{text}'") @step('Text in "{selector}" should contain "{text}"') @step("Text in '{selector}' should contain \"{text}\"") @step('Text in "{selector}" should contain \'{text}\'') def assert_text_in_element(context, text, selector): sb = context.sb text = normalize_text(text) sb.assert_text(text, selector) @step("Assert text '{text}'") @step('Assert text "{text}"') @step("Assert text '{text}' is visible") @step('Assert text "{text}" is visible') @step("Text '{text}' should be visible") @step('Text "{text}" should be visible') def assert_text(context, text): sb = context.sb text = normalize_text(text) sb.assert_text(text) @step("Assert exact text '{text}' in '{selector}'") @step('Assert exact text "{text}" in "{selector}"') @step("Assert exact text '{text}' in \"{selector}\"") @step('Assert exact text "{text}" in \'{selector}\'') @step("Text in '{selector}' should be '{text}'") @step('Text in "{selector}" should be "{text}"') @step("Text in '{selector}' should be \"{text}\"") @step('Text in "{selector}" should be \'{text}\'') def assert_exact_text(context, text, selector): sb = context.sb text = normalize_text(text) sb.assert_exact_text(text, selector) @step("Assert non-empty text in '{selector}'") @step('Assert non-empty text in "{selector}"') @step("Assert text in '{selector}' is not empty") @step('Assert text in "{selector}" is not empty') @step("Text in '{selector}' should be non-empty") @step('Text in "{selector}" should be non-empty') @step("Text in '{selector}' should not be empty") @step('Text in "{selector}" should not be empty') def assert_non_empty_text(context, selector): sb = context.sb sb.assert_non_empty_text(selector) @step("Highlight '{selector}'") @step('Highlight "{selector}"') @step("Highlight element '{selector}'") @step('Highlight element "{selector}"') @step("Use JS to highlight '{selector}'") @step('Use JS to highlight "{selector}"') def highlight_element(context, selector): sb = context.sb sb.highlight(selector) @step("Click link '{link}'") @step('Click link "{link}"') @step("User clicks link '{link}'") @step('User clicks link "{link}"') def click_link(context, link): sb = context.sb sb.click_link(link) @step("JS click '{selector}'") @step('JS click "{selector}"') @step("JS click element '{selector}'") @step('JS click element "{selector}"') @step("Use JS to click '{selector}'") @step('Use JS to click "{selector}"') def js_click(context, selector): sb = context.sb sb.js_click(selector) @step("Save screenshot as '{name}'") @step('Save screenshot as "{name}"') @step("User saves screenshot as '{name}'") @step('User saves screenshot as "{name}"') def save_screenshot_as(context, name): sb = context.sb name = normalize_text(name) sb.save_screenshot(name) @step("Save screenshot to '{folder}' as '{name}'") @step('Save screenshot to "{folder}" as "{name}"') @step("Save screenshot to '{folder}' as \"{name}\"") @step('Save screenshot to "{folder}" as \'{name}\'') @step("User saves screenshot to '{folder}' as '{name}'") @step('User saves screenshot to "{folder}" as "{name}"') @step("User saves screenshot to '{folder}' as \"{name}\"") @step('User saves screenshot to "{folder}" as \'{name}\'') def save_screenshot_to_folder_as(context, name, folder): sb = context.sb name = normalize_text(name) sb.save_screenshot(name, folder) @step("Save screenshot to logs") @step("Save a screenshot to the logs") @step("User saves screenshot to logs") @step("User saves a screenshot to the logs") def save_screenshot_to_logs(context): sb = context.sb sb.save_screenshot_to_logs() @step("Refresh page") @step("Reload page") @step("User refreshes the page") @step("User reloads the page") def refresh_page(context): sb = context.sb sb.refresh_page() @step("Go back") @step("User goes back") @step("User navigates back") def go_back(context): sb = context.sb sb.go_back() @step("Go forward") @step("User goes forward") @step("User navigates forward") def go_forward(context): sb = context.sb sb.go_forward() @step("Set value of '{selector}' to '{text}'") @step('Set value of "{selector}" to "{text}"') @step("Set value of \"{selector}\" to '{text}'") @step('Set value of \'{selector}\' to "{text}"') @step("User sets value of '{selector}' to '{text}'") @step('User sets value of "{selector}" to "{text}"') @step("User sets value of \"{selector}\" to '{text}'") @step('User sets value of \'{selector}\' to "{text}"') def set_value(context, selector, text): sb = context.sb text = normalize_text(text) sb.set_value(selector, text) @step("Switch to iframe '{frame}'") @step('Switch to iframe "{frame}"') @step("Switch to frame '{frame}'") @step('Switch to frame "{frame}"') @step("User switches to iframe '{frame}'") @step('User switches to iframe "{frame}"') @step("User switches to frame '{frame}'") @step('User switches to frame "{frame}"') def switch_to_frame(context, frame): sb = context.sb sb.switch_to_frame(frame) @step("Switch to default content") @step("Exit from iframes") @step("Exit from frames") @step("User switches to default content") @step("User exits from iframes") @step("User exits from frames") def switch_to_default_content(context): sb = context.sb sb.switch_to_default_content() @step("Switch to parent frame") @step("Exit current iframe") @step("Exit current frame") @step("User switches to parent frame") @step("User exits current iframe") @step("User exits current frame") def switch_to_parent_frame(context): sb = context.sb sb.switch_to_parent_frame() @step("Into '{selector}' enter MFA code '{totp_key}'") @step('Into "{selector}" enter MFA code "{totp_key}"') @step("Into '{selector}' enter MFA code \"{totp_key}\"") @step('Into "{selector}" enter MFA code \'{totp_key}\'') @step("Into '{selector}' do MFA '{totp_key}'") @step('Into "{selector}" do MFA "{totp_key}"') @step("Into '{selector}' do MFA \"{totp_key}\"") @step('Into "{selector}" do MFA \'{totp_key}\'') @step("Do MFA '{totp_key}' into '{selector}'") @step('Do MFA "{totp_key}" into "{selector}"') @step("Do MFA \"{totp_key}\" into '{selector}'") @step('Do MFA \'{totp_key}\' into "{selector}"') @step("Enter MFA code '{totp_key}' into '{selector}'") @step('Enter MFA code "{totp_key}" into "{selector}"') @step("Enter MFA code \"{totp_key}\" into '{selector}'") @step('Enter MFA code \'{totp_key}\' into "{selector}"') @step("User enters MFA code '{totp_key}' into '{selector}'") @step('User enters MFA code "{totp_key}" into "{selector}"') @step("User enters MFA code \"{totp_key}\" into '{selector}'") @step('User enters MFA code \'{totp_key}\' into "{selector}"') def enter_mfa_code(context, selector, totp_key): sb = context.sb sb.enter_mfa_code(selector, totp_key) @step("Open if not '{url}'") @step('Open if not "{url}"') @step("Open if not URL '{url}'") @step('Open if not URL "{url}"') @step("User opens '{url}' if not on page") @step('User opens "{url}" if not on page') @step("User opens URL '{url}' if not on page") @step('User opens URL "{url}" if not on page') def open_if_not_url(context, url): sb = context.sb sb.open_if_not_url(url) @step("Select if unselected '{selector}'") @step('Select if unselected "{selector}"') @step("Select '{selector}' if unselected") @step('Select "{selector}" if unselected') @step("User selects '{selector}' if unselected") @step('User selects "{selector}" if unselected') def select_if_unselected(context, selector): sb = context.sb sb.select_if_unselected(selector) @step("Unselect if selected '{selector}'") @step('Unselect if selected "{selector}"') @step("Unselect '{selector}' if selected") @step('Unselect "{selector}" if selected') @step("User unselects '{selector}' if selected") @step('User unselects "{selector}" if selected') def unselect_if_selected(context, selector): sb = context.sb sb.unselect_if_selected(selector) @step("Check if unchecked '{selector}'") @step('Check if unchecked "{selector}"') @step("Check '{selector}' if unchecked") @step('Check "{selector}" if unchecked') @step("User checks '{selector}' if unchecked") @step('User checks "{selector}" if unchecked') def check_if_unchecked(context, selector): sb = context.sb sb.check_if_unchecked(selector) @step("Uncheck if checked '{selector}'") @step('Uncheck if checked "{selector}"') @step("Uncheck '{selector}' if checked") @step('Uncheck "{selector}" if checked') @step("User unchecks '{selector}' if checked") @step('User unchecks "{selector}" if checked') def uncheck_if_checked(context, selector): sb = context.sb sb.uncheck_if_checked(selector) @step("Drag '{drag_selector}' into '{drop_selector}'") @step('Drag "{drag_selector}" into "{drop_selector}"') @step("Drag '{drag_selector}' into \"{drop_selector}\"") @step('Drag "{drag_selector}" into \'{drop_selector}\'') @step("User drags '{drag_selector}' into '{drop_selector}'") @step('User drags "{drag_selector}" into "{drop_selector}"') @step("User drags '{drag_selector}' into \"{drop_selector}\"") @step('User drags "{drag_selector}" into \'{drop_selector}\'') def drag_and_drop(context, drag_selector, drop_selector): sb = context.sb sb.drag_and_drop(drag_selector, drop_selector) @step("Hover '{hover_selector}' and click '{click_selector}'") @step('Hover "{hover_selector}" and click "{click_selector}"') @step("Hover '{hover_selector}' and click \"{click_selector}\"") @step('Hover "{hover_selector}" and click \'{click_selector}\'') @step("User hovers '{hover_selector}' and clicks '{click_selector}'") @step('User hovers "{hover_selector}" and clicks "{click_selector}"') @step("User hovers '{hover_selector}' and clicks \"{click_selector}\"") @step('User hovers "{hover_selector}" and clicks \'{click_selector}\'') def hover_and_click(context, hover_selector, click_selector): sb = context.sb sb.hover_and_click(hover_selector, click_selector) @step("Find '{selector}' and select '{text}'") @step('Find "{selector}" and select "{text}"') @step("Find '{selector}' and select \"{text}\"") @step('Find "{selector}" and select \'{text}\'') @step("User selects '{text}' in '{selector}'") @step('User selects "{text}" in "{selector}"') @step("User selects \"{text}\" in '{selector}'") @step('User selects \'{text}\' in "{selector}"') @step("User finds '{selector}' and selects '{text}'") @step('User finds "{selector}" and selects "{text}"') @step("User finds '{selector}' and selects \"{text}\"") @step('User finds "{selector}" and selects \'{text}\'') def select_option_by_text(context, selector, text): sb = context.sb text = normalize_text(text) sb.select_option_by_text(selector, text) @step("Find '{selector}' and select '{text}' by {option}") @step('Find "{selector}" and select "{text}" by {option}') @step("Find '{selector}' and select \"{text}\" by {option}") @step('Find "{selector}" and select \'{text}\' by {option}') @step("User finds '{selector}' and selects '{text}' by {option}") @step('User finds "{selector}" and selects "{text}" by {option}') @step("User finds '{selector}' and selects \"{text}\" by {option}") @step('User finds "{selector}" and selects \'{text}\' by {option}') def select_option_by_option(context, selector, text, option): sb = context.sb text = normalize_text(text) if option.startswith("'") or option.startswith('"'): option = option[1:] if option.endswith("'") or option.endswith('"'): option = option[:-1] if option == "text": sb.select_option_by_text(selector, text) elif option == "index": sb.select_option_by_index(selector, text) elif option == "value": sb.select_option_by_value(selector, text) else: raise Exception("Unknown option: %s" % option) @step("Wait for '{selector}' to be visible") @step('Wait for "{selector}" to be visible') @step("Wait for element '{selector}'") @step('Wait for element "{selector}"') @step("User waits for '{selector}' to be visible") @step('User waits for "{selector}" to be visible') @step("User waits for element '{selector}'") @step('User waits for element "{selector}"') def wait_for_element(context, selector): sb = context.sb sb.wait_for_element(selector) @step("Wait for text '{text}' in '{selector}'") @step('Wait for text "{text}" in "{selector}"') @step("Wait for text '{text}' in \"{selector}\"") @step('Wait for text "{text}" in \'{selector}\'') @step("Wait for '{selector}' to have text '{text}'") @step('Wait for "{selector}" to have text "{text}"') @step('Wait for "{selector}" to have text \'{text}\'') @step("Wait for '{selector}' to have text \"{text}\"") @step("User waits for text '{text}' in '{selector}'") @step('User waits for text "{text}" in "{selector}"') @step("User waits for text '{text}' in \"{selector}\"") @step('User waits for text "{text}" in \'{selector}\'') @step("User waits for '{selector}' to have text '{text}'") @step('User waits for "{selector}" to have text "{text}"') @step('User waits for "{selector}" to have text \'{text}\'') @step("User waits for '{selector}' to have text \"{text}\"") def wait_for_text_in_element(context, text, selector): sb = context.sb text = normalize_text(text) sb.wait_for_text(text, selector) @step("Wait for exact text '{text}' in '{selector}'") @step('Wait for exact text "{text}" in "{selector}"') @step("Wait for exact text '{text}' in \"{selector}\"") @step('Wait for exact text "{text}" in \'{selector}\'') @step("Wait for '{selector}' to have exact text '{text}'") @step('Wait for "{selector}" to have exact text "{text}"') @step('Wait for "{selector}" to have exact text \'{text}\'') @step("Wait for '{selector}' to have exact text \"{text}\"") @step("User waits for exact text '{text}' in '{selector}'") @step('User waits for exact text "{text}" in "{selector}"') @step("User waits for exact text '{text}' in \"{selector}\"") @step('User waits for exact text "{text}" in \'{selector}\'') @step("User waits for '{selector}' to have exact text '{text}'") @step('User waits for "{selector}" to have exact text "{text}"') @step('User waits for "{selector}" to have exact text \'{text}\'') @step("User waits for '{selector}' to have exact text \"{text}\"") def wait_for_exact_text_in_element(context, text, selector): sb = context.sb text = normalize_text(text) sb.wait_for_exact_text(text, selector) @step("Wait for non-empty text in '{selector}'") @step('Wait for non-empty text in "{selector}"') @step("Wait for '{selector}' to have non-empty text") @step('Wait for "{selector}" to have non-empty text') @step("User waits for non-empty text in '{selector}'") @step('User waits for non-empty text in "{selector}"') @step("User waits for '{selector}' to have non-empty text") @step('User waits for "{selector}" to have non-empty text') @step("Wait for '{selector}' to not have text") @step('Wait for "{selector}" to not have text') @step("Wait for text in '{selector}' to not be empty") @step('Wait for text in "{selector}" to not be empty') @step("User waits for '{selector}' to not have text") @step('User waits for "{selector}" to not have text') @step("User waits for text in '{selector}' to not be empty") @step('User waits for text in "{selector}" to not be empty') def wait_for_non_empty_text_in_element(context, selector): sb = context.sb sb.wait_for_non_empty_text(selector) @step("Wait for text '{text}'") @step('Wait for text "{text}"') @step("User waits for text '{text}'") @step('User waits for text "{text}"') def wait_for_text(context, text): sb = context.sb text = normalize_text(text) sb.wait_for_text(text) @step("Double click '{selector}'") @step('Double click "{selector}"') @step("Double click element '{selector}'") @step('Double click element "{selector}"') @step("User double clicks '{selector}'") @step('User double clicks "{selector}"') @step("User double clicks element '{selector}'") @step('User double clicks element "{selector}"') def double_click_element(context, selector): sb = context.sb sb.double_click(selector) @step("Slow click '{selector}'") @step('Slow click "{selector}"') @step("Slow click element '{selector}'") @step('Slow click element "{selector}"') @step("User slow clicks '{selector}'") @step('User slow clicks "{selector}"') @step("User slow clicks element '{selector}'") @step('User slow clicks element "{selector}"') def slow_click_element(context, selector): sb = context.sb sb.slow_click(selector) @step("Clear text field '{selector}'") @step('Clear text field "{selector}"') @step("Clear text in '{selector}'") @step('Clear text in "{selector}"') @step("User clears text field '{selector}'") @step('User clears text field "{selector}"') @step("User clears text in '{selector}'") @step('User clears text in "{selector}"') def clear_text_field(context, selector): sb = context.sb sb.clear(selector) @step("Maximize window") @step("Maximize the window") @step("User maximizes window") @step("User maximizes the window") def maximize_window(context): sb = context.sb sb.maximize_window() @step("Get new driver") @step("User gets new driver") def get_new_driver(context): sb = context.sb sb.get_new_driver() @step("Switch to default driver") @step("User switches to default driver") def switch_to_default_driver(context): sb = context.sb sb.switch_to_default_driver() @step("Press up arrow") @step("User presses up arrow") def press_up_arrow(context): sb = context.sb sb.press_up_arrow() @step("Press down arrow") @step("User presses down arrow") def press_down_arrow(context): sb = context.sb sb.press_down_arrow() @step("Press left arrow") @step("User presses left arrow") def press_left_arrow(context): sb = context.sb sb.press_left_arrow() @step("Press right arrow") @step("User presses right arrow") def press_right_arrow(context): sb = context.sb sb.press_right_arrow() @step("Clear all cookies") @step("Delete all cookies") @step("User clears all cookies") @step("User deletes all cookies") def delete_all_cookies(context): sb = context.sb sb.delete_all_cookies() @step("Clear Local Storage") @step("Delete Local Storage") @step("User clears Local Storage") @step("User deletes Local Storage") def clear_local_storage(context): sb = context.sb sb.clear_local_storage() @step("Clear Session Storage") @step("Delete Session Storage") @step("User clears Session Storage") @step("User deletes Session Storage") def clear_session_storage(context): sb = context.sb sb.clear_session_storage() @step("JS click all '{selector}'") @step('JS click all "{selector}"') @step("Use JS to click all '{selector}'") @step('Use JS to click all "{selector}"') def js_click_all(context, selector): sb = context.sb sb.js_click_all(selector) @step("Click '{selector}' at ({px},{py})") @step('Click "{selector}" at ({px},{py})') @step("Click '{selector}' at ({px}, {py})") @step('Click "{selector}" at ({px}, {py})') @step("User clicks '{selector}' at ({px},{py})") @step('User clicks "{selector}" at ({px},{py})') @step("User clicks '{selector}' at ({px}, {py})") @step('User clicks "{selector}" at ({px}, {py})') def click_with_offset(context, selector, px, py): sb = context.sb sb.click_with_offset(selector, px, py) @step("In '{selector}' choose file '{file_path}'") @step('In "{selector}" choose file "{file_path}"') @step("In '{selector}' choose file \"{file_path}\"") @step('In "{selector}" choose file \'{file_path}\'') @step("Into '{selector}' choose file '{file_path}'") @step('Into "{selector}" choose file "{file_path}"') @step("Into '{selector}' choose file \"{file_path}\"") @step('Into "{selector}" choose file \'{file_path}\'') @step("User chooses file '{file_path}' for '{selector}'") @step('User chooses file "{file_path}" for "{selector}" ') @step("User chooses file \"{file_path}\" for '{selector}' ") @step('User chooses file \'{file_path}\' for "{selector}" ') def choose_file(context, selector, file_path): sb = context.sb sb.choose_file(selector, file_path) @step("Set content to frame '{frame}'") @step('Set content to frame "{frame}"') @step("User sets content to frame '{frame}'") @step('User sets content to frame "{frame}"') def set_content_to_frame(context, frame): sb = context.sb sb.set_content_to_frame(frame) @step("Set content to default") @step("User sets content to default") def set_content_to_default(context): sb = context.sb sb.set_content_to_default() @step("Set content to parent") @step("User sets content to parent") def set_content_to_parent(context): sb = context.sb sb.set_content_to_parent() @step("Assert element present '{selector}'") @step('Assert element present "{selector}"') @step("Element '{selector}' should be present") @step('Element "{selector}" should be present') def assert_element_present(context, selector): sb = context.sb sb.assert_element_present(selector) @step("Assert element not visible '{selector}'") @step('Assert element not visible "{selector}"') @step("Element '{selector}' should not be visible") @step('Element "{selector}" should not be visible') def assert_element_not_visible(context, selector): sb = context.sb sb.assert_element_not_visible(selector) @step("Assert link text '{text}'") @step('Assert link text "{text}"') @step("Link text '{text}' should be visible") @step('Link text "{text}" should be visible') def assert_link_text(context, text): sb = context.sb text = normalize_text(text) sb.assert_link_text(text) @step("Assert title '{title}'") @step('Assert title "{title}"') @step("The title should be '{title}'") @step('The title should be "{title}"') def assert_title(context, title): sb = context.sb title = normalize_text(title) sb.assert_title(title) @step("Assert downloaded file '{file}'") @step('Assert downloaded file "{file}"') @step("File '{file}' should be in downloads") @step('File "{file}" should be in downloads') def assert_downloaded_file(context, file): sb = context.sb sb.assert_downloaded_file(file) @step("Download '{file}' to downloads") @step('Download "{file}" to downloads') @step("Download file '{file}' to downloads") @step('Download file "{file}" to downloads') @step("User downloads '{file}' to downloads") @step('User downloads "{file}" to downloads') def download_file(context, file): sb = context.sb sb.download_file(file) @step("Download '{file}' to '{destination}'") @step('Download "{file}" to "{destination}"') @step("Download file '{file}' to '{destination}'") @step('Download file "{file}" to "{destination}"') @step("User downloads '{file}' to '{destination}'") @step('User downloads "{file}" to "{destination}"') def download_file_to_destination(context, file, destination): sb = context.sb sb.download_file(file, destination) @step("In '{selector}' assert attribute \'{attribute}\'") @step('In "{selector}" assert attribute \"{attribute}\"') @step("In \"{selector}\" assert attribute '{attribute}'") @step('In \'{selector}\' assert attribute "{attribute}"') def assert_attribute(context, selector, attribute): sb = context.sb sb.assert_attribute(selector, attribute) @step("In '{selector}' assert attribute/value '{attribute}'/'{value}'") @step('In "{selector}" assert attribute/value "{attribute}"/"{value}"') @step("In \"{selector}\" assert attribute/value '{attribute}'/\"{value}\"") @step('In \'{selector}\' assert attribute/value "{attribute}"/\'{value}\'') @step("In '{selector}' assert attribute/value '{attribute}'/\"{value}\"") @step('In "{selector}" assert attribute/value "{attribute}"/\'{value}\'') @step("In \"{selector}\" assert attribute/value '{attribute}'/'{value}'") @step('In \'{selector}\' assert attribute/value "{attribute}"/"{value}"') def assert_attribute_has_value(context, selector, attribute, value): sb = context.sb value = normalize_text(value) sb.assert_attribute(selector, attribute, value) @step("Show file choosers") @step("Show hidden file choosers") @step("Use JS to show file choosers") @step("Use JS to show hidden file choosers") def show_file_choosers(context): sb = context.sb sb.show_file_choosers() @step("Sleep for {seconds} seconds") @step("Wait for {seconds} seconds") @step("User sleeps for {seconds} seconds") @step("User waits for {seconds} seconds") def sleep(context, seconds): sb = context.sb sb.sleep(float(seconds)) @step("Activate Demo Mode") @step("User activates Demo Mode") def activate_demo_mode(context): sb = context.sb sb.activate_demo_mode() @step("Deactivate Demo Mode") @step("User deactivates Demo Mode") def deactivate_demo_mode(context): sb = context.sb sb.deactivate_demo_mode() @step("Deferred assert element '{selector}'") @step('Deferred assert element "{selector}"') def deferred_assert_element(context, selector): sb = context.sb sb.deferred_assert_element(selector) @step("Deferred assert element present '{selector}'") @step('Deferred assert element present "{selector}"') def deferred_assert_element_present(context, selector): sb = context.sb sb.deferred_assert_element_present(selector) @step("Deferred assert text '{text}' in '{selector}'") @step('Deferred assert text "{text}" in "{selector}"') @step("Deferred assert text '{text}' in \"{selector}\"") @step('Deferred assert text "{text}" in \'{selector}\'') def deferred_assert_text_in_element(context, text, selector): sb = context.sb text = normalize_text(text) sb.deferred_assert_text(text, selector) @step("Deferred assert text '{text}'") @step('Deferred assert text "{text}"') def deferred_assert_text(context, text): sb = context.sb text = normalize_text(text) sb.deferred_assert_text(text) @step("Deferred assert exact text '{text}' in '{selector}'") @step('Deferred assert exact text "{text}" in "{selector}"') @step("Deferred assert exact text '{text}' in \"{selector}\"") @step('Deferred assert exact text "{text}" in \'{selector}\'') def deferred_assert_exact_text(context, text, selector): sb = context.sb text = normalize_text(text) sb.deferred_assert_exact_text(text, selector) @step("Deferred assert non-empty text in '{selector}'") @step('Deferred assert non-empty text in "{selector}"') def deferred_assert_non_empty_text(context, selector): sb = context.sb sb.deferred_assert_non_empty_text(selector) @step("Process deferred asserts") def process_deferred_asserts(context): sb = context.sb sb.process_deferred_asserts() @step("Assert text not visible '{text}' in '{selector}'") @step('Assert text not visible "{text}" in "{selector}"') @step("Assert text not visible '{text}' in \"{selector}\"") @step('Assert text not visible "{text}" in \'{selector}\'') @step("Text '{text}' should not be visible in '{selector}'") @step('Text "{text}" should not be visible in "{selector}"') @step("Text '{text}' should not be visible in \"{selector}\"") @step('Text "{text}" should not be visible in \'{selector}\'') def assert_text_not_visible_in_element(context, text, selector): sb = context.sb text = normalize_text(text) sb.assert_text_not_visible(text, selector) @step("Assert text not visible '{text}'") @step('Assert text not visible "{text}"') @step("Text '{text}' should not be visible") @step('Text "{text}" should not be visible') def assert_text_not_visible(context, text): sb = context.sb text = normalize_text(text) sb.assert_text_not_visible(text) @step("Assert exact text not visible '{text}' in '{selector}'") @step('Assert exact text not visible "{text}" in "{selector}"') @step("Assert exact text not visible '{text}' in \"{selector}\"") @step('Assert exact text not visible "{text}" in \'{selector}\'') @step("Exact text '{text}' should not be visible in '{selector}'") @step('Exact text "{text}" should not be visible in "{selector}"') @step("Exact text '{text}' should not be visible in \"{selector}\"") @step('Exact text "{text}" should not be visible in \'{selector}\'') def assert_exact_text_not_visible_in_element(context, text, selector): sb = context.sb text = normalize_text(text) sb.assert_exact_text_not_visible(text, selector) @step("Assert exact text not visible '{text}'") @step('Assert exact text not visible "{text}"') @step("Exact text '{text}' should not be visible") @step('Exact text "{text}" should not be visible') def assert_exact_text_not_visible(context, text): sb = context.sb text = normalize_text(text) sb.assert_exact_text_not_visible(text) @step("Assert title contains '{substring}'") @step('Assert title contains "{substring}"') @step("The title should contain '{substring}'") @step('The title should contain "{substring}"') def assert_title_contains(context, substring): sb = context.sb substring = normalize_text(substring) sb.assert_title_contains(substring) @step("Open new tab") @step("Open new window") @step("User opens new tab") @step("User opens new window") def open_new_window(context): sb = context.sb sb.open_new_window() @step("Accept alert") @step("User accepts alert") def accept_alert(context): sb = context.sb sb.accept_alert() @step("Dismiss alert") @step("User dismisses alert") def dismiss_alert(context): sb = context.sb sb.dismiss_alert() @step("Assert URL '{url}'") @step('Assert URL "{url}"') @step("The URL should be '{url}'") @step('The URL should be "{url}"') def assert_url(context, url): sb = context.sb url = normalize_text(url) sb.assert_url(url) @step("Assert URL contains '{substring}'") @step('Assert URL contains "{substring}"') @step("The URL should contain '{substring}'") @step('The URL should contain "{substring}"') def assert_url_contains(context, substring): sb = context.sb substring = normalize_text(substring) sb.assert_url_contains(substring) @step("Hover '{selector}'") @step('Hover "{selector}"') @step("Hover over '{selector}'") @step('Hover over "{selector}"') @step("Hover element '{selector}'") @step('Hover element "{selector}"') @step("User hovers over '{selector}'") @step('User hovers over "{selector}"') @step("User hovers over element '{selector}'") @step('User hovers over element "{selector}"') def hover(context, selector): sb = context.sb sb.hover(selector) @step("Context click '{selector}'") @step('Context click "{selector}"') @step("Context click element '{selector}'") @step('Context click element "{selector}"') @step("Right click '{selector}'") @step('Right click "{selector}"') @step("Right click element '{selector}'") @step('Right click element "{selector}"') @step("User right clicks '{selector}'") @step('User right clicks "{selector}"') @step("User right clicks element '{selector}'") @step('User right clicks element "{selector}"') def context_click(context, selector): sb = context.sb sb.context_click(selector) @step("JS type '{text}' in '{selector}'") @step('JS type "{text}" in "{selector}"') @step("JS type '{text}' in \"{selector}\"") @step('JS type "{text}" in \'{selector}\'') @step("JS type '{text}' into '{selector}'") @step('JS type "{text}" into "{selector}"') @step("JS type '{text}' into \"{selector}\"") @step('JS type "{text}" into \'{selector}\'') @step("JS type text '{text}' in '{selector}'") @step('JS type text "{text}" in "{selector}"') @step("JS type text '{text}' in \"{selector}\"") @step('JS type text "{text}" in \'{selector}\'') @step("JS type text '{text}' into '{selector}'") @step('JS type text "{text}" into "{selector}"') @step("JS type text '{text}' into \"{selector}\"") @step('JS type text "{text}" into \'{selector}\'') @step("Use JS to type '{text}' in '{selector}'") @step('Use JS to type "{text}" in "{selector}"') @step("Use JS to type '{text}' in \"{selector}\"") @step('Use JS to type "{text}" in \'{selector}\'') @step("Use JS to type '{text}' into '{selector}'") @step('Use JS to type "{text}" into "{selector}"') @step("Use JS to type '{text}' into \"{selector}\"") @step('Use JS to type "{text}" into \'{selector}\'') def js_type(context, text, selector): sb = context.sb text = normalize_text(text) sb.js_type(selector, text) @step("jQuery click '{selector}'") @step('jQuery click "{selector}"') @step("jQuery click element '{selector}'") @step('jQuery click element "{selector}"') @step("Use jQuery to click '{selector}'") @step('Use jQuery to click "{selector}"') def jquery_click(context, selector): sb = context.sb sb.jquery_click(selector) @step("jQuery click all '{selector}'") @step('jQuery click all "{selector}"') @step("Use jQuery to click all '{selector}'") @step('Use jQuery to click all "{selector}"') def jquery_click_all(context, selector): sb = context.sb sb.jquery_click_all(selector) @step("jQuery type '{text}' in '{selector}'") @step('jQuery type "{text}" in "{selector}"') @step("jQuery type '{text}' in \"{selector}\"") @step('jQuery type "{text}" in \'{selector}\'') @step("jQuery type '{text}' into '{selector}'") @step('jQuery type "{text}" into "{selector}"') @step("jQuery type '{text}' into \"{selector}\"") @step('jQuery type "{text}" into \'{selector}\'') @step("jQuery type text '{text}' in '{selector}'") @step('jQuery type text "{text}" in "{selector}"') @step("jQuery type text '{text}' in \"{selector}\"") @step('jQuery type text "{text}" in \'{selector}\'') @step("jQuery type text '{text}' into '{selector}'") @step('jQuery type text "{text}" into "{selector}"') @step("jQuery type text '{text}' into \"{selector}\"") @step('jQuery type text "{text}" into \'{selector}\'') @step("Use jQuery to type '{text}' in '{selector}'") @step('Use jQuery to type "{text}" in "{selector}"') @step("Use jQuery to type '{text}' in \"{selector}\"") @step('Use jQuery to type "{text}" in \'{selector}\'') @step("Use jQuery to type '{text}' into '{selector}'") @step('Use jQuery to type "{text}" into "{selector}"') @step("Use jQuery to type '{text}' into \"{selector}\"") @step('Use jQuery to type "{text}" into \'{selector}\'') def jquery_type(context, text, selector): sb = context.sb text = normalize_text(text) sb.jquery_type(selector, text) @step("Press keys '{text}' in '{selector}'") @step('Press keys "{text}" in "{selector}"') @step("Press keys '{text}' in \"{selector}\"") @step('Press keys "{text}" in \'{selector}\'') @step("Press keys '{text}' into '{selector}'") @step('Press keys "{text}" into "{selector}"') @step("Press keys '{text}' into \"{selector}\"") @step('Press keys "{text}" into \'{selector}\'') @step("In '{selector}' press keys '{text}'") @step('In "{selector}" press keys "{text}"') @step("In '{selector}' press keys \"{text}\"") @step('In "{selector}" press keys \'{text}\'') @step("Into '{selector}' press keys '{text}'") @step('Into "{selector}" press keys "{text}"') @step("Into '{selector}' press keys \"{text}\"") @step('Into "{selector}" press keys \'{text}\'') @step("Find '{selector}' and press keys '{text}'") @step('Find "{selector}" and press keys "{text}"') @step("Find '{selector}' and press keys \"{text}\"") @step('Find "{selector}" and press keys \'{text}\'') @step("User presses keys '{text}' in '{selector}'") @step('User presses keys "{text}" in "{selector}"') @step("User presses keys '{text}' in \"{selector}\"") @step('User presses keys "{text}" in \'{selector}\'') @step("User presses keys '{text}' into '{selector}'") @step('User presses keys "{text}" into "{selector}"') @step("User presses keys '{text}' into \"{selector}\"") @step('User presses keys "{text}" into \'{selector}\'') def press_keys(context, text, selector): sb = context.sb text = normalize_text(text) sb.press_keys(selector, text) @step("Find '{selector}' and set {attribute} to '{value}'") @step('Find "{selector}" and set {attribute} to "{value}"') @step("Find '{selector}' and set {attribute} to \"{value}\"") @step('Find "{selector}" and set {attribute} to \'{value}\'') def set_attribute(context, selector, attribute, value): sb = context.sb value = normalize_text(value) if attribute.startswith("'") or attribute.startswith('"'): attribute = attribute[1:] if attribute.endswith("'") or attribute.endswith('"'): attribute = attribute[:-1] sb.set_attribute(selector, attribute, value) @step("Find all '{selector}' and set {attribute} to '{value}'") @step('Find all "{selector}" and set {attribute} to "{value}"') @step("Find all '{selector}' and set {attribute} to \"{value}\"") @step('Find all "{selector}" and set {attribute} to \'{value}\'') def set_attributes(context, selector, attribute, value): sb = context.sb value = normalize_text(value) if attribute.startswith("'") or attribute.startswith('"'): attribute = attribute[1:] if attribute.endswith("'") or attribute.endswith('"'): attribute = attribute[:-1] sb.set_attributes(selector, attribute, value) @step("Save as PDF to logs") @step("Save as PDF to the logs") @step("User saves page as PDF to logs") @step("User saves page as PDF to the logs") def save_as_pdf_to_logs(context): sb = context.sb sb.save_as_pdf_to_logs() @step("Save page source to logs") @step("Save the page source to the logs") @step("User saves page source to logs") @step("User saves the page source to the logs") def save_page_source_to_logs(context): sb = context.sb sb.save_page_source_to_logs() @step("Activate CDP Mode") @step("User activates CDP Mode") def activate_cdp_mode(context): sb = context.sb sb.activate_cdp_mode() ================================================ FILE: seleniumbase/ReadMe.md ================================================ SeleniumBase

      Framework Folders

      * [fixtures](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/fixtures): Includes [base_case.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py), where SeleniumBase test methods are defined. * [core](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/core): Includes [browser_launcher.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/core/browser_launcher.py), which is used for spinning up browsers for tests. * [plugins](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/plugins): Includes [pytest_plugin.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/plugins/pytest_plugin.py), which is used to add pytest command-line options. * [console_scripts](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/console_scripts): Includes [run.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/run.py), which is used to call SeleniumBase console scripts. * [drivers](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/drivers): This is the folder where web drivers get downloaded when installing them. * [config](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/config): Includes [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py), which provides default configuration options for tests. * [js_code](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/js_code): This folder contains JavaScript code for various SeleniumBase components. * [undetected](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/undetected): This folder contains code for preventing sites from detecting Selenium. * [extensions](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/extensions): This folder contains Chromium extensions that can be used by tests. * [common](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/common): This folder contains Python decorators that can be used by tests. * [utilities](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/utilities): This folder contains code for spinning up your own Selenium Grid. * [resources](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/resources): This folder contains copies of JavaScript resources used by tests. * [translate](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/translate): This folder contains code for translating tests into different languages. * [behave](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/behave): This folder contains code for integrating with the Behave BDD test runner. * [masterqa](https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/masterqa): MasterQA is a tool for combining automation with manual verification. --------
      SeleniumBase
      ================================================ FILE: seleniumbase/__init__.py ================================================ import collections import os import pdb import sys from contextlib import suppress from selenium import webdriver from seleniumbase.__version__ import __version__ from seleniumbase.common import decorators # noqa from seleniumbase.common import encryption # noqa from seleniumbase.core import colored_traceback from seleniumbase.core import sb_cdp # noqa from seleniumbase.core.browser_launcher import get_driver # noqa from seleniumbase.fixtures import js_utils # noqa from seleniumbase.fixtures import page_actions # noqa from seleniumbase.fixtures import page_utils # noqa from seleniumbase.fixtures import shared_utils from seleniumbase.fixtures.base_case import BaseCase # noqa from seleniumbase.masterqa.master_qa import MasterQA # noqa from seleniumbase.plugins.sb_manager import SB # noqa from seleniumbase.plugins.driver_manager import Driver # noqa from seleniumbase.plugins.driver_manager import DriverContext # noqa from seleniumbase.undetected import cdp_driver # noqa from seleniumbase import translate # noqa with suppress(Exception): import colorama with suppress(Exception): import pdbp # (Pdb+) --- Python Debugger Plus with suppress(Exception): shared_utils.fix_colorama_if_windows() colorama.init(autoreset=True) if sys.version_info[0] < 3 and "pdbp" in locals(): # With Python3, "import pdbp" is all you need for key in pdbp.__dict__.keys(): # Replace pdb with pdbp pdb.__dict__[key] = pdbp.__dict__[key] if hasattr(pdb, "DefaultConfig"): # Here's how to customize Pdb+ options pdb.DefaultConfig.filename_color = pdb.Color.fuchsia pdb.DefaultConfig.line_number_color = pdb.Color.turquoise pdb.DefaultConfig.truncate_long_lines = False pdb.DefaultConfig.sticky_by_default = True colored_traceback.add_hook() os.environ["SE_AVOID_STATS"] = "true" # Disable Selenium Manager stats webdriver.TouchActions = None # Lifeline for past selenium-wire versions if sys.version_info >= (3, 10): collections.Callable = collections.abc.Callable # Lifeline for nosetests del collections # Undo "import collections" / Simplify "dir(seleniumbase)" del os # Undo "import os" / Simplify "dir(seleniumbase)" del sys # Undo "import sys" / Simplify "dir(seleniumbase)" del webdriver # Undo "import webdriver" / Simplify "dir(seleniumbase)" version_list = [int(i) for i in __version__.split(".") if i.isdigit()] version_tuple = tuple(version_list) version_info = version_tuple # noqa ================================================ FILE: seleniumbase/__main__.py ================================================ import os import sys # Remove "" and current working directory from the first entry # of sys.path (if present) to avoid using the current directory # in SeleniumBase commands when invoked as "python -m seleniumbase " if sys.path[0] in ("", os.getcwd()): sys.path.pop(0) if __package__ == "": path = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, path) if __name__ == "__main__": import warnings from seleniumbase.console_scripts.run import main warnings.filterwarnings( "ignore", category=DeprecationWarning, module=".*packaging\\.version" ) main() sys.exit() ================================================ FILE: seleniumbase/__version__.py ================================================ # seleniumbase package __version__ = "4.47.4" ================================================ FILE: seleniumbase/behave/__init__.py ================================================ ================================================ FILE: seleniumbase/behave/behave_helper.py ================================================ """Generating Gherkin-formatted code from the Recorder.""" def generate_gherkin(srt_actions): sb_actions = [] for action in srt_actions: if action[0] == "begin" or action[0] == "_url_": if "%" in action[2]: try: from urllib.parse import unquote action[2] = unquote(action[2], errors="strict") except Exception: pass if '"' not in action[2]: sb_actions.append('Open "%s"' % action[2]) elif "'" not in action[2]: sb_actions.append("Open '%s'" % action[2]) else: sb_actions.append('Open "%s"' % action[2].replace('"', '\\"')) elif action[0] == "f_url": if "%" in action[2]: try: from urllib.parse import unquote action[2] = unquote(action[2], errors="strict") except Exception: pass if '"' not in action[2]: sb_actions.append('Open if not "%s"' % action[2]) elif "'" not in action[2]: sb_actions.append("Open if not '%s'" % action[2]) else: sb_actions.append( 'Open if not "%s"' % action[2].replace('"', '\\"') ) elif action[0] == "click": if '"' not in action[1]: sb_actions.append('Click "%s"' % action[1]) else: sb_actions.append("Click '%s'" % action[1]) elif action[0] == "dbclk": if '"' not in action[1]: sb_actions.append('Double click "%s"' % action[1]) else: sb_actions.append("Double click '%s'" % action[1]) elif action[0] == "js_cl": if '"' not in action[1]: sb_actions.append('JS click "%s"' % action[1]) else: sb_actions.append("JS click '%s'" % action[1]) elif action[0] == "js_ca": if '"' not in action[1]: sb_actions.append('JS click all "%s"' % action[1]) else: sb_actions.append("JS click all '%s'" % action[1]) elif action[0] == "jq_cl": if '"' not in action[1]: sb_actions.append('jQuery click "%s"' % action[1]) else: sb_actions.append("jQuery click '%s'" % action[1]) elif action[0] == "jq_ca": if '"' not in action[1]: sb_actions.append('jQuery click all "%s"' % action[1]) else: sb_actions.append("jQuery click all '%s'" % action[1]) elif action[0] == "r_clk": if '"' not in action[1]: sb_actions.append('Context click "%s"' % action[1]) else: sb_actions.append("Context click '%s'" % action[1]) elif action[0] == "canva": selector = action[1][0] p_x = action[1][1] p_y = action[1][2] if '"' not in selector: sb_actions.append( 'Click "%s" at (%s, %s)' % (selector, p_x, p_y) ) else: sb_actions.append( "Click '%s' at (%s, %s)" % (selector, p_x, p_y) ) elif action[0] == "input": text = action[2].replace("\n", "\\n") if '"' not in text and '"' not in action[1]: sb_actions.append('Type "%s" into "%s"' % (text, action[1])) elif '"' in text and '"' not in action[1]: sb_actions.append('Type \'%s\' into "%s"' % (text, action[1])) elif '"' not in text and '"' in action[1]: sb_actions.append('Type "%s" into \'%s\'' % (text, action[1])) elif '"' in text and '"' in action[1]: sb_actions.append("Type '%s' into '%s'" % (text, action[1])) elif action[0] == "js_ty": text = action[2].replace("\n", "\\n") if '"' not in text and '"' not in action[1]: sb_actions.append('JS type "%s" in "%s"' % (text, action[1])) elif '"' in text and '"' not in action[1]: sb_actions.append('JS type \'%s\' in "%s"' % (text, action[1])) elif '"' not in text and '"' in action[1]: sb_actions.append('JS type "%s" in \'%s\'' % (text, action[1])) elif '"' in text and '"' in action[1]: sb_actions.append("JS type '%s' in '%s'" % (text, action[1])) elif action[0] == "jq_ty": text = action[2].replace("\n", "\\n") if '"' not in text and '"' not in action[1]: sb_actions.append( 'jQuery type "%s" in "%s"' % (text, action[1]) ) elif '"' in text and '"' not in action[1]: sb_actions.append( 'jQuery type \'%s\' in "%s"' % (text, action[1]) ) elif '"' not in text and '"' in action[1]: sb_actions.append( 'jQuery type "%s" in \'%s\'' % (text, action[1]) ) elif '"' in text and '"' in action[1]: sb_actions.append( "jQuery type '%s' in '%s'" % (text, action[1]) ) elif action[0] == "pkeys": text = action[2].replace("\n", "\\n") if '"' not in text and '"' not in action[1]: sb_actions.append( 'Press keys "%s" in "%s"' % (text, action[1]) ) elif '"' in text and '"' not in action[1]: sb_actions.append( 'Press keys \'%s\' in "%s"' % (text, action[1]) ) elif '"' not in text and '"' in action[1]: sb_actions.append( 'Press keys "%s" in \'%s\'' % (text, action[1]) ) elif '"' in text and '"' in action[1]: sb_actions.append( "Press keys '%s' in '%s'" % (text, action[1]) ) elif action[0] == "hover": if '"' not in action[1]: sb_actions.append('Hover "%s"' % action[1]) else: sb_actions.append("Hover '%s'" % action[1]) elif action[0] == "e_mfa": text = action[2].replace("\n", "\\n") if '"' not in action[1] and '"' not in text: sb_actions.append('Into "%s" do MFA "%s"' % (action[1], text)) elif '"' not in action[1] and '"' in text: sb_actions.append( 'Into "%s" do MFA \'%s\'' % (action[1], text) ) elif '"' in action[1] and '"' not in text: sb_actions.append( 'Into \'%s\' do MFA "%s"' % (action[1], text) ) elif '"' in action[1] and '"' in text: sb_actions.append("Into '%s' do MFA '%s'" % (action[1], text)) elif action[0] == "h_clk": if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'Hover "%s" and click "%s"' % (action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'Hover "%s" and click \'%s\'' % (action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'Hover \'%s\' and click "%s"' % (action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "Hover '%s' and click '%s'" % (action[1], action[2]) ) elif action[0] == "ddrop": if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'Drag "%s" into "%s"' % (action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'Drag "%s" into \'%s\'' % (action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'Drag \'%s\' into "%s"' % (action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "Drag '%s' into '%s'" % (action[1], action[2]) ) elif action[0] == "s_opt": if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'Find "%s" and select "%s"' % (action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'Find "%s" and select \'%s\'' % (action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'Find \'%s\' and select "%s"' % (action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "Find '%s' and select '%s'" % (action[1], action[2]) ) elif action[0] == "set_v": if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'Set value of "%s" to "%s"' % (action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'Set value of "%s" to \'%s\'' % (action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'Set value of \'%s\' to "%s"' % (action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "Set value of '%s' to '%s'" % (action[1], action[2]) ) elif action[0] == "cho_f": action[2] = action[2].replace("\\", "\\\\") if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'Into "%s" choose file "%s"' % (action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'Into "%s" choose file \'%s\'' % (action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'Into \'%s\' choose file "%s"' % (action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "Into '%s' choose file '%s'" % (action[1], action[2]) ) elif action[0] == "sw_fr": method = "Switch to frame" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "sw_dc": sb_actions.append("Switch to default content") elif action[0] == "sw_pf": sb_actions.append("Switch to parent frame") elif action[0] == "s_c_f": method = "Set content to frame" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "s_c_d": nested = action[1] if nested: sb_actions.append("Set content to parent") else: sb_actions.append("Set content to default") elif action[0] == "sleep": sb_actions.append("Sleep for %s seconds" % action[1]) elif action[0] == "wf_el": method = "Wait for element" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "as_el": method = "Assert element" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "as_ep": method = "Assert element present" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "asenv": method = "Assert element not visible" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "s_at_" or action[0] == "s_ats": start = "Find" if action[0] == "s_ats": start = "Find all" if '"' not in action[1][0]: sb_actions.append( '%s "%s" and set %s to "%s"' % (start, action[1][0], action[1][1], action[1][2]) ) elif "'" not in action[1][0]: sb_actions.append( "%s '%s' and set %s to \"%s\"" % (start, action[1][0], action[1][1], action[1][2]) ) else: sb_actions.append( '%s "%s" and set %s to "%s")' % ( start.replace('"', '\\"'), action[1][0], action[1][1], action[1][2] ) ) elif action[0] == "acc_a": sb_actions.append("Accept alert") elif action[0] == "dis_a": sb_actions.append("Dismiss alert") elif action[0] == "hi_li": method = "Highlight" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "as_lt": method = "Assert link text" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "as_ti": method = "Assert title" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "as_tc": method = "Assert title contains" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "a_url": method = "Assert URL" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "a_u_c": method = "Assert URL contains" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "as_df": method = "Assert downloaded file" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "do_fi": method = "Download file" file_url = action[1][0] dest = action[1][1] if not dest: sb_actions.append('%s "%s" to downloads' % (method, file_url)) else: sb_actions.append('%s "%s" to "%s"' % (method, file_url, dest)) elif action[0] == "as_at": if ('"' not in action[1][0]) and action[1][2]: sb_actions.append( 'In "%s" assert attribute/value "%s"/"%s"' % (action[1][0], action[1][1], action[1][2]) ) elif ('"' not in action[1][0]) and not action[1][2]: sb_actions.append( 'In "%s" assert attribute "%s"' % (action[1][0], action[1][1]) ) elif ('"' in action[1][0]) and action[1][2]: sb_actions.append( 'In \'%s\' assert attribute/value "%s"/"%s"' % (action[1][0], action[1][1], action[1][2]) ) else: sb_actions.append( 'In \'%s\' assert attribute "%s"' % (action[1][0], action[1][1]) ) elif ( action[0] == "as_te" or action[0] == "as_et" or action[0] == "astnv" or action[0] == "aetnv" or action[0] == "da_te" or action[0] == "da_et" ): import unicodedata action[1][0] = unicodedata.normalize("NFKC", action[1][0]) action[1][0] = action[1][0].replace("\n", "\\n") action[1][0] = action[1][0].replace("\u00B6", "") method = "Assert text" if action[0] == "as_et": method = "Assert exact text" elif action[0] == "astnv": method = "Assert text not visible" elif action[0] == "aetnv": method = "Assert exact text not visible" elif action[0] == "da_te": method = "Deferred assert text" elif action[0] == "da_et": method = "Deferred assert exact text" if action[1][1] != "html": if '"' not in action[1][0] and '"' not in action[1][1]: sb_actions.append( '%s "%s" in "%s"' % (method, action[1][0], action[1][1]) ) elif '"' not in action[1][0] and '"' in action[1][1]: sb_actions.append( '%s "%s" in \'%s\'' % (method, action[1][0], action[1][1]) ) elif '"' in action[1] and '"' not in action[1][1]: sb_actions.append( '%s \'%s\' in "%s"' % (method, action[1][0], action[1][1]) ) elif '"' in action[1] and '"' in action[1][1]: sb_actions.append( "%s '%s' in '%s'" % (method, action[1][0], action[1][1]) ) else: if '"' not in action[1][0]: sb_actions.append('%s "%s"' % (method, action[1][0])) else: sb_actions.append("%s '%s'" % (method, action[1][0])) elif action[0] == "asnet": method = "Assert non-empty text in" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "da_el": method = "Deferred assert element" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "da_ep": method = "Deferred assert element present" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "danet": method = "Deferred assert non-empty text in" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("%s '%s'" % (method, action[1])) else: sb_actions.append( "%s '%s'" % (method, action[1].replace("'", "\\'")) ) elif action[0] == "s_scr": method = "Save screenshot as" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) elif action[0] == "ss_tf": if '"' not in action[2] and '"' not in action[1]: sb_actions.append( 'Save screenshot to "%s" as "%s"' % (action[2], action[1]) ) elif '"' not in action[2] and '"' in action[1]: sb_actions.append( 'Save screenshot to "%s" as \'%s\'' % (action[2], action[1]) ) elif '"' in action[2] and '"' not in action[1]: sb_actions.append( 'Save screenshot to \'%s\' as "%s"' % (action[2], action[1]) ) elif '"' in action[2] and '"' in action[1]: sb_actions.append( "Save screenshot to '%s' as '%s'" % (action[2], action[1]) ) elif action[0] == "ss_tl": sb_actions.append("Save screenshot to logs") elif action[0] == "pdftl": sb_actions.append("Save as PDF to logs") elif action[0] == "spstl": sb_actions.append("Save page source to logs") elif action[0] == "sh_fc": sb_actions.append("Show file choosers") elif action[0] == "pr_da": sb_actions.append("Process deferred asserts") elif action[0] == "a_d_m": sb_actions.append("Activate Demo Mode") elif action[0] == "d_d_m": sb_actions.append("Deactivate Demo Mode") elif action[0] == "c_l_s": sb_actions.append("Clear Local Storage") elif action[0] == "c_s_s": sb_actions.append("Clear Session Storage") elif action[0] == "d_a_c": sb_actions.append("Delete all cookies") elif action[0] == "go_bk": sb_actions.append("Go back") elif action[0] == "go_fw": sb_actions.append("Go forward") elif action[0] == "c_box": method = "Check if unchecked" if action[2] == "no": method = "Uncheck if checked" if '"' not in action[1]: sb_actions.append('%s "%s"' % (method, action[1])) else: sb_actions.append("%s '%s'" % (method, action[1])) return sb_actions ================================================ FILE: seleniumbase/behave/behave_sb.py ================================================ """ The SeleniumBase-Behave Connector configures command-line options. ****************************************************************** Examples: behave -D browser=edge -D dashboard -D headless behave -D rs -D dashboard behave -D agent="User Agent String" -D demo **************************************************************** -D browser=BROWSER (The web browser to use. Default: "chrome".) -D chrome (Shortcut for "-D browser=chrome". Default.) -D edge (Shortcut for "-D browser=edge".) -D firefox (Shortcut for "-D browser=firefox".) -D safari (Shortcut for "-D browser=safari".) -D cft (Shortcut for using `Chrome for Testing`) -D chs (Shortcut for using `Chrome-Headless-Shell`) -D settings-file=FILE (Override default SeleniumBase settings.) -D env=ENV (Set the test env. Access with "self.env" in tests.) -D account=STR (Set account. Access with "self.account" in tests.) -D data=STRING (Extra test data. Access with "self.data" in tests.) -D var1=STRING (Extra test data. Access with "self.var1" in tests.) -D var2=STRING (Extra test data. Access with "self.var2" in tests.) -D var3=STRING (Extra test data. Access with "self.var3" in tests.) -D variables=DICT (A test var dict. Access with "self.variables".) -D user-data-dir=DIR (Set the Chrome user data directory to use.) -D protocol=PROTOCOL (The Selenium Grid protocol: http|https.) -D server=SERVER (The Selenium Grid server/IP used for tests.) -D port=PORT (The Selenium Grid port used by the test server.) -D cap-file=FILE (The web browser's desired capabilities to use.) -D cap-string=STRING (The web browser's desired capabilities to use.) -D proxy=SERVER:PORT (Connect to a proxy server:port as tests are running) -D proxy=USERNAME:PASSWORD@SERVER:PORT (Use an authenticated proxy server) -D proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com") -D proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.) -D proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.) -D multi-proxy (Allow multiple authenticated proxies when multi-threaded.) -D agent=STRING (Modify the web browser's User-Agent string.) -D mobile (Use the mobile device emulator while running tests.) -D metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".) -D chromium-arg=ARG (Add a Chromium arg for Chrome/Edge, comma-separated.) -D firefox-arg=ARG (Add a Firefox arg for Firefox, comma-separated.) -D firefox-pref=SET (Set a Firefox preference:value set, comma-separated.) -D extension-zip=ZIP (Load a Chrome Extension .zip|.crx, comma-separated.) -D extension-dir=DIR (Load a Chrome Extension directory, comma-separated.) -D binary-location=PATH (Set path of the Chromium browser binary to use.) -D driver-version=VER (Set the chromedriver or uc_driver version to use.) -D sjw (Skip JS Waits for readyState to be "complete" or Angular to load.) -D wfa (Wait for AngularJS to be done loading after specific web actions.) -D pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) -D headless (The default headless mode. Linux uses this mode by default.) -D headless1 (Use Chrome's old headless mode. Fast, but has limitations.) -D headless2 (Use Chrome's new headless mode, which supports extensions.) -D headed (Run tests in headed/GUI mode on Linux OS, where not default.) -D xvfb (Run tests using the Xvfb virtual display server on Linux OS.) -D xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".) -D locale=LOCALE_CODE (Set the Language Locale Code for the web browser.) -D pdb (Activate Post Mortem Debug Mode if a test fails.) -D interval=SECONDS (The autoplay interval for presentations & tour steps) -D start-page=URL (The starting URL for the web browser when tests begin.) -D archive-logs (Archive existing log files instead of deleting them.) -D archive-downloads (Archive old downloads instead of deleting them.) -D time-limit=SECONDS (Safely fail any test that exceeds the time limit.) -D slow (Slow down the automation. Faster than using Demo Mode.) -D demo (Slow down and visually see test actions as they occur.) -D demo-sleep=SECONDS (Set the wait time after Slow & Demo Mode actions.) -D highlights=NUM (Number of highlight animations for Demo Mode actions.) -D message-duration=SECONDS (The time length for Messenger alerts.) -D check-js (Check for JavaScript errors after page loads.) -D ad-block (Block some types of display ads from loading.) -D block-images (Block images from loading during tests.) -D do-not-track (Indicate to websites that you don't want to be tracked.) -D verify-delay=SECONDS (The delay before MasterQA verification checks.) -D recorder (Enables the Recorder for turning browser actions into code.) -D rec-behave (Same as Recorder Mode, but also generates behave-gherkin.) -D rec-sleep (If the Recorder is enabled, also records self.sleep calls.) -D rec-print (If the Recorder is enabled, prints output after tests end.) -D disable-cookies (Disable Cookies on websites. Pages might break!) -D disable-js (Disable JavaScript on websites. Pages might break!) -D disable-csp (Disable the Content Security Policy of websites.) -D disable-ws (Disable Web Security on Chromium-based browsers.) -D enable-ws (Enable Web Security on Chromium-based browsers.) -D enable-sync (Enable "Chrome Sync".) -D uc | -D undetected (Use undetected-chromedriver to evade bot-detection) -D uc-cdp-events (Capture CDP events when running in "-D undetected" mode) -D log-cdp ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) -D remote-debug (Sync to Chrome Remote Debugger chrome://inspect/#devices) -D dashboard (Enable the SeleniumBase Dashboard. Saved at: dashboard.html) -D dash-title=STRING (Set the title shown for the generated dashboard.) -D enable-3d-apis (Enables WebGL and 3D APIs.) -D swiftshader (Use Chrome's SwiftShader Graphics Library.) -D incognito (Enable Chrome's Incognito mode.) -D guest (Enable Chrome's Guest mode.) -D dark (Enable Chrome's Dark mode.) -D devtools (Open Chrome's DevTools when the browser opens.) -D rs | -D reuse-session (Reuse browser session for all tests.) -D rcs | -D reuse-class-session (Reuse session for tests in class/feature) -D crumbs (Delete all cookies between tests reusing a session.) -D disable-beforeunload (Disable the "beforeunload" event on Chrome.) -D window-position=X,Y (Set the browser's starting window position.) -D window-size=WIDTH,HEIGHT (Set the browser's starting window size.) -D maximize (Start tests with the browser window maximized.) -D screenshot (Save a screenshot at the end of each test.) -D no-screenshot (No screenshots saved unless tests directly ask it.) -D visual-baseline (Set the visual baseline for Visual/Layout tests.) -D wire (Use selenium-wire's webdriver for replacing selenium webdriver.) -D external-pdf (Set Chromium "plugins.always_open_pdf_externally":True.) -D timeout-multiplier=MULTIPLIER (Multiplies the default timeout values.) """ import ast import colorama import os import re import sys from contextlib import suppress from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import detect_b_ver from seleniumbase.core import download_helper from seleniumbase.core import log_helper from seleniumbase.core import proxy_helper from seleniumbase.core import session_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils is_linux = shared_utils.is_linux() is_windows = shared_utils.is_windows() sb_config.__base_class = None def set_base_class(base_class): """ # You can override the base class from Behave's environment.py file. # If not using SeleniumBase's BaseCase class, then use a subclass. from seleniumbase import BaseCase from seleniumbase.plugins import behave_plugin behave_plugin.set_base_class(BaseCase) """ sb_config.__base_class = base_class def get_configured_sb(context): if not sb_config.__base_class: from seleniumbase import BaseCase sb_config.__base_class = BaseCase sb_config.__base_class.test_method = {} sb = sb_config.__base_class("test_method") # Set default values sb.browser = "chrome" sb.is_behave = True sb.headless = False sb.headless1 = False sb.headless2 = False sb.headless_active = False sb.headed = False sb.xvfb = False sb.xvfb_metrics = None sb.start_page = None sb.locale_code = None sb.pdb_option = False sb.protocol = "http" sb.servername = "localhost" sb.port = 4444 sb.data = None sb.var1 = None sb.var2 = None sb.var3 = None sb.variables = {} sb.account = None sb.environment = "test" sb.env = "test" sb.user_agent = None sb.incognito = False sb.guest_mode = False sb.dark_mode = False sb.devtools = False sb.mobile_emulator = False sb.device_metrics = None sb.extension_zip = None sb.extension_dir = None sb.binary_location = None sb_config.binary_location = None sb.driver_version = None sb.page_load_strategy = None sb.database_env = "test" sb.log_path = constants.Logs.LATEST + os.sep sb.archive_logs = False sb.disable_cookies = False sb.disable_js = False sb.disable_csp = False sb.disable_ws = False sb.enable_ws = False sb.enable_sync = False sb.use_auto_ext = False sb.undetectable = False sb.uc_cdp_events = False sb.uc_subprocess = False sb.log_cdp_events = False sb.no_sandbox = False sb.disable_gpu = False sb._multithreaded = False sb._reuse_session = False sb._reuse_class_session = False sb._crumbs = False sb._disable_beforeunload = False sb.visual_baseline = False sb.use_wire = False sb.window_position = None sb.window_size = None sb.maximize_option = False sb.is_context_manager = False sb.save_screenshot_after_test = False sb.no_screenshot_after_test = False sb.timeout_multiplier = None sb.pytest_html_report = None sb.with_db_reporting = False sb.with_s3_logging = False sb.js_checking_on = False sb.recorder_mode = False sb.recorder_ext = False sb.record_sleep = False sb.rec_behave = False sb.rec_print = False sb.report_on = False sb.is_pytest = False sb.slow_mode = False sb.demo_mode = False sb.time_limit = None sb.demo_sleep = None sb.dashboard = False sb.dash_title = None sb._dash_initialized = False sb.message_duration = None sb.block_images = False sb.do_not_track = False sb.external_pdf = False sb.remote_debug = False sb.settings_file = None sb.user_data_dir = None sb.chromium_arg = None sb.firefox_arg = None sb.firefox_pref = None sb.disable_features = None sb.proxy_string = None sb.proxy_bypass_list = None sb.proxy_pac_url = None sb.multi_proxy = False sb.host_resolver_rules = None sb.enable_3d_apis = False sb.swiftshader = False sb.ad_block_on = False sb.is_nosetest = False sb.highlights = None sb.interval = None sb.cap_file = None sb.cap_string = None # Set a few sb_config vars early in case parsing args fails sb_config.dashboard = None sb_config._has_logs = None sb_config._has_exception = None sb_config.save_screenshot = None sb_config.reuse_class_session = None browsers = set() # To error if selecting more than one valid_browsers = constants.ValidBrowsers.valid_browsers valid_envs = constants.ValidEnvs.valid_envs # Process command-line options userdata = context.config.userdata for key in userdata.keys(): # Convert --ARG to ARG, etc. if key.startswith("--"): key = key[2:] if key.startswith("-"): key = key[1:] low_key = key.lower() # Handle: -D browser=BROWSER if low_key == "browser": browser = userdata[key].lower() if browser in valid_browsers: sb.browser = browser browsers.add(browser) elif browser == "true": raise Exception( '\nThe "browser" argument requires a value!' "\nChoose from %s." '\nEg. -D browser="edge"' % valid_browsers ) else: raise Exception( '\n"%s" is not a valid "browser" selection!' "\nChoose from %s." '\nEg. -D browser="edge"' % (browser, valid_browsers) ) continue # Handle: -D BROWSER if low_key in valid_browsers: browser = low_key sb.browser = browser browsers.add(browser) continue # Handle: -D headless if low_key == "headless": sb.headless = True continue # Handle: -D headless2 if low_key == "headless1": sb.headless1 = True sb.headless = True continue # Handle: -D headless2 if low_key == "headless2": sb.headless2 = True continue # Handle: -D headed / gui if low_key in ["headed", "gui"]: sb.headed = True continue # Handle: -D xvfb if low_key == "xvfb": sb.xvfb = True continue # Handle: -D xvfb-metrics=STR / xvfb_metrics=STR if low_key in ["xvfb-metrics", "xvfb_metrics"]: xvfb_metrics = userdata[key] if xvfb_metrics == "true": xvfb_metrics = sb.xvfb_metrics # revert to default sb.xvfb_metrics = xvfb_metrics continue # Handle: -D start-page=URL / start_page=URL / url=URL if low_key in ["start-page", "start_page", "url"]: start_page = userdata[key] if start_page == "true": start_page = sb.start_page # revert to default sb.start_page = start_page continue # Handle: -D locale-code=CODE / locale_code=CODE / locale=CODE if low_key in ["locale-code", "locale_code", "locale"]: sb.start_page = userdata[key] continue # Handle: -D pdb / ipdb if low_key in ["pdb", "ipdb"]: sb.pdb_option = True continue # Handle: -D protocol=PROTOCOL if low_key == "protocol": protocol = userdata[key].lower() if protocol in ["http", "https"]: sb.protocol = protocol elif protocol == "true": raise Exception( '\nThe Selenium Grid "protocol" argument requires a value!' '\nChoose from ["http", "https"]' '\nEg. -D protocol="https"' ) else: raise Exception( '\n"%s" is not a valid Selenium Grid "protocol" selection!' '\nChoose from ["http", "https"]' '\nEg. -D protocol="https"' % protocol ) continue # Handle: -D server=SERVERNAME / servername=SERVERNAME if low_key in ["server", "servername"]: servername = userdata[key] if servername == "true": servername = sb.servername # revert to default sb.servername = servername continue # Handle: -D port=PORT if low_key == "port": port = int(userdata[key]) if port == "true": port = sb.port # revert to default sb.port = port continue # Handle: -D data=DATA if low_key == "data": sb.data = userdata[key] continue # Handle: -D var1=DATA if low_key == "var1": sb.var1 = userdata[key] continue # Handle: -D var2=DATA if low_key == "var2": sb.var2 = userdata[key] continue # Handle: -D var3=DATA if low_key == "var3": sb.var3 = userdata[key] continue # Handle: -D variables="{'KEY':'VALUE','KEY2':'VALUE2'}" if low_key == "variables": variables = userdata[key] if variables and isinstance(variables, str) and len(variables) > 0: bad_input = False if ( not variables.startswith("{") or not variables.endswith("}") ): bad_input = True else: try: variables = ast.literal_eval(variables) if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True if bad_input: raise Exception( '\nExpecting a Python dictionary for "variables"!' "\nEg. -D variables=\"{'KEY':'VALUE', 'KEY2':123}\"" ) else: variables = {} continue # Handle: -D account=ACCOUNT if low_key == "account": sb.account = userdata[key] continue # Handle: -D env=ENVIRONMENT if low_key == "environment": environment = userdata[key].lower() if environment in valid_envs: sb.environment = environment sb.env = environment elif environment == "true": raise Exception( '\nThe "env" argument requires a value!' "\nChoose from %s." '\nEg. -D env="production"' % valid_envs ) else: raise Exception( '\n"%s" is not a valid "env" selection!' "\nChoose from %s." '\nEg. -D env="production"' % (environment, valid_envs) ) continue # Handle: -D user-agent=STRING / user_agent=STRING / agent=STRING if low_key in ["user-agent", "user_agent", "agent"]: user_agent = userdata[key] if user_agent == "true": user_agent = sb.user_agent # revert to default sb.user_agent = user_agent continue # Handle: -D incognito / incognito-mode / incognito_mode if low_key in ["incognito", "incognito-mode", "incognito_mode"]: sb.incognito = True continue # Handle: -D guest / guest-mode / guest_mode if low_key in ["guest", "guest-mode", "guest_mode"]: sb.guest_mode = True continue # Handle: -D dark / dark-mode / dark_mode if low_key in ["dark", "dark-mode", "dark_mode"]: sb.dark_mode = True continue # Handle: -D devtools / open-devtools / open_devtools if low_key in ["devtools", "open-devtools", "open_devtools"]: sb.devtools = True continue # Handle: -D mobile / mobile-emulator / mobile_emulator if low_key in ["mobile", "mobile-emulator", "mobile_emulator"]: sb.mobile_emulator = True continue # Handle: -D metrics=STR / device-metrics=STR / device_metrics=STR if low_key in ["metrics", "device-metrics", "device_metrics"]: device_metrics = userdata[key] if device_metrics == "true": device_metrics = sb.device_metrics # revert to default sb.device_metrics = device_metrics continue # Handle: -D crx=ZIP / extension-zip=ZIP / extension_zip=ZIP if low_key in ["crx", "extension-zip", "extension_zip"]: extension_zip = userdata[key] if extension_zip == "true": extension_zip = sb.extension_zip # revert to default sb.extension_zip = extension_zip continue # Handle: -D extension-dir=DIR / extension_dir=DIR if low_key in ["extension-dir", "extension_dir"]: extension_dir = userdata[key] if extension_dir == "true": extension_dir = sb.extension_dir # revert to default sb.extension_dir = extension_dir continue # Handle: -D binary-location=PATH / binary_location=PATH / bl=PATH if low_key in ["binary-location", "binary_location", "bl"]: binary_location = userdata[key] if binary_location == "true": binary_location = sb.binary_location # revert to default sb.binary_location = binary_location sb_config.binary_location = binary_location continue # Handle: -D use-chromium if low_key in ["use-chromium"] and not sb_config.binary_location: binary_location = "_chromium_" sb.binary_location = binary_location sb_config.binary_location = binary_location continue # Handle: -D cft if low_key in ["cft"] and not sb_config.binary_location: binary_location = "cft" sb.binary_location = binary_location sb_config.binary_location = binary_location continue # Handle: -D chs if low_key in ["chs"] and not sb_config.binary_location: binary_location = "chs" sb.binary_location = binary_location sb_config.binary_location = binary_location continue # Handle: -D driver-version=VER / driver_version=VER if low_key in ["driver-version", "driver_version"]: driver_version = userdata[key] if driver_version == "true": driver_version = sb.driver_version # revert to default sb.driver_version = driver_version continue # Handle: -D pls=PLS / page-load-strategy=PLS / page_load_strategy=PLS if low_key in ["pls", "page-load-strategy", "page_load_strategy"]: page_load_strategy = userdata[key].lower() if page_load_strategy in ["normal", "eager", "none"]: sb.page_load_strategy = page_load_strategy elif page_load_strategy == "true": raise Exception( '\nThe "pls" / "page-load-strategy" arg requires a value!' '\nChoose from ["normal", "eager", "none"]' '\nEg. -D pls="none"' ) else: raise Exception( '\n"%s" is not a valid "pls" / "page-load-strategy" value!' '\nChoose from ["normal", "eager", "none"]' '\nEg. -D pls="none"' % page_load_strategy ) continue # Handle: -D database-env=ENVIRONMENT / database_env=ENVIRONMENT if low_key in ["database-env", "database_env"]: database_env = userdata[key].lower() if database_env in valid_envs: sb.database_env = database_env elif database_env == "true": raise Exception( '\nThe "database-env" argument requires a value!' "\nChoose from %s." '\nEg. -D database-env="production"' % valid_envs ) else: raise Exception( '\n"%s" is not a valid "database-env" selection!' "\nChoose from %s." '\nEg. -D database-env="production"' % (environment, valid_envs) ) continue # Handle: -D archive-logs / archive_logs if low_key in ["archive-logs", "archive_logs"]: sb.archive_logs = True continue # Handle: -D disable-cookies / disable_cookies if low_key in ["disable-cookies", "disable_cookies"]: sb.disable_cookies = True continue # Handle: -D disable-js / disable_js if low_key in ["disable-js", "disable_js"]: sb.disable_js = True continue # Handle: -D disable-csp / disable_csp / dcsp if low_key in ["disable-csp", "disable_csp", "dcsp"]: sb.disable_csp = True continue # Handle: -D disable-ws / disable_ws / dws if low_key in ["disable-ws", "disable_ws", "dws"]: sb.disable_ws = True continue # Handle: -D enable-ws / enable_ws if low_key in ["enable-ws", "enable_ws"]: sb.enable_ws = True continue # Handle: -D enable-sync / enable_sync if low_key in ["enable-sync", "enable_sync"]: sb.enable_sync = True continue # Handle: -D use-auto-ext / use_auto_ext / auto-ext if low_key in ["use-auto-ext", "use_auto_ext", "auto-ext"]: sb.use_auto_ext = True continue # Handle: -D undetected / undetectable / uc if low_key in ["undetected", "undetectable", "uc"]: sb.undetectable = True continue # Handle: -D uc-cdp-events / uc_cdp_events / uc-cdp if low_key in ["uc-cdp-events", "uc_cdp_events", "uc-cdp"]: sb.uc_cdp_events = True sb.undetectable = True continue # Handle: -D uc-subprocess / uc_subprocess / uc-sub if low_key in ["uc-subprocess", "uc_subprocess", "uc-sub"]: sb.uc_subprocess = True sb.undetectable = True continue # Handle: -D log-cdp-events / log_cdp_events / log-cdp if low_key in ["log-cdp-events", "log_cdp_events", "log-cdp"]: sb.log_cdp_events = True continue # Handle: -D no-sandbox / no_sandbox if low_key in ["no-sandbox", "no_sandbox"]: sb.no_sandbox = True continue # Handle: -D disable-gpu / disable_gpu if low_key in ["disable-gpu", "disable_gpu"]: sb.disable_gpu = True continue # Handle: -D rs / reuse-session / reuse_session if low_key in ["rs", "reuse-session", "reuse_session"]: sb._reuse_session = True continue # Handle: -D rcs / rfs / reuse-class-session / reuse-feature-session if low_key in [ "rcs", "rfs", "reuse-class-session", "reuse-feature-session" ]: sb._reuse_session = True sb._reuse_class_session = True continue # Handle: -D crumbs if low_key == "crumbs": sb._crumbs = True continue # Handle: -D disable-beforeunload / disable_beforeunload if low_key in ["disable-beforeunload", "disable_beforeunload"]: sb._disable_beforeunload = True continue # Handle: -D sjw / skip-js-waits / skip_js_waits if low_key in ["sjw", "skip-js-waits", "skip_js_waits"]: settings.SKIP_JS_WAITS = True continue # Handle: -D wfa / wait-for-angularjs / wait_for_angularjs if low_key in ["wfa", "wait-for-angularjs", "wait_for_angularjs"]: settings.WAIT_FOR_ANGULARJS = True continue # Handle: -D visual-baseline / visual_baseline if low_key in ["visual-baseline", "visual_baseline"]: sb.visual_baseline = True continue # Handle: -D wire if low_key == "wire": sb.use_wire = True continue # Handle: -D window-position=X,Y / window_position=X,Y if low_key in ["window-position", "window_position"]: window_position = userdata[key] if window_position == "true": window_position = sb.window_position # revert to default sb.window_position = window_position continue # Handle: -D window-size=Width,Height / window_size=Width,Height if low_key in ["window-size", "window_size"]: window_size = userdata[key] if window_size == "true": window_size = sb.window_size # revert to default sb.window_size = window_size continue # Handle: -D maximize / fullscreen / maximize-window if low_key in [ "maximize", "fullscreen", "maximize-window", "maximize_window" ]: sb.maximize_option = True continue # Handle: -D screenshot / save-screenshot / save_screenshot / ss if low_key in [ "screenshot", "save-screenshot", "save_screenshot", "ss" ]: sb.save_screenshot_after_test = True continue # Handle: -D no-screenshot / no_screenshot / ns if low_key in ["no-screenshot", "no_screenshot", "ns"]: sb.no_screenshot_after_test = True continue # Handle: -D timeout-multiplier=FLOAT / timeout_multiplier=FLOAT if low_key in ["timeout-multiplier", "timeout_multiplier"]: timeout_multiplier = userdata[key] if timeout_multiplier == "true": timeout_multiplier = sb.timeout_multiplier # revert to default sb.timeout_multiplier = timeout_multiplier continue # Handle: -D with-db-reporting / with-db_reporting if low_key in ["with-db-reporting", "with-db_reporting"]: sb.with_db_reporting = True continue # Handle: -D with-s3-logging / with-s3_logging if low_key in ["with-s3-logging", "with-s3_logging"]: sb.with_s3_logging = True continue # Handle: -D check-js / check_js if low_key in ["check-js", "check_js"]: sb.js_checking_on = True continue # Handle: -D recorder / record / rec / codegen if low_key in ["recorder", "record", "rec", "codegen"]: sb.recorder_mode = True sb.recorder_ext = True continue # Handle: -D rec-behave / rec-gherkin if low_key in ["rec-behave", "rec-gherkin"]: sb.rec_behave = True sb.recorder_mode = True sb.recorder_ext = True continue # Handle: -D record-sleep / record_sleep / rec-sleep / rec_sleep if low_key in ["record-sleep", "rec-sleep"]: sb.record_sleep = True sb.recorder_mode = True sb.recorder_ext = True continue # Handle: -D rec-print if low_key in ["rec-print"]: sb.rec_print = True sb.recorder_mode = True sb.recorder_ext = True continue # Handle: -D slow / slowmo / slow-mode / slow_mode if low_key in ["slow", "slowmo", "slow-mode", "slow_mode"]: sb.slow_mode = True continue # Handle: -D demo / demo-mode / demo_mode if low_key in ["demo", "demo-mode", "demo_mode"]: sb.demo_mode = True continue # Handle: -D time-limit / time_limit / timelimit if low_key in ["time-limit", "time_limit", "timelimit"]: time_limit = userdata[key] if time_limit == "true": time_limit = sb.time_limit # revert to default sb.time_limit = time_limit continue # Handle: -D demo-sleep / demo_sleep if low_key in ["demo-sleep", "demo_sleep"]: demo_sleep = userdata[key] if demo_sleep == "true": demo_sleep = sb.demo_sleep # revert to default sb.demo_sleep = demo_sleep continue # Handle: -D dashboard if low_key == "dashboard": sb.dashboard = True continue # Handle: -D dash-title=TITLE / dash_title=TITLE if low_key in ["dash-title", "dash_title"]: sb.dash_title = userdata[key] continue # Handle: -D message-duration / message_duration if low_key in ["message-duration", "message_duration"]: message_duration = userdata[key] if message_duration == "true": message_duration = sb.message_duration # revert to default sb.message_duration = message_duration continue # Handle: -D block-images / block_images if low_key in ["block-images", "block_images"]: sb.block_images = True continue # Handle: -D do-not-track / do_not_track if low_key in ["do-not-track", "do_not_track"]: sb.do_not_track = True continue # Handle: -D external-pdf / external_pdf if low_key in ["external-pdf", "external_pdf"]: sb.external_pdf = True continue # Handle: -D remote-debug / remote_debug / remote-debugger if low_key in ["remote-debug", "remote_debug", "remote-debugger"]: sb.remote_debug = True continue # Handle: -D settings=FILE / settings-file=FILE / settings_file=FILE if low_key in ["settings", "settings-file", "settings_file"]: settings_file = userdata[key] if settings_file == "true": settings_file = sb.settings_file # revert to default sb.settings_file = settings_file continue # Handle: -D user-data-dir=DIR / user_data_dir=DIR if low_key in ["user-data-dir", "user_data_dir"]: user_data_dir = userdata[key] if user_data_dir == "true": user_data_dir = sb.user_data_dir # revert to default sb.user_data_dir = user_data_dir continue # Handle: -D chromium-arg="ARG=N,ARG2" / chromium_arg="ARG=N,ARG2" if low_key in ["chromium-arg", "chromium_arg"]: chromium_arg = userdata[key] if chromium_arg == "true": chromium_arg = sb.chromium_arg # revert to default sb.chromium_arg = chromium_arg continue # Handle: -D firefox-arg="ARG=N,ARG2" / firefox_arg="ARG=N,ARG2" if low_key in ["firefox-arg", "firefox_arg"]: firefox_arg = userdata[key] if firefox_arg == "true": firefox_arg = sb.firefox_arg # revert to default sb.firefox_arg = firefox_arg continue # Handle: -D firefox-pref="PREF:VAL" / firefox_pref="PREF:VAL" if low_key in ["firefox-pref", "firefox_pref"]: firefox_pref = userdata[key] if firefox_pref == "true": firefox_pref = sb.firefox_pref # revert to default sb.firefox_pref = firefox_pref continue # Handle: -D disable-features="F1,F2" / disable_features="F1,F2" if low_key in ["disable-features", "disable_features"]: disable_features = userdata[key] if disable_features == "true": disable_features = sb.disable_features # revert to default sb.disable_features = disable_features continue # Handle: -D proxy=SERVER:PORT / proxy=USERNAME:PASSWORD@SERVER:PORT if low_key in ["proxy", "proxy-server", "proxy-string"]: proxy_string = userdata[key] if proxy_string == "true": proxy_string = sb.proxy_string # revert to default sb.proxy_string = proxy_string continue # Handle: -D proxy-bypass-list="DOMAIN1;D2" / proxy_bypass_list="D1;D2" if low_key in ["proxy-bypass-list", "proxy_bypass_list"]: proxy_bypass_list = userdata[key] if proxy_bypass_list == "true": proxy_bypass_list = sb.proxy_bypass_list # revert to default sb.proxy_bypass_list = proxy_bypass_list continue # Handle: -D proxy-pac-url=URL / proxy-pac-url=USERNAME:PASSWORD@URL if low_key in ["proxy-pac-url", "pac-url"]: proxy_pac_url = userdata[key] if proxy_pac_url == "true": proxy_pac_url = sb.proxy_pac_url # revert to default sb.proxy_pac_url = proxy_pac_url continue # Handle: -D multi-proxy / multi_proxy if low_key in ["multi-proxy", "multi_proxy"]: sb.multi_proxy = True continue # Handle: -D host-resolver-rules=RULES / host_resolver_rules=RULES if low_key in ["host-resolver-rules", "host_resolver_rules"]: host_resolver_rules = userdata[key] if host_resolver_rules == "true": host_resolver_rules = sb.host_resolver_rules sb.host_resolver_rules = host_resolver_rules continue # Handle: -D enable-3d-apis / enable_3d_apis if low_key in ["enable-3d-apis", "enable_3d_apis"]: sb.enable_3d_apis = True continue # Handle: -D swiftshader if low_key == "swiftshader": sb.swiftshader = True continue # Handle: -D adblock / ad-block / ad_block / block-ads / block_ads if low_key in [ "adblock", "ad-block", "ad_block", "block-ads", "block_ads" ]: sb.ad_block_on = True continue # Handle: -D highlights=NUM if low_key == "highlights": highlights = userdata[key] if highlights == "true": highlights = sb.highlights # revert to default sb.highlights = highlights continue # Handle: -D interval=SECONDS if low_key == "interval": interval = userdata[key] if interval == "true": interval = sb.interval # revert to default sb.interval = interval continue # Handle: -D cap-file=FILE / cap_file=FILE if low_key in ["cap-file", "cap_file"]: cap_file = userdata[key] if cap_file == "true": cap_file = sb.cap_file # revert to default sb.cap_file = cap_file continue # Handle: -D cap-string=STRING / cap_string=STRING if low_key == "cap_string": cap_string = userdata[key] if cap_string == "true": cap_string = sb.cap_string # revert to default sb.cap_string = cap_string continue # Fail immediately if trying to set more than one default browser. if len(browsers) > 1: raise Exception( "\nOnly ONE default browser is allowed!\n" "%s browsers were selected: %s" % (len(browsers), browsers) ) if sb.browser in ["opera", "brave", "comet", "atlas"]: bin_loc = detect_b_ver.get_binary_location(sb.browser) if bin_loc and os.path.exists(bin_loc): sb_config._cdp_browser = sb.browser sb_config._cdp_bin_loc = bin_loc sb_config.binary_location = bin_loc sb.binary_location = bin_loc # Recorder Mode can still optimize scripts in "-D headless2" mode. if sb.recorder_ext and sb.headless: sb.headless = False sb.headless1 = False sb.headless2 = True if sb.headless2 and sb.browser == "firefox": sb.headless2 = False # Only for Chromium browsers sb.headless = True # Firefox has regular headless elif sb.browser not in ["chrome", "edge"]: sb.headless2 = False # Only for Chromium browsers if ( sb.binary_location and sb.binary_location.lower() == "chs" and sb.browser == "chrome" ): sb.headless = True sb.headless1 = False sb.headless2 = False # Recorder Mode only supports Chromium browsers. if sb.recorder_ext and (sb.browser not in ["chrome", "edge"]): raise Exception( "\n\n Recorder Mode ONLY supports Chrome and Edge!" '\n (Your browser choice was: "%s")\n' % sb.browser ) # The Xvfb virtual display server is for Linux OS Only. if sb.xvfb and not is_linux: sb.xvfb = False if ( is_linux and not sb.headed and not sb.headless and not sb.headless2 and not sb.xvfb ): if not sb.undetectable: print( '(Linux uses "-D headless" by default. ' 'To override, use "-D headed" / "-D gui". ' 'For Xvfb mode instead, use "-D xvfb". ' "Or you can hide this info by using" '"-D headless" / "-D headless2" / "-D uc".)' ) sb.headless = True else: sb.xvfb = True # Recorder Mode can still optimize scripts in --headless2 mode. if sb.recorder_mode and sb.headless: sb.headless = False sb.headless1 = False sb.headless2 = True if not sb.headless and not sb.headless2: sb.headed = True if sb.browser == "safari" and sb.headless: sb.headless = False # Safari doesn't support headless mode sb.headless1 = False if sb.save_screenshot_after_test and sb.no_screenshot_after_test: sb.save_screenshot_after_test = False # "no_screenshot" has priority if sb.servername != "localhost": # Using Selenium Grid # (Set -D server="127.0.0.1" for localhost Grid) # If the port is "443", the protocol is "https" if str(sb.port) == "443": sb.protocol = "https" if ( (sb.enable_ws is None and sb.disable_ws is None) or (sb.disable_ws is not None and not sb.disable_ws) or (sb.enable_ws is not None and sb.enable_ws) ): sb.enable_ws = True sb.disable_ws = False else: sb.enable_ws = False sb.disable_ws = True if sb.window_position: window_position = sb.window_position if window_position.count(",") != 1: message = ( '\n\n window_position expects an "x,y" string!' '\n (Your input was: "%s")\n' % window_position ) raise Exception(message) window_position = window_position.replace(" ", "") win_x = None win_y = None try: win_x = int(window_position.split(",")[0]) win_y = int(window_position.split(",")[1]) except Exception: message = ( '\n\n Expecting integer values for "x,y"!' '\n (window_position input was: "%s")\n' % window_position ) raise Exception(message) settings.WINDOW_START_X = win_x settings.WINDOW_START_Y = win_y if sb.window_size: window_size = sb.window_size if window_size.count(",") != 1: message = ( '\n\n window_size expects a "width,height" string!' '\n (Your input was: "%s")\n' % window_size ) raise Exception(message) window_size = window_size.replace(" ", "") width = None height = None try: width = int(window_size.split(",")[0]) height = int(window_size.split(",")[1]) except Exception: message = ( '\n\n Expecting integer values for "width,height"!' '\n (window_size input was: "%s")\n' % window_size ) raise Exception(message) settings.CHROME_START_WIDTH = width settings.CHROME_START_HEIGHT = height settings.HEADLESS_START_WIDTH = width settings.HEADLESS_START_HEIGHT = height # Set sb_config sb_config.browser = sb.browser sb_config.headless = sb.headless sb_config.headless_active = False sb_config.headed = sb.headed sb_config.is_behave = True sb_config.is_pytest = False sb_config.is_nosetest = False sb_config.is_context_manager = False sb_config.window_position = sb.window_position sb_config.window_size = sb.window_size sb_config.maximize_option = sb.maximize_option sb_config.xvfb = sb.xvfb sb_config.xvfb_metrics = sb.xvfb_metrics sb_config.reuse_class_session = sb._reuse_class_session sb_config.save_screenshot = sb.save_screenshot_after_test sb_config.no_screenshot = sb.no_screenshot_after_test sb_config._has_logs = False sb_config.variables = sb.variables sb_config.dashboard = sb.dashboard sb_config.dash_title = sb.dash_title sb_config.pdb_option = sb.pdb_option sb_config.rec_behave = sb.rec_behave sb_config.rec_print = sb.rec_print sb_config.disable_cookies = sb.disable_cookies sb_config.disable_js = sb.disable_js sb_config.disable_csp = sb.disable_csp sb_config.record_sleep = sb.record_sleep sb_config._is_timeout_changed = False sb_config._SMALL_TIMEOUT = settings.SMALL_TIMEOUT sb_config._LARGE_TIMEOUT = settings.LARGE_TIMEOUT sb_config._recorded_actions = {} sb_config._behave_recorded_actions = {} # Dashboard-specific variables sb_config._results = {} # SBase Dashboard test results sb_config._duration = {} # SBase Dashboard test duration sb_config._display_id = {} # SBase Dashboard display ID sb_config._d_t_log_path = {} # SBase Dashboard test log path sb_config._dash_html = None # SBase Dashboard HTML copy sb_config._test_id = None # SBase Dashboard test id sb_config._latest_display_id = None # The latest SBase display id sb_config._dashboard_initialized = False # Becomes True after init sb_config._has_exception = False # This becomes True if any test fails sb_config._multithreaded = False # This becomes True if multithreading sb_config._only_unittest = True # If any test uses BaseCase, becomes False sb_config._sbase_detected = False # Becomes True during SeleniumBase tests sb_config._extra_dash_entries = [] # Dashboard entries for non-SBase tests sb_config._using_html_report = False # Becomes True when using html report sb_config._dash_is_html_report = False # Dashboard becomes the html report sb_config._saved_dashboard_pie = None # Copy of pie chart for html report sb_config._dash_final_summary = None # Dash status to add to html report sb_config._html_report_name = None # The name of the pytest html report if sb_config.dash_title: constants.Dashboard.TITLE = sb_config.dash_title.replace("_", " ") log_helper.log_folder_setup( constants.Logs.LATEST + "/", sb.archive_logs ) download_helper.reset_downloads_folder() proxy_helper.remove_proxy_zip_if_present() return sb def calculate_test_id(file_name, scenario_name): file_name = file_name.replace("/", ".").replace("\\", ".") scenario_name = re.sub(r"[^\w" + r"_ " + r"]", "", scenario_name) scenario_name = scenario_name.replace(" ", "_") if " -- @" in scenario_name: scenario_name = scenario_name.split(" # ")[0].rstrip() test_id = "%s.%s" % (file_name, scenario_name) return test_id def calculate_display_id(file_name, line_num, scenario_name): if " -- @" in scenario_name: scenario_name = scenario_name.split(" # ")[0].rstrip() display_id = "%s:%s => %s" % (file_name, line_num, scenario_name) return display_id def get_test_id(): file_name = sb_config.behave_scenario.filename file_name = file_name.replace("/", ".").replace("\\", ".") scenario_name = sb_config.behave_scenario.name if " -- @" in scenario_name: scenario_name = scenario_name.split(" # ")[0].rstrip() scenario_name = re.sub(r"[^\w" + r"_ " + r"]", "", scenario_name) scenario_name = scenario_name.replace(" ", "_") test_id = "%s.%s" % (file_name, scenario_name) return test_id def get_display_id(): file_name = sb_config.behave_scenario.filename line_num = str(sb_config.behave_scenario.line) scenario_name = sb_config.behave_scenario.name if " -- @" in scenario_name: scenario_name = scenario_name.split(" # ")[0].rstrip() display_id = "%s:%s => %s" % (file_name, line_num, scenario_name) return display_id def _get_test_ids_(): test_id = get_test_id() display_id = get_display_id() return test_id, display_id def dashboard_pre_processing(): import subprocess command_args = sys.argv[1:] command_string = " ".join(command_args) command_string = command_string.replace("--quiet", "") command_string = command_string.replace("-q", "") proc = subprocess.Popen( "behave -d %s --show-source" % command_string, stdout=subprocess.PIPE, shell=True, ) (output, error) = proc.communicate() filename_count = 0 filename_list = [] feature_count = 0 feature_list = [] scenario_count = 0 scenario_list = [] sb_config.item_count = 0 sb_config.item_count_passed = 0 sb_config.item_count_failed = 0 sb_config.item_count_skipped = 0 sb_config.item_count_untested = 0 filename = None feature_name = None scenario_name = None if is_windows: output = output.decode("latin1") else: output = output.decode("utf-8") for row in output.replace("\r", "").split("\n"): if row.startswith("Feature: "): filename_count += 1 feature_count += 1 feature_name = row.split("Feature: ")[1] if " # features/" in feature_name: filename = feature_name.split(" # features/")[-1] filename = "features/" + filename.split(":")[0] feature_name = feature_name.split(" # features/")[0] elif " # features\\" in feature_name: filename = feature_name.split(" # features\\")[-1] filename = "features\\" + filename.split(":")[0] feature_name = feature_name.split(" # features\\")[0] else: filename = feature_name.split(" # ")[-1] filename = filename.split(":")[0] feature_name = feature_name.split(" # ")[-1] filename = filename.strip() filename_list.append(filename) feature_name = feature_name.strip() feature_list.append(feature_name) # Maybe filename is good enough elif ( row.startswith(" Scenario: ") or row.startswith(" Scenario Outline: ") ): line_num = row.split(":")[-1] scenario_count += 1 scenario_name = None if row.startswith(" Scenario: "): scenario_name = row.split(" Scenario: ")[-1] else: scenario_name = row.split(" Scenario Outline: ")[-1] if " -- @" in scenario_name: scenario_name = scenario_name.split(" # ")[0].rstrip() elif " # features/" in scenario_name: scenario_name = scenario_name.split(" # features/")[0] else: scenario_name = scenario_name.split(" # ")[0] scenario_name = scenario_name.strip() scenario_list.append(scenario_name) # Dashboard row preparation test_id = calculate_test_id(filename, scenario_name) display_id = calculate_display_id( filename, line_num, scenario_name ) sb_config._results[test_id] = "Untested" sb_config._duration[test_id] = "-" sb_config._display_id[test_id] = display_id sb_config._d_t_log_path[test_id] = None # Set the total number of dashboard entries sb_config.item_count = scenario_count def _create_dashboard_assets_(): from seleniumbase.js_code.live_js import live_js from seleniumbase.core.style_sheet import get_pytest_style abs_path = os.path.abspath(".") assets_folder = os.path.join(abs_path, "assets") if not os.path.exists(assets_folder): os.makedirs(assets_folder) pytest_style_css = os.path.join(assets_folder, "pytest_style.css") add_pytest_style_css = True if os.path.exists(pytest_style_css): existing_pytest_style = None with open(pytest_style_css, mode="r") as f: existing_pytest_style = f.read() if existing_pytest_style == get_pytest_style(): add_pytest_style_css = False if add_pytest_style_css: out_file = open(pytest_style_css, mode="w+", encoding="utf-8") out_file.writelines(get_pytest_style()) out_file.close() live_js_file = os.path.join(assets_folder, "live.js") add_live_js_file = True if os.path.exists(live_js_file): existing_live_js = None with open(live_js_file, mode="r") as f: existing_live_js = f.read() if existing_live_js == live_js: add_live_js_file = False if add_live_js_file: out_file = open(live_js_file, mode="w+", encoding="utf-8") out_file.writelines(live_js) out_file.close() def behave_dashboard_prepare(): """Print the dashboard path if at least one test runs.""" if sb_config.item_count > 0: _create_dashboard_assets_() # Output Dashboard info to the console sb_config.item_count_untested = sb_config.item_count dash_path = os.path.join(os.getcwd(), "dashboard.html") star_len = len("Dashboard: ") + len(dash_path) with suppress(Exception): terminal_size = os.get_terminal_size().columns if terminal_size > 30 and star_len > terminal_size: star_len = terminal_size stars = "*" * star_len c1 = "" cr = "" if not is_linux: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX cr = colorama.Style.RESET_ALL print("Dashboard: %s%s%s\n%s" % (c1, dash_path, cr, stars)) def _perform_behave_unconfigure_(): if hasattr(sb_config, "multi_proxy") and not sb_config.multi_proxy: proxy_helper.remove_proxy_zip_if_present() if hasattr(sb_config, "reuse_session") and sb_config.reuse_session: # Close the shared browser session if sb_config.shared_driver: try: if ( not is_windows or sb_config.browser == "ie" or sb_config.shared_driver.service.process ): sb_config.shared_driver.quit() except AttributeError: pass except Exception: pass sb_config.shared_driver = None if hasattr(sb_config, "archive_logs"): log_helper.archive_logs_if_set( constants.Logs.LATEST + "/", sb_config.archive_logs ) log_helper.clear_empty_logs() # Dashboard post-processing: Disable time-based refresh and stamp complete if not hasattr(sb_config, "dashboard") or not sb_config.dashboard: # Done with "behave_unconfigure" unless using the Dashboard return stamp = "\n" find_it = constants.Dashboard.META_REFRESH_HTML swap_with = "" # Stop refreshing the page after the run is done find_it_2 = "Awaiting results... (Refresh the page for updates)" swap_with_2 = ( "Test Run ENDED: Some results UNREPORTED due to skipped tearDown()" ) find_it_3 = 'Untested' swap_with_3 = 'Unreported' # These use caching to prevent extra method calls DASH_PIE_PNG_1 = constants.Dashboard.get_dash_pie_1() DASH_PIE_PNG_2 = constants.Dashboard.get_dash_pie_2() find_it_4 = 'href="%s"' % DASH_PIE_PNG_1 swap_with_4 = 'href="%s"' % DASH_PIE_PNG_2 try: abs_path = os.path.abspath(".") dashboard_path = os.path.join(abs_path, "dashboard.html") # Part 1: Finalizing the dashboard / integrating html report if os.path.exists(dashboard_path): the_html_d = None with open(dashboard_path, mode="r", encoding="utf-8") as f: the_html_d = f.read() if sb_config._multithreaded and "-c" in sys.argv: # Threads have "-c" in sys.argv, except for the last raise Exception('Break out of "try" block.') if sb_config._multithreaded: dash_pie_loc = constants.Dashboard.DASH_PIE pie_path = os.path.join(abs_path, dash_pie_loc) if os.path.exists(pie_path): import json with open(pie_path, mode="r") as f: dash_pie = f.read().strip() sb_config._saved_dashboard_pie = json.loads(dash_pie) # If the test run doesn't complete by itself, stop refresh the_html_d = the_html_d.replace(find_it, swap_with) the_html_d = the_html_d.replace(find_it_2, swap_with_2) the_html_d = the_html_d.replace(find_it_3, swap_with_3) the_html_d = the_html_d.replace(find_it_4, swap_with_4) the_html_d += stamp with open(dashboard_path, mode="w", encoding="utf-8") as f: f.write(the_html_d) # Finalize the dashboard except KeyboardInterrupt: pass except Exception: pass def do_final_driver_cleanup_as_needed(): with suppress(Exception): if hasattr(sb_config, "last_driver") and sb_config.last_driver: if ( not is_windows or sb_config.browser == "ie" or sb_config.last_driver.service.process ): sb_config.last_driver.quit() def _perform_behave_terminal_summary_(): latest_logs_dir = os.path.join( os.getcwd(), constants.Logs.LATEST + os.sep ) dash_path = os.path.join(os.getcwd(), "dashboard.html") equals_len = len("Dashboard: ") + len(dash_path) with suppress(Exception): terminal_size = os.get_terminal_size().columns if terminal_size > 30 and equals_len > terminal_size: equals_len = terminal_size equals = "=" * (equals_len + 2) c2 = "" cr = "" if not is_linux: c2 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL if sb_config.dashboard: # Print link a second time because the first one may be off-screen print("%s- Dashboard:%s %s" % (c2, cr, dash_path)) if ( sb_config._has_exception or sb_config.save_screenshot or sb_config._has_logs ): # Log files are generated during test failures and Screenshot Mode print("%s--- LogPath:%s %s" % (c2, cr, latest_logs_dir)) if ( sb_config.dashboard and not (sb_config._has_exception or sb_config.save_screenshot) ): print("%s" % equals) elif ( not sb_config.dashboard and (sb_config._has_exception or sb_config.save_screenshot) ): print("%s" % equals[2:]) elif ( sb_config.dashboard and (sb_config._has_exception or sb_config.save_screenshot) ): print("%s" % equals[2:]) ########################################### def before_all(context): context.sb = get_configured_sb(context) if context.sb.dashboard: dashboard_pre_processing() behave_dashboard_prepare() def before_feature(context, feature): sb_config.behave_feature = feature session_helper.end_reused_class_session_as_needed() def before_scenario(context, scenario): sb_config.behave_context = context sb_config.behave_scenario = scenario sb_config.behave_line_num = scenario.line sb_config.behave_step_count = 0 context.sb.setUp() def before_step(context, step): sb_config.behave_step_count += 1 sb_config.behave_step = step def after_step(context, step): sb_config.behave_step = step if step.status == "failed": number = sb_config.behave_step_count print(">>> STEP FAILED: (#%s) %s" % (number, step.name)) print("Class / Feature: ", sb_config.behave_feature.name) print("Test / Scenario: ", sb_config.behave_scenario.name) def after_scenario(context, scenario): sb = context.sb sb_config.last_driver = sb.driver sb_config.behave_context = context sb_config.behave_scenario = scenario sb.tearDown() def after_feature(context, feature): sb_config.feature = feature session_helper.end_reused_class_session_as_needed() def after_all(context): _perform_behave_unconfigure_() do_final_driver_cleanup_as_needed() _perform_behave_terminal_summary_() ================================================ FILE: seleniumbase/behave/steps.py ================================================ from sbase import steps # noqa """ This is a proxy for importing SeleniumBase-Behave steps. A short path name shortens the output of "behave" tests. See "../../sbase/steps.py" for the actual list of steps. Eg. BEHAVE STEP NAME # ../../sbase/steps.py:11 0.111s BEHAVE STEP NAME # ../../sbase/steps.py:74 0.550s BEHAVE STEP NAME # ../../sbase/steps.py:67 0.385s """ ================================================ FILE: seleniumbase/common/ReadMe.md ================================================ ## [seleniumbase/common](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/common) decorators and security. ### Part 1: Decorators - (from [decorators.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/common/decorators.py)) #### Use these Python decorators with your test methods as needed: * ``@print_runtime(description=None, limit=None)`` * ``@runtime_limit(limit, description=None)`` * ``@retry_on_exception(tries=6, delay=1, backoff=2, max_delay=32)`` * ``@rate_limited(max_per_second)`` Example demonstrating a rate-limited printing functionality: ```python import unittest from seleniumbase import decorators class MyTestClass(unittest.TestCase): @decorators.rate_limited(3.5) # The arg is max calls per second def print_item(self, item): print(item) def test_rate_limited_printing(self): print("\nRunning rate-limited print test:") for item in range(1, 11): self.print_item(item) ``` ### Part 2: String/Password Obfuscation, Encryption, and Decryption #### Intro: Often in your tests, you may need to login to a website to perform testing. This generally means storing passwords in plaintext formats. For security reasons, that may not be an optimal solution. For this reason, encryption/obfuscation tools have been built here to help you mask your passwords in your tests. It's not a bulletproof solution, but it can keep anyone looking over your shoulder during test creation from getting your login passwords if they don't have your encryption key, which is stored in a separate file. #### Usage: * First, set your custom encryption/decryption key in your local clone of [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py). (If you modify the key later, you'll need to encrypt all your passwords again.) * Next, use [obfuscate.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/common/obfuscate.py) to obfuscate/encrypt passwords into coded strings: ```zsh python obfuscate.py Enter password to obfuscate: (CTRL+C to exit) Password: ********* Verify password: Password: ********* Here is the obfuscated password: $^*ENCRYPT=RXlYMSJWTz8HSwM=?&#$ ``` (You can also use [unobfuscate.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/common/unobfuscate.py) to encrypt passwords without having them masked while typing them. Or you can use it to decrypt an obfuscated password.) * Finally, in your tests you can now decrypt obfuscated passwords for use in login methods like this: ```python from seleniumbase import encryption ... password = encryption.decrypt('$^*ENCRYPT=RXlYMSJWTz8HSwM=?&#$') ``` (You'll notice that encrypted strings have a common start token and end token. This is to help tell them apart from non-encrypted strings. You can customize these tokens in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py). The current default setting is `$^*ENCRYPT=` for the start token and `?&#$` for the end token.) See [test_decryption.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_decryption.py) for an example of decrypting encrypted passwords in tests. ================================================ FILE: seleniumbase/common/__init__.py ================================================ ================================================ FILE: seleniumbase/common/decorators.py ================================================ import colorama import logging import math import sys import time import warnings from contextlib import contextmanager from functools import wraps from seleniumbase.common.exceptions import TimeoutException c1 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL if "linux" in sys.platform: c1 = cr = "" @contextmanager def print_runtime(description=None, limit=None): """Print the runtime duration of a method or "with"-block after completion. If limit, fail if the runtime duration exceeds the limit after completion. Method / Function example usage -> from seleniumbase import decorators @decorators.print_runtime("My Method") def my_method(): # code ... # code ... "with"-block example usage -> from seleniumbase import decorators with decorators.print_runtime("My Code Block"): # code ... # code ... """ if not description: description = "Code Block" description = str(description) if limit: limit = float("%.2f" % limit) if limit < 0.01: limit = 0.01 # Minimum runtime limit exception = None start_time = time.time() try: yield except Exception as e: exception = e raise finally: end_time = time.time() run_time = end_time - start_time name = description info = c1 + "" + cr # Print times with a statistically significant number of decimal places if run_time < 0.00001: print("%s - {%s} ran for %.8f seconds." % (info, name, run_time)) elif run_time < 0.0001: print("%s - {%s} ran for %.7f seconds." % (info, name, run_time)) elif run_time < 0.001: print("%s - {%s} ran for %.6f seconds." % (info, name, run_time)) elif run_time < 0.01: print("%s - {%s} ran for %.5f seconds." % (info, name, run_time)) elif run_time < 0.1: print("%s - {%s} ran for %.4f seconds." % (info, name, run_time)) elif run_time < 1: print("%s - {%s} ran for %.3f seconds." % (info, name, run_time)) else: print("%s - {%s} ran for %.2f seconds." % (info, name, run_time)) if limit and limit > 0 and run_time > limit: message = ( "\n {%s} duration of %.2fs exceeded the time limit of %.2fs!" % (name, run_time, limit) ) if exception: message = exception.msg + "\nAND " + message raise TimeoutException(message) @contextmanager def runtime_limit(limit, description=None): """Fail if the runtime duration of a method or "with"-block exceeds limit. (The failure won't occur until after the method or "with"-block completes.) Method / Function example usage -> from seleniumbase import decorators @decorators.runtime_limit(4.5) def my_method(): # code ... # code ... "with"-block example usage -> from seleniumbase import decorators with decorators.runtime_limit(32): # code ... # code ... """ limit = float("%.2f" % limit) if limit < 0.01: limit = 0.01 # Minimum runtime limit if not description: description = "Code Block" description = str(description) exception = None start_time = time.time() try: yield except Exception as e: exception = e raise finally: end_time = time.time() run_time = end_time - start_time # Fail if the runtime of the code block exceeds the limit if limit and limit > 0 and run_time > limit: message = ( "\n {%s} duration of %.2fs exceeded the time limit of %.2fs!" % (description, run_time, limit) ) if exception: message = exception.msg + "\nAND " + message raise TimeoutException(message) def retry_on_exception(tries=6, delay=1, backoff=2, max_delay=32): """Decorator for implementing exponential backoff for retrying on failures. tries: Max number of tries to execute the wrapped function before failing. delay: Delay time in seconds before the FIRST retry. backoff: Multiplier to extend the initial delay by for each retry. max_delay: Max time in seconds to wait between retries.""" tries = math.floor(tries) if tries < 1: raise ValueError('"tries" must be greater than or equal to 1.') if delay < 0: raise ValueError('"delay" must be greater than or equal to 0.') if backoff < 1: raise ValueError('"backoff" must be greater than or equal to 1.') if max_delay < delay: raise ValueError('"max_delay" must be greater than or equal to delay.') def decorated_function_with_retry(func): @wraps(func) def function_to_retry(*args, **kwargs): local_tries, local_delay = tries, delay while local_tries > 1: try: return func(*args, **kwargs) except Exception as e: if local_delay > max_delay: local_delay = max_delay logging.exception( "%s: Retrying in %d seconds..." % (str(e), local_delay) ) time.sleep(local_delay) local_tries -= 1 local_delay *= backoff return func(*args, **kwargs) return function_to_retry return decorated_function_with_retry def rate_limited(max_per_second): """This decorator limits how often a method can get called in a second. If the limit is exceeded, the call will be held in a queue until enough time has passed. Useful when trying to avoid overloading a system with rapid calls.""" import threading min_interval = 1.0 / float(max_per_second) def decorate(func): last_time_called = [0.0] rate_lock = threading.Lock() # To support multi-threading def rate_limited_function(*args, **kargs): try: rate_lock.acquire(True) elapsed = None elapsed = time.process_time() - last_time_called[0] wait_time_remaining = min_interval - elapsed if wait_time_remaining > 0: time.sleep(wait_time_remaining) last_time_called[0] = time.process_time() finally: rate_lock.release() return func(*args, **kargs) return rate_limited_function return decorate def deprecated(message=None): """This decorator marks methods as deprecated. A warning is displayed if the method is called.""" import inspect def decorated_method_to_deprecate(func): if inspect.isclass(func): # Handle a deprecated class differently from a deprecated method msg = "Class {}() is DEPRECATED!".format(func.__name__) if message: msg += " *** %s ***" % message warnings.simplefilter("always", DeprecationWarning) # See Warnings warnings.warn(msg, category=DeprecationWarning, stacklevel=2) warnings.simplefilter("default", DeprecationWarning) # Set Default return func @wraps(func) def new_func(*args, **kwargs): msg = "Method {}() is DEPRECATED!".format(func.__name__) if message: msg += " *** %s ***" % message warnings.simplefilter("always", DeprecationWarning) # See Warnings warnings.warn(msg, category=DeprecationWarning, stacklevel=2) warnings.simplefilter("default", DeprecationWarning) # Set Default return func(*args, **kwargs) return new_func return decorated_method_to_deprecate ================================================ FILE: seleniumbase/common/encryption.py ================================================ """This is mainly for string obfuscation.""" import base64 import codecs import hashlib from seleniumbase.config import settings def str_xor(string, key): if len(key) < 1: raise Exception("2nd arg of str_xor() must be a string of length > 0!") if len(string) > len(key): difference = len(string) - len(key) key = key + (((difference / len(key)) * key) + key) result = None try: result = "".join( [chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(string, key)] ) except Exception: string = string.decode("utf-8") result = "".join( [chr(ord(c1) ^ ord(c2)) for (c1, c2) in zip(string, key)] ) return result def is_obfuscated(string): # Based on settings, determines if a string has already been obfuscated. # Obfuscated strings have a common predefined start token and end token. start_token = settings.OBFUSCATION_START_TOKEN end_token = settings.OBFUSCATION_END_TOKEN return string.startswith(start_token) and string.endswith(end_token) def shuffle_string(string): if len(string) < 2: return string return string[1::2] + string[::2] def reverse_shuffle_string(string): if len(string) < 2: return string new_string = "" odd = len(string) % 2 == 1 part1 = string[: int(len(string) / 2) : 1] # noqa: E203 part2 = string[int(len(string) / 2) :: 1] # noqa: E203 for c in range(len(part1)): new_string += part2[c] new_string += part1[c] if odd: new_string += part2[-1] return new_string def blend_strings(string1, string2): smallest_length = min(len(string1), len(string2)) new_string = "" for c in range(smallest_length): new_string += string1[c] new_string += string2[c] if len(string1) > len(string2): new_string += string1[smallest_length:] elif len(string2) > len(string1): new_string += string2[smallest_length:] else: # Equal length strings pass return new_string def rotate(string, n): return string[n:] + string[:n] def ord_string_sum(string): count = 0 try: for c in string: count += ord(c) except Exception: string = string.decode("utf-8") for c in string: count += ord(c) return count def decrypt(string): # Password/String obfuscation/de-obfuscation # Used for both encryption and decryption # If you update the algorithm, you must re-encrypt all encrypted passwords! encryption_key = settings.ENCRYPTION_KEY start_token = settings.OBFUSCATION_START_TOKEN end_token = settings.OBFUSCATION_END_TOKEN already_encrypted = False if is_obfuscated(string): already_encrypted = True string = string[len(start_token) : -len(end_token)] # noqa: E203 string = base64.b64decode(codecs.encode(string)) # Obfuscate the key used for string obfuscation hd1 = hashlib.sha256(str(encryption_key).encode("utf-8")).hexdigest() hd2 = hashlib.sha256(str(encryption_key[::-1]).encode("utf-8")).hexdigest() b64_key = base64.b64encode(codecs.encode(encryption_key * 8)) xor_key = "".join( [ chr(ord(str(c3)) - int(c1, 16) - int(c2, 16)) for (c1, c2, c3) in zip(hd1, hd2, b64_key.decode("utf-8")) ] ) xor_key = blend_strings(xor_key, encryption_key) if len(xor_key) % 7 == 0: xor_key = xor_key + encryption_key[-1] xor_key = shuffle_string((xor_key * 8)[::7]) # Use the str_xor method for the main string obfuscation / de-obfuscation if not already_encrypted: if len(string) > 0: rem1 = (ord_string_sum(string)) % 3 rem2 = (ord_string_sum(string)) % 4 rem3 = (ord_string_sum(string)) % 2 rem4 = (len(string) + ord_string_sum(string)) % 2 if len(string) % 2 != 0: if rem3 == 1: string = ( chr(ord(string[-1]) - 5 - rem1) + string + chr(ord(string[-1]) - 13 - rem1) ) else: string = ( chr(ord(string[-1]) - 11 - rem1) + string + chr(ord(string[-1]) - 23 - rem1) ) elif len(string) > 1: if rem4 == 1: string = ( chr(ord(string[0]) - 19 + rem2) + string + chr(ord(string[0]) - 7 - rem2) ) else: string = ( chr(ord(string[0]) - 26 + rem2) + string + chr(ord(string[0]) - 12 - rem2) ) rem5 = (len(string) + ord_string_sum(string)) % 23 string = rotate(string, rem5) result = str_xor(shuffle_string(string)[::-1], xor_key) rem6 = (len(result) + ord_string_sum(result)) % 17 result = rotate(result, rem6) else: rem6 = (len(string) + ord_string_sum(string)) % 17 string = rotate(string, -rem6) result = reverse_shuffle_string(str_xor(string, xor_key)[::-1]) if len(result) > 2: rem5 = (len(result) + ord_string_sum(result)) % 23 result = rotate(result, -rem5) result = result[1:-1] # Finalize encryption of non-encrypted string if not already_encrypted: result = base64.b64encode(codecs.encode(result)) result = start_token + result.decode("utf-8") + end_token return result ================================================ FILE: seleniumbase/common/exceptions.py ================================================ """ SeleniumBase Exceptions LinkTextNotFoundException => Called when expected link text is not visible. NoSuchFileException => Called when self.assert_downloaded_file(...) fails. NoSuchOptionException => Called when select_option_by_*() lacks the option. NotConnectedException => Called when Internet is not reachable when needed. NotUsingChromeException => Used by Chrome-only methods if not using Chrome. NotUsingChromiumException => Used by Chromium-only methods if not Chromium. OutOfScopeException => Used by BaseCase methods when setUp() is skipped. ProxyConnectionException => Called when the proxy connection failed. TextNotVisibleException => Called when the expected text is not visible. TimeLimitExceededException => Called when exceeding "--time-limit=SECONDS". TimeoutException => Called when some timeout limit has been exceeded. VisualException => Called when there's a Visual Diff Assertion Failure. """ class LinkTextNotFoundException(Exception): pass class NoSuchFileException(Exception): pass class NoSuchOptionException(Exception): pass class NotConnectedException(Exception): pass class NotUsingChromeException(Exception): pass class NotUsingChromiumException(Exception): pass class OutOfScopeException(Exception): pass class ProxyConnectionException(Exception): pass class TextNotVisibleException(Exception): pass class TimeLimitExceededException(Exception): pass class TimeoutException(Exception): pass class VisualException(Exception): pass """ Selenium Exceptions (Simplified for SeleniumBase) """ class WebDriverException(Exception): """Base webdriver exception.""" def __init__(self, msg=None, screen=None, stacktrace=None): super().__init__() self.msg = msg self.screen = screen self.stacktrace = stacktrace def __str__(self): exception_msg = "Message: %s\n" % self.msg if self.screen: exception_msg += "Screenshot: available via screen\n" if self.stacktrace: stacktrace = "\n".join(self.stacktrace) exception_msg += "Stacktrace:\n%s" % stacktrace return exception_msg class InvalidSwitchToTargetException(WebDriverException): """Thrown when frame or window target to be switched doesn't exist.""" class NoSuchFrameException(InvalidSwitchToTargetException): """Thrown when frame target to be switched doesn't exist.""" class NoSuchWindowException(InvalidSwitchToTargetException): """Thrown when window target to be switched doesn't exist.""" class NoSuchElementException(WebDriverException): """Thrown when element could not be found.""" class NoSuchAttributeException(WebDriverException): """Thrown when the attribute of element could not be found.""" class InvalidElementStateException(WebDriverException): """Thrown when a command could not be completed because the element is in an invalid state.""" class NoAlertPresentException(WebDriverException): """Thrown when switching to no presented alert.""" class ElementNotVisibleException(InvalidElementStateException): """Thrown when an element is present in the DOM, but not visible.""" ================================================ FILE: seleniumbase/common/obfuscate.py ================================================ """ Obfuscates a string/password into a string that can be decrypted later on. Usage: python obfuscate.py Then enter the password. The result is an encrypted password. """ from seleniumbase.common import encryption import getpass import time def main(): try: while 1: print("\nEnter password to obfuscate: (CTRL+C to exit)") password = getpass.getpass() print("Verify password:") verify_password = getpass.getpass() if password != verify_password: print("*** ERROR: Passwords don't match! .. Please try again!") continue print("\nHere is the obfuscated password:") time.sleep(0.2) encrypted_password = encryption.decrypt(password) print(encrypted_password) time.sleep(0.2) print("\nInside a test, use the following to decrypt it:\n") time.sleep(0.2) print(" from seleniumbase import encryption") print(' encryption.decrypt("%s")' % encrypted_password) time.sleep(0.2) except KeyboardInterrupt: print("\nExiting...\n") if __name__ == "__main__": main() ================================================ FILE: seleniumbase/common/unobfuscate.py ================================================ """ Unobfuscates an encrypted string/password into a plaintext string/password. Usage: python unobfuscate.py Then enter the encrypted string/password. The result is a plaintext string/password. Works the same as obfuscate.py, but doesn't mask the input. """ from seleniumbase.common import encryption import time def main(): input_method = input try: while 1: code = input_method( "\nEnter obfuscated/encrypted string: (CTRL+C to exit):\n" ) print("\nHere is the unobfuscated string/password:") time.sleep(0.07) print(encryption.decrypt(code)) time.sleep(0.21) except KeyboardInterrupt: print("\nExiting...\n") if __name__ == "__main__": main() ================================================ FILE: seleniumbase/config/__init__.py ================================================ ================================================ FILE: seleniumbase/config/ad_block_list.py ================================================ """ For use with SeleniumBase ad_block functionality. Usage: On the command line: "pytest SOME_TEST.py --ad_block" From inside a test: self.ad_block() If using the command line version, the ad_block functionality gets activated after "self.wait_for_ready_state_complete()" is called, which is always run after page loads, unless changed in "settings.py". Using ad_block will slow down test runs a little. (Use only if necessary.) Format: A CSS Selector that's ready for JavaScript's querySelectorAll() """ AD_BLOCK_LIST = [ '[aria-label="Ads"]', '[src*="adservice."]', '[src*="adclick"]', '[src*="doubleclick"]', '[src*="snigelweb.com"]', '[src*="tagservices.com"]', '[src*="adsby"]', '[src*="adroll.com"]', '[src*="pagead"]', '[src*="3lift"]', '[src*="smartads."]', '[src*="ad_nexus"]', '[src*="/ads/"]', '[src*="moatads.com"]', '[src*="adsystem"]', '[src*="connectad"]', '[src*="/adservice."]', '[src*="syndication.com"]', '[src*="/ads."]', '[src*="lijit"]', '[src*="pagead"]', '[src*="adnxs.com"]', '[src*="onetag-sys.com"]', '[src*="indexww.com"]', '[src*="3lift.com"]', '[src*="rubiconproject.com"]', '[src*="brealtime.com"]', '[src*="33across.com"]', '[src*="adsrvr"]', '[type="data-doubleclick"]', "iframe[data-google-container-id]", 'iframe[src*="doubleclick"]', 'iframe[src*="/AdServer/"]', 'iframe[src*="openx.net"]', 'iframe[onload*="doWithAds"]', 'iframe[id*="_ads_frame"]', 'iframe[style="height:0px;width:0px;display:none;"]', '[aria-label="Ad"]', '[aria-label="Timeline: Trending now"]', '[aria-label="Timeline: Carousel"]', '[aria-roledescription="carousel"]', '[aria-label="Who to follow"]', '[class*="sponsored-content"]', '[class*="adsbygoogle"]', '[class^="adroll"]', '[data-ad-details*="Advertisement"]', '[data-native_ad*="placement"]', '[data-provider="dianomi"]', '[data-type="ad"]', '[data-track-event-label*="-taboola-"]', '[data-ad-feedback-beacon*="AD_"]', "[data-ad-feedback-beacon]", '[data-dcm-click-tracker*="/adclick."]', "[data-google-av-adk]", "[data-google-query-id]", '[data-ylk*="sponsored_cluster"]', "[data-google-av-cxn]", "[data-ad-client]", "[data-ad-slot]", '[href*="doubleclick"]', '[href*="amazon-adsystem"]', '[alt="Advertisement"]', '[alt$=" Ad"]', '[id*="-ad-"]', '[id*="_ads_"]', '[id*="AdFrame"]', '[id*="carbonads"]', '[id^="ad-"]', '[id^="my-ads"]', '[id^="outbrain_widget"]', '[id^="taboola-"]', '[id^="google_ads_frame"]', '[id^="google_ads_iframe"]', '[id="tryitLeaderboard"]', '[id="dianomiRightRail"]', '[allow*="advertising.com"]', "ins.adsby", "li.strm-ad-clusters", "li.js-stream-ad", "div.after_ad", "div.ad-container", "div.ad_module", "div.ad-subnav-container", "div.ad-wrapper", "div.adroll-block", "div.data-ad-container", "div.GoogleActiveViewElement", "div.l-ad", "div.right-ad", "div.wx-adWrapper", 'div.image > a > img[src*="HomepageAd"]', 'img[src*="HomepageAd"]', "img.img_ad", 'link[href*="/adservice."]', "section.dianomi-ad", "ytd-promoted-video-renderer", "ytd-video-masthead-ad-v3-renderer", ] ================================================ FILE: seleniumbase/config/proxy_list.py ================================================ """ Proxy Server "Phone Book". Simplify running browser tests through a proxy server by adding your frequently-used proxies here. Now you can do something like this on the command line: "pytest SOME_TEST.py --proxy=proxy1" Format of PROXY_LIST server entries: * "ip_address:port" OR "username:password@ip_address:port" * "server:port" OR "username:password@server:port" (Do NOT include the http:// or https:// in your proxy string!) Example proxies in PROXY_LIST below are not guaranteed to be active or secure. If you don't already have a proxy server to connect to, you can try finding one from one of following sites: * https://www.sslproxies.org/ * https://bit.ly/36GtZa1 * https://www.us-proxy.org/ * https://hidemy.name/en/proxy-list/ * http://free-proxy.cz/en/proxylist/country/all/https/ping/all """ PROXY_LIST = { "example1": "98.8.195.160:443", # (Example) - set your own proxy here "example2": "200.174.198.86:8888", # (Example) "example3": "socks5://184.178.172.5:15303", # (Example) "proxy1": None, "proxy2": None, "proxy3": None, "proxy4": None, "proxy5": None, } ================================================ FILE: seleniumbase/config/settings.py ================================================ """ You'll probably want to customize this to your own environment and needs. For changes to take effect immediately, use Python's Develop Mode. Develop Mode Install: "pip install -e ." (from the top-level directory) """ # #####>>>>>----- REQUIRED/IMPORTANT SETTINGS -----<<<<<##### # Default maximum time (in seconds) to wait for page elements to appear. # Different methods/actions in base_case.py use different timeouts. # If the element to be acted on does not appear in time, the test fails. MINI_TIMEOUT = 2 SMALL_TIMEOUT = 7 LARGE_TIMEOUT = 10 EXTREME_TIMEOUT = 30 # Default page load timeout. # If a page takes longer than this to load, you'll get the following error: # selenium.common.exceptions.TimeoutException: # Message: timeout: Timed out receiving message from renderer: PLT # In global Selenium settings, this value is set to 300 seconds by default. PAGE_LOAD_TIMEOUT = 120 # Default page load strategy. # ["normal", "eager", "none"] # Selenium default = "normal" PAGE_LOAD_STRATEGY = "normal" # If True, existing logs from past test runs will be saved and take up space. # If False, only the logs from the most recent test run will be saved locally. # You can also archive existing logs on the command line with: "--archive_logs" ARCHIVE_EXISTING_LOGS = False # If True, existing downloads from past runs will be saved and take up space. # If False, only the downloads from the most recent run will be saved locally. ARCHIVE_EXISTING_DOWNLOADS = False # If True, the last page screenshot will include the and background. # If False, the last page screenshot will only include the section. # Depending on the screen size, including a background could make the # appear very small in the screenshot, which may require manually zooming in # on the to see page details if you decide to include the background. SCREENSHOT_WITH_BACKGROUND = False # Default names for files saved during test failures. # (These files will get saved to the "latest_logs/" folder.) SCREENSHOT_NAME = "screenshot.png" BASIC_INFO_NAME = "basic_test_info.txt" PAGE_SOURCE_NAME = "page_source.html" # Default names for files and folders saved when using nosetests reports. # Usage: "--report". (NOSETESTS only) LATEST_REPORT_DIR = "latest_report" REPORT_ARCHIVE_DIR = "archived_reports" HTML_REPORT = "report.html" RESULTS_TABLE = "results_table.csv" """ If True, switch to new tabs/windows automatically if a click opens a new one. (This switch only happens if the initial tab is still on same URL as before, which prevents a situation where a click opens up a new URL in the same tab, where a pop-up might open up a new tab on its own, leading to a double open. If False, the browser will stay on the current tab where the click happened. """ SWITCH_TO_NEW_TABS_ON_CLICK = True """ These methods add global waits, such as self.wait_for_ready_state_complete(), which waits for document.readyState to be "complete" after browser actions. """ # Called after self.open(URL), NOT driver.get(URL) WAIT_FOR_RSC_ON_PAGE_LOADS = True # Called after self.click(selector), NOT element.click() WAIT_FOR_RSC_ON_CLICKS = False # Wait for AngularJS calls to complete after various browser actions. WAIT_FOR_ANGULARJS = True # Skip all calls to wait_for_ready_state_complete() and wait_for_angularjs(). SKIP_JS_WAITS = False # Default time to wait after each browser action performed during Demo Mode. # Use Demo Mode when you want others to see what your automation is doing. # Usage: "--demo_mode". (Can be overwritten by using "--demo_sleep=TIME".) DEFAULT_DEMO_MODE_TIMEOUT = 0.5 # Number of times to repeat the demo_mode highlight animation. # Each loop is about 0.18 seconds. (Override by using "--highlights=TIMES".) HIGHLIGHTS = 4 # Default time to keep messenger notifications visible (in seconds). # Messenger notifications appear when reaching assert statements in Demo Mode. DEFAULT_MESSAGE_DURATION = 2.55 # If True, the Content Security Policy will be disabled on Firefox. # If False, each website's default Content Security Policy will be used. # (A website's CSP may prevent SeleniumBase from loading custom JavaScript.) # If using demo_mode or MasterQA, this value will become True regardless. # You can also disable the CSP on the command line by using "--disable_csp". DISABLE_CSP_ON_FIREFOX = True # If True, the Content Security Policy will be disabled on Chrome. # If False, each website's default Content Security Policy will be used. # (A website's CSP may prevent SeleniumBase from loading custom JavaScript.) # You can also disable the CSP on the command line by using "--disable_csp". DISABLE_CSP_ON_CHROME = False # If True, an Exception is raised immediately for invalid proxy string syntax. # If False, a Warning will appear after the test, with no proxy server used. # (This applies when using --proxy=[PROXY_STRING] for using a proxy server.) RAISE_INVALID_PROXY_STRING_EXCEPTION = True # Default browser coordinates when opening new windows for tests. WINDOW_START_X = 20 WINDOW_START_Y = 54 # Default browser resolutions when opening new windows for tests. # (Headless resolutions take priority, and include all browsers.) # (Firefox starts maximized by default when running in GUI Mode.) CHROME_START_WIDTH = 1280 CHROME_START_HEIGHT = 840 HEADLESS_START_WIDTH = 1440 HEADLESS_START_HEIGHT = 1880 # If True, hides messages related to downloading drivers. # If False, you'll see details about downloading drivers. # (This only affects driver downloads coming from tests.) # (If calling "sbase get chromedriver", then won't hide.) HIDE_DRIVER_DOWNLOADS = False # #####>>>>>----- MasterQA SETTINGS -----<<<<<##### # ##### (Used when importing MasterQA as the parent class) # The default message that appears when you don't specify a custom message MASTERQA_DEFAULT_VALIDATION_MESSAGE = "Does the page look good?" # The time delay (in seconds) before the MasterQA validation pop-up appears. # This value can be overwritten on the command line. Ex: --verify_delay=1.0 MASTERQA_WAIT_TIME_BEFORE_VERIFY = 0.5 # If True, the automation will start in full-screen mode MASTERQA_START_IN_FULL_SCREEN_MODE = False # The maximum idle time allowed (in seconds) before timing out and exiting MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT = 600 # #####>>>>>----- RECOMMENDED SETTINGS -----<<<<<##### # ##### (For multi-factor auth, DB/cloud logging, and password encryption) # Google Authenticator # (For 2-factor authentication using a time-based one-time password algorithm) # (See https://github.com/pyotp/pyotp and https://pypi.org/project/pyotp/ ) # (Also works with Authy and other compatible apps.) # Usage: "self.get_google_auth_password()" (output based on timestamp) # Usage with override: "self.get_google_auth_password(totp_key=TOTP_KEY)" TOTP_KEY = "base32secretABCD" # MySQL DB Credentials # (For saving data from tests to a MySQL DB) # Usage: "--with-db_reporting" DB_HOST = "127.0.0.1" DB_PORT = 3306 DB_USERNAME = "root" DB_PASSWORD = "test" DB_SCHEMA = "test_db" # Amazon S3 Bucket Credentials # (For saving screenshots and other log files from tests) # (Bucket names are unique across all existing bucket names in Amazon S3) # Usage: "--with-s3_logging" S3_LOG_BUCKET = "[S3 BUCKET NAME]" S3_BUCKET_URL = "https://s3.amazonaws.com/[S3 BUCKET NAME]/" S3_SELENIUM_ACCESS_KEY = "[S3 ACCESS KEY]" S3_SELENIUM_SECRET_KEY = "[S3 SECRET KEY]" # ENCRYPTION SETTINGS # (Used for string/password obfuscation) # (You should reset the Encryption Key for every clone of SeleniumBase) ENCRYPTION_KEY = "Pg^.l!8UdJ+Y7dMIe&fl*%!p9@ej]/#tL~3E4%6?" # These tokens are added to the beginning and end of obfuscated passwords. # Helps identify which strings/passwords have been obfuscated. OBFUSCATION_START_TOKEN = "$^*ENCRYPT=" OBFUSCATION_END_TOKEN = "?&#$" ================================================ FILE: seleniumbase/console_scripts/ReadMe.md ================================================

      Console Scripts 🪄

      🌟 SeleniumBase console scripts can do many things, such as downloading web drivers, creating test directories with config files, activating the SeleniumBase Recorder, launching the SeleniumBase Commander, translating tests into other languages, running a Selenium Grid, and more. * Usage: `seleniumbase [COMMAND] [PARAMETERS]` * (simplified): `sbase [COMMAND] [PARAMETERS]` * To list all commands: `seleniumbase --help` (For running tests, [use pytest with SeleniumBase](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md).) ```zsh COMMANDS: get / install [DRIVER] [OPTIONS] methods (List common Python methods) options (List common pytest options) behave-options (List common behave options) gui / commander [OPTIONAL PATH or TEST FILE] behave-gui (SBase Commander for Behave) caseplans [OPTIONAL PATH or TEST FILE] mkdir [DIRECTORY] [OPTIONS] mkfile [FILE.py] [OPTIONS] mkrec / codegen [FILE.py] [OPTIONS] recorder (Open Recorder Desktop App.) record (If args: mkrec. Else: App.) mkpres [FILE.py] [LANG] mkchart [FILE.py] [LANG] print [FILE] [OPTIONS] translate [SB_FILE.py] [LANG] [ACTION] convert [WEBDRIVER_UNITTEST_FILE.py] extract-objects [SB_FILE.py] inject-objects [SB_FILE.py] [OPTIONS] objectify [SB_FILE.py] [OPTIONS] revert-objects [SB_FILE.py] [OPTIONS] encrypt / obfuscate decrypt / unobfuscate proxy (Start a basic proxy server) download server (Get Selenium Grid JAR file) grid-hub [start|stop] [OPTIONS] grid-node [start|stop] --hub=[HOST/IP] * (EXAMPLE: "sbase get chromedriver") * Type "sbase help [COMMAND]" for specific command info. For info on all commands, type: "seleniumbase --help". Use "pytest" for running tests. ```

      get / install

      * Usage: ```zsh sbase get [DRIVER] [OPTIONS] sbase install [DRIVER] [OPTIONS] ``` * Examples: ```zsh sbase get chromedriver sbase get geckodriver sbase get edgedriver sbase get chromedriver 114 sbase get chromedriver 114.0.5735.90 sbase get chromedriver stable sbase get chromedriver beta sbase get chromedriver -p sbase get chromium sbase get cft 131 sbase get chs ``` * Drivers: ```zsh `chromedriver` , `cft`, `uc_driver` `edgedriver` , `chs` , `geckodriver` ``` * Options: ```zsh NUM: A specific driver version or major version integer. If not set, the driver version matches the browser. `-p` / `--path`: Also copy the driver to /usr/local/bin. ``` * Output: Downloads the webdriver to `seleniumbase/drivers/` (`chromedriver` is required for Chrome automation)
      (`geckodriver` is required for Firefox automation)
      (`edgedriver` is required for Edge automation)

      methods

      * Usage: ```zsh sbase methods ``` * Output: Displays common SeleniumBase Python methods.

      options

      * Usage: ```zsh sbase options ``` * Output: Displays common pytest command-line options that are available when using SeleniumBase. ```zsh --browser=BROWSER (The web browser to use. Default is "chrome") --edge / --firefox / --safari (Shortcut for browser selection.) --headless (Run tests headlessly. Default mode on Linux OS.) --demo (Slow down and visually see test actions as they occur.) --slow (Slow down the automation. Faster than using Demo Mode.) --rs / --reuse-session (Reuse browser session between tests.) --crumbs (Clear all cookies between tests reusing a session.) --maximize (Start tests with the web browser window maximized.) --dashboard (Enable SeleniumBase\'s Dashboard at dashboard.html) --incognito (Enable Chromium\'s Incognito mode.) --guest (Enable Chromium\'s Guest Mode.) --dark (Enable Chromium\'s Dark Mode.) --uc (Use undetected-chromedriver to evade detection.) -m=MARKER (Run tests with the specified pytest marker.) -n=NUM (Multithread the tests using that many threads.) -v (Verbose mode. Print the full names of each test run.) --html=report.html (Create a detailed pytest-html report.) --collect-only / --co (Only show discovered tests. No run.) --co -q (Only show full names of discovered tests. No run.) -x (Stop running tests after the first failure is reached.) --pdb (Enter the Post Mortem Debug Mode after any test fails.) --trace (Enter Debug Mode immediately after starting any test.) | Debug Mode Commands >>> help / h: List all commands. | | n: Next line of method. s: Step through. c: Continue. | | return / r: Run until method returns. j: Jump to line. | | where / w: Show stack spot. u: Up stack. d: Down stack. | | longlist / ll: See code. dir(): List namespace objects. | --help / -h (Display list of all available pytest options.) --final-debug (Enter Final Debug Mode after each test ends.) --recorder / --rec (Save browser actions as Python scripts.) --rec-behave / --rec-gherkin (Save actions as Gherkin code.) --rec-print (Display recorded scripts when they are created.) --save-screenshot (Save a screenshot at the end of each test.) --archive-logs (Archive old log files instead of deleting them.) --check-js (Check for JavaScript errors after page loads.) --start-page=URL (The browser start page when tests begin.) --agent=STRING (Modify the web browser\'s User-Agent string.) --mobile (Use Chromium\'s mobile device emulator during tests.) --metrics=STRING (Set mobile "CSSWidth,CSSHeight,PixelRatio".) --ad-block (Block some types of display ads after page loads.) --settings-file=FILE (Override default SeleniumBase settings.) --env=ENV (Set the test env. Access with "self.env" in tests.) --data=DATA (Extra test data. Access with "self.data" in tests.) --disable-csp (Disable the Content Security Policy of websites.) --remote-debug (Sync to Ch-R-Debugger chrome://inspect/#devices) --server=SERVER (The Selenium Grid server/IP used for tests.) --port=PORT (The Selenium Grid port used by the test server.) --proxy=SERVER:PORT (Connect to a proxy server:port for tests.) --proxy=USER:PASS@SERVER:PORT (Use authenticated proxy server.) For the full list of command-line options, type: "pytest --help". ```

      behave-options

      * Usage: ```zsh sbase behave-options ``` * Output: Displays common Behave command-line options that are available when using SeleniumBase. ```zsh -D browser=BROWSER (The web browser to use. Default is "chrome") -D headless (Run tests headlessly. Default mode on Linux OS.) -D demo (Slow down and visually see test actions as they occur.) -D slow (Slow down the automation. Faster than using Demo Mode.) -D reuse-session / -D rs (Reuse browser session between tests.) -D crumbs (Clear all cookies between tests reusing a session.) -D maximize (Start tests with the web browser window maximized.) -D dashboard (Enable SeleniumBase\'s Dashboard at dashboard.html) -D incognito (Enable Chromium\'s Incognito Mode.) -D guest (Enable Chromium\'s Guest Mode.) -D dark (Enable Chromium\'s Dark Mode.) -D uc (Use undetected-chromedriver to evade detection.) --no-snippets / -q (Quiet mode. Don\'t print snippets.) --dry-run / -d (Dry run. Only show discovered tests.) --stop (Stop running tests after the first failure is reached.) -D pdb (Enter the Post Mortem Debug Mode after any test fails.) | Debug Mode Commands >>> help / h: List all commands. | | n: Next line of method. s: Step through. c: Continue. | | return / r: Run until method returns. j: Jump to line. | | where / w: Show stack spot. u: Up stack. d: Down stack. | | longlist / ll: See code. dir(): List namespace objects. | -D recorder (Record browser actions to generate test scripts.) -D rec-print (Display recorded scripts when they are created.) -D save-screenshot (Save a screenshot at the end of each test.) -D archive-logs (Archive old log files instead of deleting them.) -D check-js (Check for JavaScript errors after page loads.) -D start-page=URL (The browser start page when tests begin.) -D agent=STRING (Modify the web browser\'s User-Agent string.) -D mobile (Use Chromium\'s mobile device emulator during tests.) -D metrics=STRING (Set mobile "CSSWidth,CSSHeight,PixelRatio".) -D ad-block (Block some types of display ads after page loads.) -D settings-file=FILE (Override default SeleniumBase settings.) -D env=ENV (Set the test env. Access with "self.env" in tests.) -D data=DATA (Extra test data. Access with "self.data" in tests.) -D disable-csp (Disable the Content Security Policy of websites.) -D remote-debug (Sync to Ch-R-Debugger chrome://inspect/#devices) -D server=SERVER (The Selenium Grid server/IP used for tests.) -D port=PORT (The Selenium Grid port used by the test server.) -D proxy=SERVER:PORT (Connect to a proxy server:port for tests.) -D proxy=USER:PASS@SERVER:PORT (Use authenticated proxy server.) For the full list of command-line options, type: "behave --help". ```

      gui / commander

      * Usage: ```zsh sbase gui [OPTIONAL PATH or TEST FILE] sbase commander [OPTIONAL PATH or TEST FILE] ```

      behave-gui

      * Usage: ```zsh sbase behave-gui [OPTIONAL PATH or TEST FILE] sbase gui-behave [OPTIONAL PATH or TEST FILE] ``` * Examples: ```zsh sbase behave-gui sbase behave-gui -i=calculator sbase behave-gui features/ sbase behave-gui features/calculator.feature ``` * Output: Launches SeleniumBase Commander / GUI for Behave.

      caseplans

      * Usage: ```zsh sbase caseplans [OPTIONAL PATH or TEST FILE] ``` * Examples: ```zsh sbase caseplans sbase caseplans -k agent sbase caseplans -m marker2 sbase caseplans test_suite.py sbase caseplans offline_examples/ ``` * Output: Launches the SeleniumBase Case Plans Generator.

      mkdir

      * Usage: ```zsh sbase mkdir [DIRECTORY] [OPTIONS] ``` * Example: ```zsh sbase mkdir ui_tests ``` * Options: ```zsh -b / --basic (Only config files. No tests added.) --gha (Include GitHub Actions YML with defaults.) ``` * Output: Creates a new folder for running SBase scripts. The new folder contains default config files, sample tests for helping new users get started, and Python boilerplates for setting up customized test frameworks. ```zsh ui_tests/ ├── __init__.py ├── my_first_test.py ├── parameterized_test.py ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── test_demo_site.py └── boilerplates/ ├── __init__.py ├── base_test_case.py ├── boilerplate_test.py ├── classic_obj_test.py ├── page_objects.py ├── sb_fixture_test.py └── samples/ ├── __init__.py ├── google_objects.py ├── google_test.py ├── sb_swag_test.py └── swag_labs_test.py ``` If running with the `-b` or `--basic` option: ```zsh ui_tests/ ├── __init__.py ├── pytest.ini ├── requirements.txt └── setup.cfg ``` If running with the `--gha` option, this is added: ```zsh ui_tests/ └── .github └── workflows/ └── python-package.yml ```

      mkfile

      * Usage: ```zsh sbase mkfile [FILE.py] [OPTIONS] ``` * Example: ```zsh sbase mkfile new_test.py ``` * Options: ```zsh --uc (UC Mode boilerplate using SB context manager) -b / --basic (Basic boilerplate / single-line test) -r / --rec (Adds Pdb+ breakpoint for Recorder Mode) --url=URL (Makes the test start on a specific page) ``` * Language Options: ```zsh --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish ``` * Syntax Formats: ```zsh --bc / --basecase (BaseCase class inheritance) --pf / --pytest-fixture (sb pytest fixture) --cf / --class-fixture (class + sb pytest fixture) --cm / --context-manager (SB context manager) --dc / --driver-context (DriverContext manager) --dm / --driver-manager (Driver manager) ``` * Output: Creates a new SBase test file with boilerplate code. If the file already exists, an error is raised. By default, uses English with BaseCase inheritance, and creates a boilerplate with common SeleniumBase methods: "open", "type", "click", "assert_element", and "assert_text". If using the basic boilerplate option, only the "open" method is included. Only the BaseCase format supports Languages or Recorder Mode. UC Mode automatically uses English with SB() format.

      mkrec / record / codegen

      * Usage: ```zsh sbase mkrec [FILE.py] [OPTIONS] sbase codegen [FILE.py] [OPTIONS] ``` * Examples: ```zsh sbase mkrec new_test.py sbase mkrec new_test.py --url=seleniumbase.io sbase codegen new_test.py sbase codegen new_test.py --url=wikipedia.org ``` * Options: ```zsh --url=URL (Sets the initial start page URL.) --edge (Use Edge browser instead of Chrome.) --gui / --headed (Use headed mode on Linux.) --uc / --undetected (Use undetectable mode.) --ee (Use SHIFT + ESC to end the recording.) --overwrite (Overwrite file when it exists.) --behave (Also output Behave/Gherkin files.) ``` * Output: Creates a new SeleniumBase test using the Recorder. If the filename already exists, an error is raised.

      recorder

      * Usage: ```zsh sbase recorder [OPTIONS] ``` * Options: ```zsh --uc / --undetected (Use undetectable mode.) --behave (Also output Behave/Gherkin files.) ``` * Output: Launches the SeleniumBase Recorder Desktop App.

      mkpres

      * Usage: ```zsh sbase mkpres [FILE.py] [LANG] ``` * Example: ```zsh sbase mkpres new_presentation.py --en ``` * Language Options: ```zsh --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish ``` * Output: Creates a new presentation with 3 example slides. If the file already exists, an error is raised. By default, the slides are written in English, and use "serif" theme with "slide" transition. The slides can be used as a basic boilerplate.

      mkchart

      * Usage: ```zsh sbase mkchart [FILE.py] [LANG] ``` * Example: ```zsh sbase mkchart new_chart.py --en ``` * Language Options: ```zsh --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish ``` * Output: Creates a new SeleniumBase chart presentation. If the file already exists, an error is raised. By default, the slides are written in English, and use a "sky" theme with "slide" transition. The chart can be used as a basic boilerplate.

      print

      * Usage: ```zsh sbase print [FILE] [OPTIONS] ``` * Options: ```zsh -n (Add line Numbers to the rows) ``` * Output: Prints the code/text of any file with syntax-highlighting.

      translate

      * Usage: ```zsh sbase translate [SB_FILE.py] [LANGUAGE] [ACTION] ``` * Languages: ```zsh --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish ``` * Actions: ```zsh -p / --print (Print translation output to the screen) -o / --overwrite (Overwrite the file being translated) -c / --copy (Copy the translation to a new `.py` file) ``` * Options: ```zsh -n (include line Numbers when using the Print action) ``` * Output: Translates a SeleniumBase Python file into the language specified. Method calls and "import" lines get swapped. Both a language and an action must be specified. The `-p` action can be paired with one other action. When running with `-c` (or `--copy`), the new file name will be the original name appended with an underscore plus the 2-letter language code of the new language. (Example: Translating "test_1.py" into Japanese with `-c` will create a new file called "test_1_ja.py".)

      extract-objects

      * Usage: ```zsh sbase extract-objects [SB_FILE.py] ``` * Output: Creates page objects based on selectors found in a seleniumbase Python file and saves those objects to the "page_objects.py" file in the same folder as the tests.

      inject-objects

      * Usage: ```zsh sbase inject-objects [SB_FILE.py] [OPTIONS] ``` * Options: ```zsh -c / --comments (Add object selectors to the comments.) ``` * Output: Takes the page objects found in the "page_objects.py" file and uses those to replace matching selectors in the selected seleniumbase Python file.

      objectify

      * Usage: ```zsh sbase objectify [SB_FILE.py] [OPTIONS] ``` * Options: ```zsh -c / --comments (Add object selectors to the comments.) ``` * Output: A modified version of the file where the selectors have been replaced with variable names defined in "page_objects.py", supporting the Page Object Pattern. (This has the same outcome as combining `extract-objects` with `inject-objects`)

      revert-objects

      * Usage: ```zsh sbase revert-objects [SB_FILE.py] [OPTIONS] ``` * Options: ```zsh -c / --comments (Keep existing comments for the lines.) ``` * Output: Reverts the changes made by `seleniumbase objectify ...` or `seleniumbase inject-objects ...` when run against a seleniumbase Python file. Objects will get replaced by selectors stored in the "page_objects.py" file.

      convert

      * Usage: ```zsh sbase convert [WEBDRIVER_UNITTEST_FILE.py] ``` * Output: Converts a Selenium IDE exported WebDriver unittest file into a SeleniumBase file. Adds `_SB` to the new filename while keeping the original file intact. Works on both Selenium IDE & Katalon Recorder scripts.

      encrypt / obfuscate

      * Usage: `sbase encrypt` / `sbase obfuscate` * Output: Runs the password encryption/obfuscation tool. (Where you can enter a password to encrypt/obfuscate.)

      decrypt / unobfuscate

      * Usage: `sbase decrypt` / `sbase unobfuscate` * Output: Runs the password decryption/unobfuscation tool. (Where you can enter an encrypted password to decrypt.)

      proxy

      * Usage: ```zsh sbase proxy [OPTIONS] ``` * Options: ```zsh --hostname=HOSTNAME (Set `hostname`) (Default: `127.0.0.1`) --port=PORT (Set `port`) (Default: `8899`) --help / -h (Display available `proxy` options.) ``` * Output: Launch a basic proxy server on the current machine. (Uses `127.0.0.1:8899` as the default address.)

      download

      * Usage: ```zsh sbase download server ``` * Output: Downloads the Selenium Server JAR file for Grid usage. (That JAR file is required when using a Selenium Grid)

      grid-hub

      * Usage: ```zsh sbase grid-hub [start|stop|restart] [OPTIONS] ``` * Options: ```zsh -v / --verbose (Increases verbosity of logging output.) --timeout=TIMEOUT (Close idle browser windows after TIMEOUT seconds.) ``` * Output: Controls the Selenium Grid Hub server, which allows for running tests on multiple machines in parallel to speed up test runs and reduce the total time of test suite execution. You can start, restart, or stop the Grid Hub server.

      grid-node

      * Usage: ```zsh sbase grid-node [start|stop|restart] [OPTIONS] ``` * Options: ```zsh --hub=HUB_IP (Grid Hub IP Address. Default: `127.0.0.1`) -v / --verbose (Increases verbosity of logging output.) ``` * Output: Controls the Selenium Grid node, which serves as a worker machine for your Selenium Grid Hub server. You can start, restart, or stop the Grid node. -------- ================================================ FILE: seleniumbase/console_scripts/__init__.py ================================================ ================================================ FILE: seleniumbase/console_scripts/logo_helper.py ================================================ """ SeleniumBase Logo Processing (for the console scripts interface) Logo generated from: http://www.patorjk.com/software/taag/#p=display&f=Slant&t=SeleniumBase """ import colorama import os from contextlib import suppress r""" ______ __ _ ____ / ____/__ / /__ ____ (_)_ ______ ___ / _ \____ ________ \__ \/ _ \/ / _ \/ __ \/ / / / / __ `__ \ / /_) / __ \/ ___/ _ \ ___/ / __/ / __/ / / / / /_/ / / / / / // /_) / (_/ /__ / __/ /____/\___/_/\___/_/ /_/_/\__,_/_/ /_/ /_//_____/\__,_/____/\___/ """ def get_seleniumbase_logo(): c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Back.CYAN c4 = colorama.Back.GREEN cr = colorama.Style.RESET_ALL sb = " " sb += cr sb += "\n" sb += c1 sb += " ______ __ _ " sb += c2 sb += "____ " sb += cr sb += "\n" sb += c1 sb += " / ____/__ / /__ ____ (_)_ ______ ___ " sb += c2 sb += "/ _ \\____ ________ " sb += cr sb += "\n" sb += c1 sb += " \\__ \\/ _ \\/ / _ \\/ __ \\/ / / / / __ `__ \\ " sb += c2 sb += "/ /_) / __ \\/ ___/ _ \\" sb += cr sb += "\n" sb += c1 sb += " ___/ / __/ / __/ / / / / /_/ / / / / / /" sb += c2 sb += "/ /_) / (_/ /__ / __/" sb += cr sb += "\n" sb += c1 sb += "/____/\\___/_/\\___/_/ /_/_/\\__,_/_/ /_/ /_/" sb += c2 sb += "/_____/\\__,_/____/\\___/ " sb += cr sb += "\n" sb += c3 sb += " " sb += c4 sb += " " sb += cr sb += cr with suppress(Exception): terminal_width = os.get_terminal_size().columns if isinstance(terminal_width, int) and terminal_width >= 66: return sb # If the logo is wider than the screen width, use a smaller one: r""" ___ _ _ ___ / __| ___| |___ _ _ (_)_ _ _ __ | _ ) __ _ ______ \__ \/ -_) / -_) ' \| | \| | ' \ | _ \/ _` (_-< -_) |___/\___|_\___|_||_|_|\_,_|_|_|_\|___/\__,_/__|___| """ sb = " " sb += cr sb += "\n" sb += c1 sb += " ___ _ _ " sb += c2 sb += " ___ " sb += cr sb += "\n" sb += c1 sb += "/ __| ___| |___ _ _ (_)_ _ _ __ " sb += c2 sb += "| _ ) __ _ ______ " sb += cr sb += "\n" sb += c1 sb += "\\__ \\/ -_) / -_) ' \\| | \\| | ' \\ " sb += c2 sb += "| _ \\/ _` (_-< -_)" sb += cr sb += "\n" sb += c1 sb += "|___/\\___|_\\___|_||_|_|\\_,_|_|_|_\\" sb += c2 sb += "|___/\\__,_/__|___|" sb += cr sb += "\n" sb += c3 sb += " " sb += c4 sb += " " sb += cr sb += cr return sb ================================================ FILE: seleniumbase/console_scripts/rich_helper.py ================================================ from rich.console import Console from rich.markdown import Markdown from rich.syntax import Syntax def process_syntax(code, lang, theme, line_numbers, code_width, word_wrap): syntax = Syntax( code, lang, theme=theme, line_numbers=line_numbers, code_width=code_width, word_wrap=word_wrap, ) return syntax def get_code_without_tag(code, tag): # Remove TAG from Code/HTML, but keep inner text. # Eg: Hello! becomes: "Hello!" tag_start = "<%s " % tag tag_solo = "<%s>" % tag tag_end = "" % tag while tag_start in code: start = code.find(tag_start) if start == -1: break end = code.find(">", start + 1) + 1 if end == 0: break code = code[:start] + code[end:] code = code.replace(tag_solo, "") code = code.replace(tag_end, "") return code def display_markdown(code): try: markdown = Markdown(code) console = Console() console.print(markdown) return True # Success except Exception: return False # Failure def display_code(code): try: console = Console() console.print(code) return True # Success except Exception: return False # Failure def fix_emoji_spacing(code): try: # Fix the display width of certain emojis that take up two spaces double_width_emojis = [ "👁️", "🗺️", "🖼️", "🗄️", "♻️", "🗂️", "🖥️", "🕹️", "🎞️", "🎛️", "🎖️", "☀️", "⏺️", "▶️", "↘️", "⬇️", "↙️", "⬅️", "↖️", "⬆️", "↗️", "➡️", ] for emoji in double_width_emojis: if emoji in code: code = code.replace(emoji, emoji + " ") code = code.replace("✅<", "✅ <") code = code.replace("❌<", "❌ <") except Exception: pass return code ================================================ FILE: seleniumbase/console_scripts/run.py ================================================ """ SeleniumBase console scripts runner Usage: seleniumbase [COMMAND] [PARAMETERS] OR sbase [COMMAND] [PARAMETERS] Examples: sbase get chromedriver sbase methods sbase options sbase commander sbase behave-gui sbase behave-options sbase caseplans sbase mkdir ui_tests sbase mkfile new_test.py sbase mkrec new_test.py sbase mkrec new_test.py --url=wikipedia.org sbase codegen new_test.py --url=wikipedia.org sbase recorder sbase record new_test.py sbase record sbase mkpres new_presentation.py sbase mkchart new_chart.py sbase convert webdriver_unittest_file.py sbase print my_first_test.py -n sbase translate my_first_test.py --zh -p sbase extract-objects my_first_test.py sbase inject-objects my_first_test.py sbase objectify my_first_test.py sbase revert-objects my_first_test.py sbase encrypt sbase decrypt sbase proxy sbase proxy --hostname=127.0.0.1 --port=8899 sbase download server sbase grid-hub start sbase grid-node start --hub=127.0.0.1 """ import colorama import sys import time from contextlib import suppress from seleniumbase.config import settings from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils def show_basic_usage(): from seleniumbase.console_scripts import logo_helper seleniumbase_logo = logo_helper.get_seleniumbase_logo() print(seleniumbase_logo) time.sleep(0.035) print("") time.sleep(0.031) show_package_location() time.sleep(0.031) show_version_info() time.sleep(0.031) print("") time.sleep(0.555) # Enough time to see the logo & version sc = "" sc += "╭──────────────────────────────────────────────────╮\n" sc += '│ * USAGE: "seleniumbase [COMMAND] [PARAMETERS]" │\n' sc += '│ * OR: "sbase [COMMAND] [PARAMETERS]" │\n' sc += "│ │\n" sc += "│ COMMANDS: PARAMETERS / DESCRIPTIONS: │\n" sc += "│ get / install [DRIVER_NAME] [OPTIONS] │\n" sc += "│ methods (List common Python methods) │\n" sc += "│ options (List common pytest options) │\n" sc += "│ behave-options (List common behave options) │\n" sc += "│ gui / commander [OPTIONAL PATH or TEST FILE] │\n" sc += "│ behave-gui (SBase Commander for Behave) │\n" sc += "│ caseplans [OPTIONAL PATH or TEST FILE] │\n" sc += "│ mkdir [DIRECTORY] [OPTIONS] │\n" sc += "│ mkfile [FILE.py] [OPTIONS] │\n" sc += "│ mkrec / codegen [FILE.py] [OPTIONS] │\n" sc += "│ recorder (Open Recorder Desktop App.) │\n" sc += "│ record (If args: mkrec. Else: App.) │\n" sc += "│ mkpres [FILE.py] [LANG] │\n" sc += "│ mkchart [FILE.py] [LANG] │\n" sc += "│ print [FILE] [OPTIONS] │\n" sc += "│ translate [SB_FILE.py] [LANG] [ACTION] │\n" sc += "│ convert [WEBDRIVER_UNITTEST_FILE.py] │\n" sc += "│ extract-objects [SB_FILE.py] │\n" sc += "│ inject-objects [SB_FILE.py] [OPTIONS] │\n" sc += "│ objectify [SB_FILE.py] [OPTIONS] │\n" sc += "│ revert-objects [SB_FILE.py] [OPTIONS] │\n" sc += "│ encrypt / obfuscate │\n" sc += "│ decrypt / unobfuscate │\n" sc += "│ proxy (Start a basic proxy server) │\n" sc += "│ download server (Get Selenium Grid JAR file) │\n" sc += "│ grid-hub [start|stop] [OPTIONS] │\n" sc += "│ grid-node [start|stop] --hub=[HOST/IP] │\n" sc += "│ │\n" sc += '│ * EXAMPLE => "sbase get chromedriver stable" │\n' sc += '│ * For command info => "sbase help [COMMAND]" │\n' sc += '│ * For info on all commands => "sbase --help" │\n' sc += "╰──────────────────────────────────────────────────╯" sc += "" bordered_sc = sc if "linux" not in sys.platform: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = sc.replace("seleniumbase", c1 + "selenium" + c2 + "base" + cr) sc = sc.replace("sbase", c1 + "s" + c2 + "base" + cr) sc = sc.replace("[COMMAND]", c3 + "[COMMAND]" + cr) sc = sc.replace("--help", c4 + "--help" + cr) sc = sc.replace("help", c4 + "help" + cr) with suppress(Exception): print(sc) return sc = bordered_sc.replace("╮\n", "") sc = sc.replace("╭", "").replace("╮", "").replace("│", "") sc = sc.replace("╰", "").replace("╯", "").replace("─", "") if "linux" not in sys.platform: sc = sc.replace("seleniumbase", c1 + "selenium" + c2 + "base" + cr) sc = sc.replace("sbase", c1 + "s" + c2 + "base" + cr) sc = sc.replace("[COMMAND]", c3 + "[COMMAND]" + cr) sc = sc.replace("--help", c4 + "--help" + cr) sc = sc.replace("help", c4 + "help" + cr) print(sc) def show_install_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "get / install" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase install [DRIVER_NAME] [OPTIONS]") print(" OR: seleniumbase get [DRIVER_NAME] [OPTIONS]") print(" OR: sbase install [DRIVER_NAME] [OPTIONS]") print(" OR: sbase get [DRIVER_NAME] [OPTIONS]") print(" (Drivers: chromedriver, cft, uc_driver,") print(" edgedriver, chs, geckodriver)") print(" Options:") print(" VERSION Specify the version to download.") print(" Tries to detect the needed version.") print(" If using chromedriver or edgedriver,") print(" you can use the major version integer.") print() print(" -p / --path Also copy driver to /usr/local/bin") print(" Examples:") print(" sbase get chromedriver") print(" sbase get geckodriver") print(" sbase get edgedriver") print(" sbase get chromedriver 114") print(" sbase get chromedriver 114.0.5735.90") print(" sbase get chromedriver stable") print(" sbase get chromedriver beta") print(" sbase get chromedriver -p") print(" sbase get cft 131") print(" sbase get chs") print(" Output:") print(" Downloads the webdriver to seleniumbase/drivers/") print(" (chromedriver is required for Chrome automation)") print(" (geckodriver is required for Firefox automation)") print(" (edgedriver is required for MS__Edge automation)") print(" (cft is for the `Chrome for Testing` binary exe)") print(" (chs is for the `Chrome-Headless-Shell` binary.)") print("") def show_commander_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "commander" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase commander [OPTIONAL PATH or TEST FILE]") print(" OR: sbase commander [OPTIONAL PATH or TEST FILE]") print(" OR: seleniumbase gui [OPTIONAL PATH or TEST FILE]") print(" OR: sbase gui [OPTIONAL PATH or TEST FILE]") print(" Examples:") print(" sbase gui") print(" sbase gui -k agent") print(" sbase gui -m marker2") print(" sbase gui test_suite.py") print(" sbase gui offline_examples/") print(" Output:") print(" Launches SeleniumBase Commander | GUI for pytest.") print("") def show_behave_gui_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "behave-gui" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase behave-gui [OPTIONAL PATH or TEST FILE]") print(" seleniumbase gui-behave [OPTIONAL PATH or TEST FILE]") print(" OR: sbase behave-gui [OPTIONAL PATH or TEST FILE]") print(" OR: sbase gui-behave [OPTIONAL PATH or TEST FILE]") print(" Examples:") print(" sbase behave-gui") print(" sbase behave-gui features/") print(" sbase behave-gui features/calculator.feature") print(" Output:") print(" Launches SeleniumBase Commander | GUI for Behave.") print("") def show_caseplans_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "caseplans" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase caseplans [OPTIONAL PATH or TEST FILE]") print(" OR: sbase caseplans [OPTIONAL PATH or TEST FILE]") print(" Examples:") print(" sbase caseplans") print(" sbase caseplans -k agent") print(" sbase caseplans -m marker2") print(" sbase caseplans test_suite.py") print(" sbase caseplans offline_examples/") print(" Output:") print(" Launches the SeleniumBase Case Plans Generator.") print("") def show_mkdir_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "mkdir" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase mkdir [DIRECTORY] [OPTIONS]") print(" OR: sbase mkdir [DIRECTORY] [OPTIONS]") print(" Example:") print(" sbase mkdir ui_tests") print(" Options:") print(" -b / --basic (Only config files. No tests added.)") print(" --gha (Include GitHub Actions YML with defaults.)") print(" Output:") print(" Creates a new folder for running SBase scripts.") print(" The new folder contains default config files,") print(" sample tests for helping new users get started,") print(" and Python boilerplates for setting up customized") print(" test frameworks.") print("") def show_mkfile_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "mkfile" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase mkfile [FILE.py] [OPTIONS]") print(" OR: sbase mkfile [FILE.py] [OPTIONS]") print(" Example:") print(" sbase mkfile new_test.py") print(" Options:") print(" --uc (UC Mode boilerplate using SB context manager)") print(" -b / --basic (Basic boilerplate / single-line test)") print(" -r / --rec (Adds Pdb+ breakpoint for Recorder Mode)") print(" --url=URL (Makes the test start on a specific page)") print(" Language Options:") print(" --en / --English | --zh / --Chinese") print(" --nl / --Dutch | --fr / --French") print(" --it / --Italian | --ja / --Japanese") print(" --ko / --Korean | --pt / --Portuguese") print(" --ru / --Russian | --es / --Spanish") print(" Syntax Formats:") print(" --bc / --basecase (BaseCase class inheritance)") print(" --pf / --pytest-fixture (sb pytest fixture)") print(" --cf / --class-fixture (class + sb pytest fixture)") print(" --cm / --context-manager (SB context manager)") print(" --dc / --driver-context (DriverContext manager)") print(" --dm / --driver-manager (Driver manager)") print(" Output:") print(" Creates a new SBase test file with boilerplate code.") print(" If the file already exists, an error is raised.") print(" By default, uses English with BaseCase inheritance,") print(" and creates a boilerplate with common SeleniumBase") print(' methods: "open", "type", "click", "assert_element",') print(' and "assert_text". If using the basic boilerplate') print(' option, only the "open" method is included. Only the') print(" BaseCase format supports Languages or Recorder Mode.") print(" UC Mode automatically uses English with SB() format.") print("") def show_mkrec_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "mkrec" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase mkrec [FILE.py] [OPTIONS]") print(" OR: sbase mkrec [FILE.py] [OPTIONS]") print(" Examples:") print(" sbase mkrec new_test.py") print(" sbase mkrec new_test.py --url=wikipedia.org") print(" Options:") print(" --url=URL (Sets the initial start page URL.)") print(" --edge (Use Edge browser instead of Chrome.)") print(" --gui / --headed (Use headed mode on Linux.)") print(" --uc / --undetected (Use undetectable mode.)") print(" --ee (Use SHIFT + ESC to end the recording.)") print(" --overwrite (Overwrite file when it exists.)") print(" --behave (Also output Behave/Gherkin files.)") print(" Output:") print(" Creates a new SeleniumBase test using the Recorder.") print(" If the filename already exists, an error is raised.") print("") def show_codegen_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "codegen" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase codegen [FILE.py] [OPTIONS]") print(" OR: sbase codegen [FILE.py] [OPTIONS]") print(" Examples:") print(" sbase codegen new_test.py") print(" sbase codegen new_test.py --url=wikipedia.org") print(" Options:") print(" --url=URL (Sets the initial start page URL.)") print(" --edge (Use Edge browser instead of Chrome.)") print(" --gui / --headed (Use headed mode on Linux.)") print(" --uc / --undetected (Use undetectable mode.)") print(" --ee (Use SHIFT + ESC to end the recording.)") print(" --overwrite (Overwrite file when it exists.)") print(" --behave (Also output Behave/Gherkin files.)") print(" Output:") print(" Creates a new SeleniumBase test using the Recorder.") print(" If the filename already exists, an error is raised.") print("") def show_recorder_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "recorder" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase recorder [OPTIONS]") print(" OR: sbase recorder [OPTIONS]") print(" Options:") print(" --uc / --undetected (Use undetectable mode.)") print(" --behave (Also output Behave/Gherkin files.)") print(" Output:") print(" Launches the SeleniumBase Recorder Desktop App.") print("") def show_mkpres_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "mkpres" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase mkpres [FILE.py] [LANG]") print(" OR: sbase mkpres [FILE.py] [LANG]") print(" Example:") print(" sbase mkpres new_presentation.py --en") print(" Language Options:") print(" --en / --English | --zh / --Chinese") print(" --nl / --Dutch | --fr / --French") print(" --it / --Italian | --ja / --Japanese") print(" --ko / --Korean | --pt / --Portuguese") print(" --ru / --Russian | --es / --Spanish") print(" Output:") print(" Creates a new presentation with 3 example slides.") print(" If the file already exists, an error is raised.") print(" By default, the slides are written in English,") print(' and use "serif" theme with "slide" transition.') print(" The slides can be used as a basic boilerplate.") print("") def show_mkchart_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "mkchart" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase mkchart [FILE.py] [LANG]") print(" OR: sbase mkchart [FILE.py] [LANG]") print(" Example:") print(" sbase mkchart new_chart.py --en") print(" Language Options:") print(" --en / --English | --zh / --Chinese") print(" --nl / --Dutch | --fr / --French") print(" --it / --Italian | --ja / --Japanese") print(" --ko / --Korean | --pt / --Portuguese") print(" --ru / --Russian | --es / --Spanish") print(" Output:") print(" Creates a new SeleniumBase chart presentation.") print(" If the file already exists, an error is raised.") print(" By default, the slides are written in English,") print(' and use a "sky" theme with "slide" transition.') print(" The chart can be used as a basic boilerplate.") print("") def show_convert_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "convert" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase convert [WEBDRIVER_UNITTEST_FILE.py]") print(" OR: sbase convert [WEBDRIVER_UNITTEST_FILE.py]") print(" Output:") print(" Converts a Selenium IDE exported WebDriver unittest") print(" file into a SeleniumBase file. Adds _SB to the new") print(" file name while keeping the original file intact.") print(" (Works with Katalon Recorder Selenium scripts.)") print("") def show_print_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "print" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase print [FILE] [OPTIONS]") print(" OR: sbase print [FILE] [OPTIONS]") print(" Options:") print(" -n (Add line Numbers to the rows)") print(" Output:") print(" Prints the code/text of any file") print(" with syntax-highlighting.") print("") def show_translate_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "translate" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase translate [SB_FILE.py] [LANG] [ACTION]") print(" OR: sbase translate [SB_FILE.py] [LANG] [ACTION]") print(" Languages:") print(" --en / --English | --zh / --Chinese") print(" --nl / --Dutch | --fr / --French") print(" --it / --Italian | --ja / --Japanese") print(" --ko / --Korean | --pt / --Portuguese") print(" --ru / --Russian | --es / --Spanish") print(" Actions:") print(" -p / --print (Print translation output to the screen)") print(" -o / --overwrite (Overwrite the file being translated)") print(" -c / --copy (Copy the translation to a new .py file)") print(" Options:") print(" -n (include line Numbers when using the Print action)") print(" Output:") print(" Translates a SeleniumBase Python file into the language") print(' specified. Method calls and "import" lines get swapped.') print(" Both a language and an action must be specified.") print(' The "-p" action can be paired with one other action.') print(' When running with "-c" (or "--copy"), the new file name') print(" will be the original name appended with an underscore") print(" plus the 2-letter language code of the new language.") print(' (Example: Translating "test_1.py" into Japanese with') print(' "-c" will create a new file called "test_1_ja.py".)') print("") def show_extract_objects_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "extract-objects" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase extract-objects [SB_FILE.py]") print(" OR: sbase extract-objects [SB_FILE.py]") print(" Output:") print(" Creates page objects based on selectors found in a") print(" seleniumbase Python file and saves those objects to the") print(' "page_objects.py" file in the same folder as the tests.') print("") def show_inject_objects_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "inject-objects" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase inject-objects [SB_FILE.py] [OPTIONS]") print(" OR: sbase inject-objects [SB_FILE.py] [OPTIONS]") print(" Options:") print(" -c, --comments (Add object selectors to the comments.)") print(" (Default: No added comments.)") print(" Output:") print(' Takes the page objects found in the "page_objects.py"') print(" file and uses those to replace matching selectors in") print(" the selected seleniumbase Python file.") print("") def show_objectify_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "objectify" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase objectify [SB_FILE.py] [OPTIONS]") print(" OR: sbase objectify [SB_FILE.py] [OPTIONS]") print(" Options:") print(" -c, --comments (Add object selectors to the comments.)") print(" (Default: No added comments.)") print(" Output:") print(" A modified version of the file where the selectors") print(" have been replaced with variable names defined in") print(' "page_objects.py", supporting the Page Object Pattern.') print("") print(' (seleniumbase "objectify" has the same outcome as') print(' combining "extract-objects" with "inject-objects")') print("") def show_revert_objects_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "revert-objects" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase revert-objects [SB_FILE.py] [OPTIONS]") print(" OR: sbase revert-objects [SB_FILE.py] [OPTIONS]") print(" Options:") print(" -c, --comments (Keep existing comments for the lines.)") print(" (Default: No comments are kept.)") print(" Output:") print(' Reverts the changes made by "seleniumbase objectify" or') print(' "seleniumbase inject-objects" when run against a') print(" seleniumbase Python file. Objects will get replaced by") print(' selectors stored in the "page_objects.py" file.') print("") def show_encrypt_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "encrypt OR obfuscate" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase encrypt || seleniumbase obfuscate") print(" --OR--") print(" sbase encrypt || sbase obfuscate") print(" Output:") print(" Runs the password encryption/obfuscation tool.") print(" (Where you can enter a password to encrypt/obfuscate.)") print("") def show_decrypt_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "decrypt OR unobfuscate" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase decrypt || seleniumbase unobfuscate") print(" --OR--") print(" sbase decrypt || sbase unobfuscate") print(" Output:") print(" Runs the password decryption/unobfuscation tool.") print(" (Where you can enter an encrypted password to decrypt.)") print("") def show_download_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "download" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase download server") print(" OR: sbase download server") print(" Output:") print(" Downloads the Selenium Standalone Server.") print(" (Server is required for using your own Selenium Grid.)") print("") def show_grid_hub_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "grid-hub" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase grid-hub {start|stop|restart} [OPTIONS]") print(" OR: sbase grid-hub {start|stop|restart} [OPTIONS]") print(" Options:") print(" -v, --verbose (Increase verbosity of logging output.)") print(" (Default: Quiet logging / not verbose.)") print(" --timeout=TIMEOUT (Close idle browser after TIMEOUT.)") print(" (The default TIMEOUT: 230 seconds.)") print(" (Use --timeout=0 to skip timeouts.)") print(" Example:") print(" seleniumbase grid-hub start") print(" Output:") print(" Controls the Selenium Grid Hub Server, which allows") print(" for running tests on multiple machines in parallel") print(" to speed up test runs and reduce the total time") print(" of test suite execution.") print(' You can "start" or "stop" the Grid Hub server.') print("") def show_grid_node_usage(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = " " + c2 + "** " + c3 + "grid-node" + c2 + " **" + cr print(sc) print("") print(" Usage:") print(" seleniumbase grid-node {start|stop|restart} [OPTIONS]") print(" OR: sbase grid-node {start|stop|restart} [OPTIONS]") print(" Options:") print(" --hub=[HOST/IP] (The Grid Hub Hostname / IP Address.)") print(" (Default: 127.0.0.1 if not set.)") print(" -v, --verbose (Increase verbosity of logging output.)") print(" (Default: Quiet logging / Not verbose.)") print(" Example:") print(" seleniumbase grid-node start --hub=127.0.0.1") print(" Output:") print(" Controls the Selenium Grid node, which serves as a") print(" worker machine for your Selenium Grid Hub server.") print(' You can "start" or "stop" the Grid node.') print("") def get_version_info(): # from pkg_resources import get_distribution # version = get_distribution("seleniumbase").version from seleniumbase import __version__ version_info = None c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sb_text = c1 + "selenium" + c2 + "base" + cr version_info = "%s %s%s%s" % (sb_text, c3, __version__, cr) return version_info def show_version_info(): version_info = get_version_info() print("%s" % version_info) def get_package_location(): # from pkg_resources import get_distribution # location = get_distribution("seleniumbase").location import os import seleniumbase location = os.path.dirname(os.path.realpath(seleniumbase.__file__)) if location.endswith("seleniumbase"): location = location[0 : -len("seleniumbase")] # noqa: E203 return location def show_package_location(): location = get_package_location() print("%s" % location) def show_methods(): c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTGREEN_EX cr = colorama.Style.RESET_ALL sc = ( "\n " + c2 + " ** " + c3 + " SeleniumBase Python Methods " "" + c2 + " ** " + cr ) print(sc) print("") line = "Here are some common methods that come with SeleniumBase:" line = c1 + line + cr print(line) line = "(Some optional args are not shown here)" print(line) print("") sbm = "" sbm += "*.open(url) => Navigate the browser window to the URL.\n" sbm += "*.type(selector, text) => Update the field with the text.\n" sbm += "*.click(selector) => Click the element with the selector.\n" sbm += "*.click_link(link_text) => Click the link containing text.\n" sbm += "*.check_if_unchecked(selector) => Check checkbox if unchecked.\n" sbm += "*.uncheck_if_checked(selector) => Uncheck checkbox if checked.\n" sbm += "*.select_option_by_text(dropdown_selector, option)\n" sbm += "*.hover_and_click(hover_selector, click_selector)\n" sbm += "*.drag_and_drop(drag_selector, drop_selector)\n" sbm += "*.choose_file(selector, file_path) => Choose a file to upload.\n" sbm += "*.get_text(selector) => Get the text from the element.\n" sbm += "*.get_current_url() => Get the URL of the current page.\n" sbm += "*.get_page_source() => Get the HTML of the current page.\n" sbm += "*.get_attribute(selector, attribute) => Get element attribute.\n" sbm += "*.get_title() => Get the title of the current page.\n" sbm += "*.go_back() => Navigate to the previous page in history.\n" sbm += "*.switch_to_frame(frame) => Switch into the iframe container.\n" sbm += "*.switch_to_default_content() => Exit all iframe containers.\n" sbm += "*.switch_to_parent_frame() => Exit from the current iframe.\n" sbm += "*.open_new_window() => Open a new window in the same browser.\n" sbm += "*.switch_to_window(window) => Switch to the browser window.\n" sbm += "*.switch_to_default_window() => Switch to the original window.\n" sbm += "*.get_new_driver(OPTIONS) => Open a new driver with OPTIONS.\n" sbm += "*.switch_to_driver(driver) => Switch to the browser driver.\n" sbm += "*.switch_to_default_driver() => Switch to the original driver.\n" sbm += "*.wait_for_element(selector) => Wait until element is visible.\n" sbm += "*.wait_for_element_present(selector) => Until element in HTML.\n" sbm += "*.is_element_visible(selector) => Return element visibility.\n" sbm += "*.is_element_present(selector) => Return element is in HTML.\n" sbm += "*.is_text_visible(text, selector) => Return text visibility.\n" sbm += "*.is_checked(selector) => Return whether the box is checked.\n" sbm += "*.sleep(seconds) => Do nothing for the given amount of time.\n" sbm += "*.save_screenshot(name) => Save a screenshot in .png format.\n" sbm += "*.assert_element(selector) => Verify the element is visible.\n" sbm += "*.assert_text(text, selector) => Verify text in the element.\n" sbm += "*.assert_exact_text(text, selector) => Verify text is exact.\n" sbm += "*.assert_url(url) => Verify that the current URL is the URL.\n" sbm += "*.assert_url_contains(substring) => Verify substring in URL.\n" sbm += "*.assert_title(title) => Verify the title of the web page.\n" sbm += "*.assert_title_contains(substring) => Verify STR in title.\n" sbm += "*.assert_downloaded_file(file) => Verify file was downloaded.\n" sbm += "*.assert_no_404_errors() => Verify there are no broken links.\n" sbm += "*.assert_no_js_errors() => Verify there are no JS errors.\n" sbm = sbm.replace("*.", "self." + c1).replace("(", cr + "(") sbm = sbm.replace("self.", c2 + "self" + c5 + "." + cr) sbm = sbm.replace("(", c3 + "(" + c4) sbm = sbm.replace(")", c3 + ")" + cr) print(sbm) def show_options(): c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = "\n " + c2 + " ** " + c3 + " pytest CLI Options " + c2 + " ** " + cr print(sc) print("") line = "Here are some common pytest options to use with SeleniumBase:" line = c1 + line + cr print(line) line = '(Some options are Chromium-specific, e.g. "--guest --mobile")' print(line) op = "\n" op += '--browser=BROWSER (Choice of web browser. Default is "chrome")\n' op += "--edge / --firefox / --safari (Shortcut for browser selection)\n" op += "--headless (Run tests headlessly. Default setting on Linux OS)\n" op += "--demo (Slow down and visually see test actions as they occur)\n" op += "--slow (Slow down the automation. Faster than using Demo Mode)\n" op += "--rs / --reuse-session (Reuse browser session between tests.)\n" op += "--reuse-class-session / --rcs (RS, but for class tests only.)\n" op += "--crumbs (Clear all cookies between tests reusing a session.)\n" op += "--maximize (Start tests with the browser window maximized)\n" op += "--dashboard (Enable SeleniumBase Dashboard at dashboard.html)\n" op += "--incognito (Enable Chromium's Incognito Mode.)\n" op += "--guest (Enable Chromium's Guest Mode.)\n" op += "--dark (Enable Chromium's Dark Mode.)\n" op += "--uc (Use undetected-chromedriver to evade detection.)\n" op += "-m=MARKER (Run tests with the specified pytest marker.)\n" op += "-n=NUM (Multithread the tests using that many threads.)\n" op += "-v (Verbose mode. Print the full names of each test run.)\n" op += "--html=report.html (Create a detailed pytest-html report.)\n" op += "--collect-only / --co (Only show discovered tests. No run.)\n" op += "--co -q (Only show full names of discovered tests. No run.)\n" op += "-x (Stop running tests after the first failure is reached.)\n" op += "--pdb (Enter Post Mortem Debug Mode after any test fails.)\n" op += "--trace (Enter Debug Mode immediately after starting tests.)\n" op += " | Debug Mode Commands >>> help / h: List all commands. |\n" op += " | n: Next line of method. s: Step into. c: Continue. |\n" op += " | where / w: Show stack spot. u: Up stack. d: Down stack. |\n" op += " | return / r: Run until method returns. j: Jump to line. |\n" op += " | longlist / ll: See code. dir(): List namespace objects. |\n" op += "--help / -h (Display list of all available pytest options.)\n" op += "--ftrace / --final-trace (Enter Debug Mode after tests end.)\n" op += "--recorder / --rec (Save browser actions as Python scripts.)\n" op += "--rec-behave / --rec-gherkin (Save actions as Gherkin code.)\n" op += "--rec-print (Display recorded scripts when they are created.)\n" op += "--save-screenshot (Save a screenshot at the end of each test.)\n" op += "--archive-logs (Archive logs after tests to prevent deletion.)\n" op += "--check-js (Check for JavaScript errors after page loads.)\n" op += "--start-page=URL (The browser start page when tests begin.)\n" op += "--agent=STRING (Modify the web browser's User-Agent string.)\n" op += "--mobile (Use Chromium's mobile device emulator during tests.)\n" op += '--metrics=STRING (Set mobile "CSSWidth,CSSHeight,PixelRatio".)\n' op += "--ad-block (Block certain types of iframe ads from appearing.)\n" op += "--settings-file=FILE (Override default SeleniumBase settings.)\n" op += '--env=ENV (Set the test env. Use "self.env" to access.)\n' op += '--data=DATA (Extra test data. Use "self.data" to access.)\n' op += "--disable-csp (Disable the Content Security Policy of sites.)\n" op += "--remote-debug (Sync to Ch_Debugger chrome://inspect/#devices)\n" op += "--server=SERVER (The Selenium Grid server/IP used for tests.)\n" op += "--port=PORT (The Selenium Grid port used by the test server.)\n" op += "--proxy=SERVER:PORT (Connect to a proxy server:port for tests)\n" op += "--proxy=USER:PASS@SERVER:PORT (Use authenticated proxy server)\n" op += cr op = op.replace("\n-", "\n" + c1 + "-").replace(" (", cr + " (") op = op.replace(" / -", cr + " / " + c1 + "-") op = op.replace("=", c2 + "=" + c3) op = op.replace(" | ", " |" + c3 + " ").replace("|\n", cr + "|\n") op = op.replace(": ", c5 + ":" + c3 + " ") op = op.replace("Debug Mode Commands", c5 + "Debug Mode Commands" + c3) op = op.replace(">>>", c4 + ">>>" + c3) print(op) line = "To view all " + c3 + "pytest" + cr line += " " + c2 + "command-line options" + cr line += ', type: "' + c3 + "pytest" + cr + " " + c1 + "--help" + cr + '".' print(line) print("") def show_behave_options(): c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL sc = "\n " + c2 + " ** " + c3 + " Behave CLI Options " + c2 + " ** " + cr print(sc) print("") line = 'Here are some common "behave" options to use with SeleniumBase:' line = c1 + line + cr print(line) line = '(Some options are Chromium-specific, e.g. "-D guest -D mobile")' print(line) op = "\n" op += '-D browser=BROWSER (Choice of web browser. Default is "chrome")\n' op += "-D headless (Run tests headlessly. Default mode on Linux OS.)\n" op += "-D demo (Slow down and visually see test actions as they occur)\n" op += "-D slow (Slow down the automation. Faster than using Demo Mode)\n" op += "-D reuse-session / -D rs (Reuse browser session between tests.)\n" op += "-D crumbs (Clear all cookies between tests reusing a session.)\n" op += "-D maximize (Start tests with the web browser window maximized)\n" op += "-D dashboard (Enable SeleniumBase Dashboard at dashboard.html)\n" op += "-D incognito (Enable Chromium's Incognito Mode.)\n" op += "-D guest (Enable Chromium's Guest Mode.)\n" op += "-D dark (Enable Chromium's Dark Mode.)\n" op += "-D uc (Use undetected-chromedriver to evade detection.)\n" op += "--no-snippets / -q (Quiet mode. Don't print snippets.)\n" op += "--dry-run / -d (Dry run. Only show discovered tests.)\n" op += "--stop (Stop running tests after the first failure is reached.)\n" op += "-D pdb (Enter the Post Mortem Debug Mode after any test fails.)\n" op += " | Debug Mode Commands >>> help / h: List all commands. |\n" op += " | n: Next line of method. s: Step through. c: Continue. |\n" op += " | return / r: Run until method returns. j: Jump to line. |\n" op += " | where / w: Show stack spot. u: Up stack. d: Down stack. |\n" op += " | longlist / ll: See code. dir(): List namespace objects. |\n" op += "-D recorder (Record browser actions to generate test scripts.)\n" op += "-D rec-print (Display recorded scripts when they are created.)\n" op += "-D save-screenshot (Save a screenshot at the end of each test.)\n" op += "-D archive-logs (Archive log files instead of deleting them.)\n" op += "-D check-js (Check for JavaScript errors after page loads.)\n" op += "-D start-page=URL (The browser start page when tests begin.)\n" op += "-D agent=STRING (Modify the web browser's User-Agent string.)\n" op += "-D mobile (Use Chromium's mobile device emulator during tests.)\n" op += '-D metrics=STRING (Set mobile "CSSWidth,CSSHeight,PixelRatio".)\n' op += "-D ad-block (Block some types of display ads after page loads.)\n" op += "-D settings-file=FILE (Override default SeleniumBase settings.)\n" op += '-D env=ENV (Set the test env. Access using "self.env" in tests)\n' op += '-D data=DATA (Extra test data. Access using "self.data".)\n' op += "-D disable-csp (Disable the Content Security Policy of sites.)\n" op += "-D remote-debug (Sync Ch-R-Debugger chrome://inspect/#devices)\n" op += "-D server=SERVER (The Selenium Grid server/IP used for tests.)\n" op += "-D port=PORT (The Selenium Grid port used by the test server.)\n" op += "-D proxy=SERVER:PORT (Connect to a proxy server:port for tests)\n" op += "-D proxy=USER:PASS@SERVER:PORT (Use authenticated proxy server)\n" op += cr op = op.replace("\n-", "\n" + c1 + "-").replace(" (", cr + " (") op = op.replace(" / -", cr + " / " + c1 + "-") op = op.replace("=", c2 + "=" + c3) op = op.replace(" | ", " |" + c3 + " ").replace("|\n", cr + "|\n") op = op.replace(": ", c5 + ":" + c3 + " ") op = op.replace("Debug Mode Commands", c5 + "Debug Mode Commands" + c3) op = op.replace(">>>", c4 + ">>>" + c3) print(op) line = "For the full list of " + c2 + "command-line options" + cr line += ', type: "' + c3 + "behave" + cr + " " + c1 + "--help" + cr + '".' print(line) print("") def show_detailed_help(): c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c6 = colorama.Back.CYAN cr = colorama.Style.RESET_ALL show_basic_usage() print(c6 + " " + c2 + " Commands: " + c6 + " ") print(cr) show_install_usage() show_commander_usage() show_behave_gui_usage() show_caseplans_usage() show_mkdir_usage() show_mkfile_usage() show_mkrec_usage() show_codegen_usage() show_recorder_usage() show_mkpres_usage() show_mkchart_usage() show_convert_usage() show_print_usage() show_translate_usage() show_extract_objects_usage() show_inject_objects_usage() show_objectify_usage() show_revert_objects_usage() show_encrypt_usage() show_decrypt_usage() show_download_usage() show_grid_hub_usage() show_grid_node_usage() print( '* (Use "' + c3 + "pytest" + cr + '" or "' + c3 + '' '' + "python" + cr + '" for running tests) *\n' ) def main(): command = None command_args = None num_args = len(sys.argv) if num_args == 1: show_basic_usage() return elif num_args == 2: command = sys.argv[1] command_args = [] elif num_args > 2: command = sys.argv[1] command_args = sys.argv[2:] command = command.lower() if command == "get" or command == "install": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_install need_retry = False need_another_retry = False retry_msg_1 = "* Unable to download driver! Retrying in 3s..." retry_msg_2 = "** Unable to download driver! Retrying in 5s..." if " --proxy=" in " ".join(sys.argv): from seleniumbase.core import proxy_helper for arg in sys.argv: if arg.startswith("--proxy="): proxy_string = arg.split("--proxy=")[1] if "@" in proxy_string: proxy_string = proxy_string.split("@")[1] proxy_helper.validate_proxy_string(proxy_string) break try: settings.HIDE_DRIVER_DOWNLOADS = False sb_install.main() except Exception as e: invalid_run_cmd = constants.Warnings.INVALID_RUN_COMMAND if invalid_run_cmd in str(e): raise print() print(retry_msg_1) time.sleep(3) print() need_retry = True if need_retry: try: sb_install.main() except Exception: print(retry_msg_2) time.sleep(5) print() need_another_retry = True if need_another_retry: sb_install.main() else: show_basic_usage() show_install_usage() elif command == "commander" or command == "gui": from seleniumbase.console_scripts import sb_commander sb_commander.main() elif command == "behave-gui" or command == "gui-behave": from seleniumbase.console_scripts import sb_behave_gui sb_behave_gui.main() elif ( command == "caseplans" or command == "case-plans" or command == "case_plans" ): from seleniumbase.console_scripts import sb_caseplans sb_caseplans.main() elif ( command == "recorder" or (command == "record" and len(command_args) == 0) ): from seleniumbase.console_scripts import sb_recorder sb_recorder.main() elif ( command == "mkrec" or command == "codegen" or (command == "record" and len(command_args) >= 1) ): if len(command_args) >= 1: from seleniumbase.console_scripts import sb_mkrec sb_mkrec.main() else: show_basic_usage() if command == "codegen": show_codegen_usage() else: show_mkrec_usage() elif command == "mkdir": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_mkdir sb_mkdir.main() else: show_basic_usage() show_mkdir_usage() elif command == "mkfile": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_mkfile sb_mkfile.main() else: show_basic_usage() show_mkfile_usage() elif command == "mkpres": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_mkpres sb_mkpres.main() else: show_basic_usage() show_mkpres_usage() elif command == "mkchart": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_mkchart sb_mkchart.main() else: show_basic_usage() show_mkchart_usage() elif command == "convert": if len(command_args) == 1: from seleniumbase.utilities.selenium_ide import convert_ide convert_ide.main() else: show_basic_usage() show_convert_usage() elif command == "print": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_print sb_print.main() else: show_basic_usage() show_print_usage() elif command == "translate": if len(command_args) >= 1: from seleniumbase.translate import translator translator.main() else: show_basic_usage() show_translate_usage() elif command == "extract-objects" or command == "extract_objects": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_objectify sb_objectify.extract_objects() else: show_basic_usage() show_extract_objects_usage() elif command == "inject-objects" or command == "inject_objects": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_objectify sb_objectify.inject_objects() else: show_basic_usage() show_inject_objects_usage() elif command == "objectify": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_objectify sb_objectify.objectify() else: show_basic_usage() show_objectify_usage() elif command == "revert-objects" or command == "revert_objects": if len(command_args) >= 1: from seleniumbase.console_scripts import sb_objectify sb_objectify.revert_objects() else: show_basic_usage() show_revert_objects_usage() elif command == "encrypt" or command == "obfuscate": if len(command_args) >= 0: from seleniumbase.common import obfuscate obfuscate.main() else: show_basic_usage() show_encrypt_usage() elif command == "decrypt" or command == "unobfuscate": if len(command_args) >= 0: from seleniumbase.common import unobfuscate unobfuscate.main() else: show_basic_usage() show_decrypt_usage() elif command == "download": if len(command_args) >= 1 and command_args[0].lower() == "server": from seleniumbase.utilities.selenium_grid import ( download_selenium_server, ) download_selenium_server.main(force_download=True) else: show_basic_usage() show_download_usage() elif command == "grid-hub" or command == "grid_hub": if len(command_args) >= 1: from seleniumbase.utilities.selenium_grid import grid_hub grid_hub.main() else: show_basic_usage() show_grid_hub_usage() elif command == "grid-node" or command == "grid_node": if len(command_args) >= 1: from seleniumbase.utilities.selenium_grid import grid_node grid_node.main() else: show_basic_usage() show_grid_node_usage() elif command == "version" or command == "--version" or command == "-v": if len(command_args) == 0: from seleniumbase.console_scripts import logo_helper seleniumbase_logo = logo_helper.get_seleniumbase_logo() print(seleniumbase_logo) print("") show_package_location() show_version_info() print("") else: show_basic_usage() elif command == "methods" or command == "--methods": show_methods() elif command == "options" or command == "--options": show_options() elif command == "behave-options" or command == "--behave-options": show_behave_options() elif command == "proxy" or command == "--proxy": import fasteners import os import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore", category=UserWarning) pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) with pip_find_lock: try: from proxy import proxy # noqa: F401 except Exception: shared_utils.pip_install( "proxy.py", version=constants.ProxyPy.VER ) os.system("proxy %s" % " ".join(sys.argv[2:])) elif command == "help" or command == "--help" or command == "-h": if len(command_args) >= 1: if command_args[0] == "get": print("") show_install_usage() return elif command_args[0] == "install": print("") show_install_usage() return elif command_args[0] == "commander": print("") show_commander_usage() return elif command_args[0] == "gui": print("") show_commander_usage() return elif command_args[0] == "behave-gui": print("") show_behave_gui_usage() return elif command_args[0] == "gui-behave": print("") show_behave_gui_usage() return elif command_args[0] == "caseplans": print("") show_caseplans_usage() return elif command_args[0] == "case-plans": print("") show_caseplans_usage() return elif command_args[0] == "case_plans": print("") show_caseplans_usage() return elif command_args[0] == "mkdir": print("") show_mkdir_usage() return elif command_args[0] == "mkfile": print("") show_mkfile_usage() return elif command_args[0] == "mkrec": print("") show_mkrec_usage() return elif command_args[0] == "codegen": print("") show_codegen_usage() return elif command_args[0] == "recorder": print("") show_recorder_usage() return elif command_args[0] == "mkpres": print("") show_mkpres_usage() return elif command_args[0] == "mkchart": print("") show_mkchart_usage() return elif command_args[0] == "convert": print("") show_convert_usage() return elif command_args[0] == "print": print("") show_print_usage() return elif command_args[0] == "translate": print("") show_translate_usage() return elif command_args[0] == "extract-objects": print("") show_extract_objects_usage() return elif command_args[0] == "inject-objects": print("") show_inject_objects_usage() return elif command_args[0] == "objectify": print("") show_objectify_usage() return elif command_args[0] == "revert-objects": print("") show_revert_objects_usage() return elif command_args[0] == "encrypt": print("") show_encrypt_usage() return elif command_args[0] == "obfuscate": print("") show_encrypt_usage() return elif command_args[0] == "decrypt": print("") show_decrypt_usage() return elif command_args[0] == "unobfuscate": print("") show_decrypt_usage() return elif command_args[0] == "download": print("") show_download_usage() return elif command_args[0] == "grid-hub": print("") show_grid_hub_usage() return elif command_args[0] == "grid-node": print("") show_grid_node_usage() return show_detailed_help() else: show_basic_usage() c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL invalid_cmd = "===> INVALID COMMAND: >> %s <<\n" % command invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) print(invalid_cmd) if __name__ == "__main__": main() ================================================ FILE: seleniumbase/console_scripts/sb_behave_gui.py ================================================ """ Launches SeleniumBase Behave Commander | GUI for Behave. Usage: seleniumbase behave-gui [OPTIONAL PATH or TEST FILE] sbase behave-gui [OPTIONAL PATH or TEST FILE] Examples: sbase behave-gui sbase behave-gui features/ sbase behave-gui features/calculator.feature Output: Launches SeleniumBase Behave Commander | GUI for Behave. """ import colorama import subprocess import sys import tkinter as tk from seleniumbase.fixtures import shared_utils from tkinter.scrolledtext import ScrolledText def set_colors(use_colors): c0 = "" c1 = "" c2 = "" c3 = "" c4 = "" c5 = "" c6 = "" cr = "" if use_colors: c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c1 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c2 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c3 = colorama.Fore.BLACK + colorama.Back.LIGHTGREEN_EX c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c6 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL return c0, c1, c2, c3, c4, c5, c6, cr def send_window_to_front(root): root.lift() root.attributes("-topmost", True) root.after_idle(root.attributes, "-topmost", False) def do_behave_run( root, tests, selected_tests, command_string, browser_string, rs_string, quiet_mode, demo_mode, mobile_mode, dashboard, headless, save_screenshots, additional_options, ): total_tests = len(tests) total_selected_tests = 0 test_to_run = None for selected_test in selected_tests: if selected_tests[selected_test].get(): total_selected_tests += 1 full_run_command = '"%s" -m behave' % sys.executable if total_selected_tests == 0 or total_tests == total_selected_tests: if command_string: full_run_command += " " full_run_command += command_string else: for test_number, test in enumerate(tests): if selected_tests[test_number].get(): full_run_command += " " test_to_run = test if test.startswith("(GROUP) "): test_to_run = test.split("(GROUP) ")[1] full_run_command += test_to_run.split(" => ")[0] else: full_run_command += test.split(" => ")[0] if "(-D edge)" in browser_string: full_run_command += " -D edge" elif "(-D firefox)" in browser_string: full_run_command += " -D firefox" elif "(-D safari)" in browser_string: full_run_command += " -D safari" if "(-D rs)" in rs_string: full_run_command += " -D rs" elif "(-D rs -D crumbs)" in rs_string: full_run_command += " -D rs -D crumbs" elif "(-D rcs)" in rs_string: full_run_command += " -D rcs" elif "(-D rcs -D crumbs)" in rs_string: full_run_command += " -D rcs -D crumbs" if quiet_mode: full_run_command += " --quiet" if demo_mode: full_run_command += " -D demo" if mobile_mode: full_run_command += " -D mobile" if dashboard: full_run_command += " -D dashboard" if headless: full_run_command += " -D headless" elif shared_utils.is_linux(): full_run_command += " -D gui" if save_screenshots: full_run_command += " -D screenshot" additional_options_list = additional_options.split(" ") dash_T_needed = False if ( "-T" not in additional_options_list and "--no-timings" not in additional_options_list and "--show-timings" not in additional_options_list ): dash_T_needed = True dash_k_needed = False if ( "-k" not in additional_options_list and "--no-skipped" not in additional_options_list and "--show-skipped" not in additional_options_list ): dash_k_needed = True additional_options = additional_options.strip() if additional_options: full_run_command += " " full_run_command += additional_options if dash_T_needed: full_run_command += " -T" if dash_k_needed: full_run_command += " -k" print(full_run_command) if not additional_options or " " not in additional_options: subprocess.Popen(full_run_command, shell=True) else: proc = subprocess.Popen( full_run_command, stderr=subprocess.PIPE, shell=True ) (output, error) = proc.communicate() if error and proc.returncode == 2: if str(error).startswith("b'") and str(error).endswith("\\n'"): error = str(error)[2:-3] elif str(error).startswith("b'") and str(error).endswith("'"): error = str(error)[2:-1] else: error = str(error) error = error.replace("\\n", "\n") print(error) send_window_to_front(root) def create_tkinter_gui(tests, command_string, t_count, f_count, s_tests): root = tk.Tk() root.title("SeleniumBase Behave Commander | GUI for Behave") if shared_utils.is_windows(): root.minsize(820, 640) else: root.minsize(820, 656) tk.Label(root, text="").pack() options_list = [ "Use Chrome Browser (Default)", "Use Edge Browser (-D edge)", "Use Firefox Browser (-D firefox)", ] if shared_utils.is_mac(): options_list.append("Use Safari Browser (-D safari)") brx = tk.StringVar(root) brx.set(options_list[0]) question_menu = tk.OptionMenu(root, brx, *options_list) question_menu.pack() options_list = [ "New Session Per Test (Default)", "Reuse Session for ALL the tests (-D rs)", "Reuse Session and clear cookies (-D rs -D crumbs)", "Reuse Session in the SAME class/feature (-D rcs)", "Reuse Session in class and clear cookies (-D rcs -D crumbs)", ] rsx = tk.StringVar(root) rsx.set(options_list[0]) question_menu = tk.OptionMenu(root, rsx, *options_list) question_menu.pack() qmx = tk.IntVar() chk = tk.Checkbutton( root, text="Quiet Mode (--quiet)", variable=qmx, pady=0 ) chk.pack() dmx = tk.IntVar() chk = tk.Checkbutton( root, text="Demo Mode (-D demo)", variable=dmx, pady=0 ) chk.pack() mmx = tk.IntVar() chk = tk.Checkbutton( root, text="Mobile Mode (-D mobile)", variable=mmx, pady=0 ) chk.pack() dbx = tk.IntVar() chk = tk.Checkbutton( root, text="Dashboard (-D dashboard)", variable=dbx, pady=0 ) chk.pack() chk.select() hbx = tk.IntVar() chk = tk.Checkbutton( root, text="Headless Browser (-D headless)", variable=hbx, pady=0 ) chk.pack() ssx = tk.IntVar() chk = tk.Checkbutton( root, text="Save Screenshots (-D screenshot)", variable=ssx, pady=0 ) chk.pack() tk.Label(root, text="").pack() plural = "s" if f_count == 1: plural = "" run_display = ( "Select from %s rows (%s feature%s with %s scenarios): " "(All tests will run if none are selected)" % (len(tests), f_count, plural, t_count) ) if t_count == 1: run_display = "Only ONE TEST was found and will be run:" tests = s_tests tk.Label(root, text=run_display, bg="yellow", fg="magenta").pack() text_area = ScrolledText( root, width=100, height=12, wrap="word", state=tk.DISABLED ) text_area.pack(side=tk.TOP, fill=tk.BOTH, expand=True) count = 0 ara = {} for row in tests: row += " " * 200 ara[count] = tk.IntVar() cb = None if shared_utils.is_windows(): cb = tk.Checkbutton( text_area, text=(row), bg="white", fg="black", anchor="w", pady=0, borderwidth=1, highlightthickness=1, variable=ara[count], ) else: cb = tk.Checkbutton( text_area, text=(row), bg="white", fg="black", anchor="w", pady=0, variable=ara[count], ) text_area.window_create("end", window=cb) text_area.insert("end", "\n") count += 1 tk.Label(root, text="").pack() additional_options = "" aopts = tk.StringVar(value=additional_options) tk.Label( root, text='Additional "behave" Options: (Eg. "-D incognito --junit")', bg="yellow", fg="blue", ).pack() entry = tk.Entry(root, textvariable=aopts) entry.pack() entry.focus() entry.bind( "", ( lambda _: do_behave_run( root, tests, ara, command_string, brx.get(), rsx.get(), qmx.get(), dmx.get(), mmx.get(), dbx.get(), hbx.get(), ssx.get(), aopts.get(), ) ), ) tk.Button( root, text="Run Selected Tests", fg="green", command=lambda: do_behave_run( root, tests, ara, command_string, brx.get(), rsx.get(), qmx.get(), dmx.get(), mmx.get(), dbx.get(), hbx.get(), ssx.get(), aopts.get(), ), ).pack() tk.Label(root, text="\n").pack() # Bring form window to front send_window_to_front(root) # Use decoy to set correct focus on main window decoy = tk.Tk() decoy.geometry("1x1") decoy.iconify() decoy.update() decoy.deiconify() decoy.destroy() # Start tkinter root.mainloop() def main(): use_colors = True if shared_utils.is_linux(): use_colors = False c0, c1, c2, c3, c4, c5, c6, cr = set_colors(use_colors) command_args = sys.argv[2:] command_string = " ".join(command_args) message = "" message += c2 message += "*" message += c4 message += " Starting the " message += c0 message += "Selenium" message += c1 message += "Base" message += c2 message += " " message += c6 message += "Behave" message += c4 message += " " message += c3 message += "Commander" message += c4 message += " GUI App" message += c2 message += "..." message += cr print(message) command_string = command_string.replace("--quiet", "") command_string = command_string.replace("-q", "") proc = subprocess.Popen( '"%s" -m behave -d %s --show-source' % (sys.executable, command_string), stdout=subprocess.PIPE, shell=True, ) (output, error) = proc.communicate() if error: error_msg = "Error collecting tests: %s" % str(error) error_msg = c5 + error_msg + cr print(error_msg) return filename = None feature_name = None scenario_name = None f_tests = [] # Features s_tests = [] # Scenarios tests = [] # All tests file_scenario_count = {} f_count = 0 s_count = 0 t_count = 0 if shared_utils.is_windows(): output = output.decode("latin1") else: output = output.decode("utf-8") for row in output.replace("\r", "").split("\n"): if row.startswith("Feature: "): if f_count > 0: file_scenario_count[str(f_count)] = s_count f_count += 1 s_count = 0 elif row.startswith(" Scenario: "): s_count += 1 file_scenario_count[str(f_count)] = s_count elif row.startswith(" Scenario Outline: "): s_count += 1 file_scenario_count[str(f_count)] = s_count file_scenario_count[str(f_count)] = s_count f_count = 0 s_count = 0 for row in output.replace("\r", "").split("\n"): if row.startswith("Feature: "): f_count += 1 feature_name = row.split("Feature: ")[1] if " # features/" in feature_name: filename = feature_name.split(" # features/")[-1] filename = "features/" + filename.split(":")[0] feature_name = feature_name.split(" # features/")[0] elif " # features\\" in feature_name: filename = feature_name.split(" # features\\")[-1] filename = "features\\" + filename.split(":")[0] feature_name = feature_name.split(" # features\\")[0] else: filename = feature_name.split(" # ")[-1] filename = filename.split(":")[0] feature_name = feature_name.split(" # ")[-1] s_count = file_scenario_count[str(f_count)] filename = filename.strip() t_name = "(GROUP) %s => %s" % (filename, feature_name) t_name += " <> (%s Total)" % s_count f_tests.append(t_name) elif ( row.startswith(" Scenario: ") or row.startswith(" Scenario Outline: ") ): t_count += 1 line_num = row.split(":")[-1] scenario_name = None if row.startswith(" Scenario: "): scenario_name = row.split(" Scenario: ")[-1] else: scenario_name = row.split(" Scenario Outline: ")[-1] if " -- @" in scenario_name: scenario_name = scenario_name.split(" # ")[0].rstrip() elif " # features/" in scenario_name: scenario_name = scenario_name.split(" # features/")[0] else: scenario_name = scenario_name.split(" # ")[0] scenario_name = scenario_name.strip() s_tests.append("%s:%s => %s" % (filename, line_num, scenario_name)) tests = f_tests + s_tests if not tests: err_msg_0 = c5 + "ERROR:" + cr + "\n" err_msg_1 = ' No "behave" tests found! Expecting "*.feature" files!' err_msg_1 = c6 + err_msg_1 + cr + "\n" err_msg_2 = ' "*.feature" files would live in a "features/" folder.' err_msg_2 = c6 + err_msg_2 + cr + "\n" err_msg_3 = "Exiting SBase Behave Commander..." err_msg_3 = c5 + err_msg_3 + cr error_msg = err_msg_0 + err_msg_1 + err_msg_2 + err_msg_3 print(error_msg) return create_tkinter_gui(tests, command_string, t_count, f_count, s_tests) if __name__ == "__main__": print('To open SBase Behave Commander, type "sbase behave-gui"') ================================================ FILE: seleniumbase/console_scripts/sb_caseplans.py ================================================ """ Launches the SeleniumBase Case Plans Generator. Usage: seleniumbase caseplans [OPTIONAL PATH or TEST FILE] sbase caseplans [OPTIONAL PATH or TEST FILE] Examples: sbase caseplans sbase caseplans -k agent sbase caseplans -m marker2 sbase caseplans test_suite.py sbase caseplans offline_examples/ Output: Launches the SeleniumBase Case Plans Generator. """ import colorama import os import subprocess import sys import tkinter as tk from seleniumbase.fixtures import shared_utils from tkinter import messagebox from tkinter.scrolledtext import ScrolledText def set_colors(use_colors): c0 = "" c1 = "" c2 = "" c3 = "" c4 = "" c5 = "" cr = "" if use_colors: c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c1 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c2 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c3 = colorama.Fore.BLACK + colorama.Back.LIGHTCYAN_EX c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL return c0, c1, c2, c3, c4, c5, cr def send_window_to_front(root): root.lift() root.attributes("-topmost", True) root.after_idle(root.attributes, "-topmost", False) def show_no_case_plans_warning(): messagebox.showwarning( "No existing Case Plans found!", "\nNo existing Case Plans found!!\n\nCreate some boilerplates first!", ) def get_test_id(display_id): """The id used in various places such as the test log path.""" return ( display_id.replace(".py::", ".").replace("::", ".").replace(" ", "_") ) def generate_case_plan_boilerplates( root, tests, selected_tests, tests_with_case_plan, tests_without_case_plan, ): total_tests = len(tests) total_selected_tests = 0 for selected_test in selected_tests: if selected_tests[selected_test].get(): total_selected_tests += 1 test_cases = [] case_plans_to_create = [] if total_selected_tests == 0: messagebox.showwarning( "No tests were selected!", "\nℹ️ No tests were selected!\nSelect tests for Case Plans!", ) send_window_to_front(root) return elif total_tests == total_selected_tests: for test in tests: test_cases.append(test) else: for test_number, test in enumerate(tests): if selected_tests[test_number].get(): test_cases.append(test) for test_case in test_cases: if ( test_case in tests_without_case_plan and test_case not in tests_with_case_plan ): case_plans_to_create.append(test_case) new_plans = 0 for case_plan in case_plans_to_create: parts = case_plan.split("/") test_address = None folder_path = None if len(parts) == 1: test_address = parts[0] if len(parts) > 1: test_address = parts[-1] folder_path = "/".join(parts[0:-1]) test_id = get_test_id(test_address) case_id = test_id + ".md" full_folder_path = None if len(parts) == 1: full_folder_path = "case_plans" if not os.path.exists(full_folder_path): os.makedirs(full_folder_path) else: full_folder_path = os.path.join(folder_path, "case_plans") if not os.path.exists(full_folder_path): os.makedirs(full_folder_path) data = [] data.append("``%s``" % test_address) data.append("---") data.append("| # | Step Description | Expected Result |") data.append("| - | ---------------- | --------------- |") data.append("| 1 | Perform Action 1 | Verify Action 1 |") data.append("| 2 | Perform Action 2 | Verify Action 2 |") data.append("") file_name = case_id file_path = os.path.join(full_folder_path, file_name) if not os.path.exists(file_path): out_file = open(file_path, mode="w+", encoding="utf-8") out_file.writelines("\r\n".join(data)) out_file.close() new_plans += 1 print("Created %s" % file_path) if new_plans == 1: messagebox.showinfo( "A new Case Plan was generated!", '\n✅ %s new boilerplate Case Plan was generated!' % new_plans, ) elif new_plans >= 2: messagebox.showinfo( "New Case Plans were generated!", '\n✅ %s new boilerplate Case Plans were generated!' % new_plans, ) else: messagebox.showwarning( "No new Case Plans were generated!", "\nℹ️ No new boilerplates were generated!\n\n" "The selected tests already have Case Plans!", ) send_window_to_front(root) def view_summary_of_existing_case_plans(root, tests): case_data_storage = [] case_to_test_hash = {} full_t = [] for test_index, test in enumerate(tests): full_t.append(test) parts = test.strip().split("/") test_address = None folder_path = None if len(parts) == 1: test_address = parts[0] folder_path = "." if len(parts) > 1: test_address = parts[-1] folder_path = "/".join(parts[0:-1]) test_id = get_test_id(test_address) case_id = test_id + ".md" case_path = None if len(parts) == 1: case_path = os.path.join("case_plans", case_id) else: case_path = os.path.join(folder_path, "case_plans", case_id) if os.path.exists(case_path): f = open(case_path, mode="r") case_data = f.read() f.close() case_data_storage.append(case_data) case_to_test_hash[len(case_data_storage) - 1] = test_index full_plan = [] if len(case_data_storage) > 0: full_plan.append( "

      Summary of existing Case Plans

      " ) full_plan.append("") full_plan.append("| | |") full_plan.append("| - | - |") full_plan.append("| 🔵 | Plans with customized step definitions. |") full_plan.append("| ⭕ | Plans using default boilerplate code. |") full_plan.append("| 🚧 | Plans under construction with no table. |") full_plan.append("") full_plan.append("--------") else: show_no_case_plans_warning() send_window_to_front(root) return full_plan.append("") full_plan.append("

      🔎 (Click rows to expand) 🔍

      ") full_plan.append("") full_plan = [] num_ready_cases = 0 num_boilerplate = 0 num_in_progress = 0 for case_index, case_data in enumerate(case_data_storage): icon = "🔵" table_missing = False if "| 1 | Perform Action 1 | Verify Action 1 |" in case_data: # Still using raw boilerplate code. (Missing real test steps) icon = "⭕" if case_data.count("|") < 9 or case_data.count("-") < 3: # Not enough characters for a minimal Markdown case plan file. # The dash(es) on line 2, and the Markdown table are required. # This is what a minimal case plan file might look like: """ TEST_ADDRESS - | Steps | Results | | - | - | | Step1 | Result1 | """ icon = "🚧" table_missing = True lines = case_data.split("\n") if len(lines) >= 3 and not table_missing: first_line = lines[0] first_line = first_line.strip() if not (first_line.startswith("``") and first_line.endswith("``")): first_line = "``%s``" % tests[case_to_test_hash[case_index]] lines.insert(0, first_line) else: first_line = "``%s``" % tests[case_to_test_hash[case_index]] lines[0] = first_line lines.insert(0, "
      ") lines[1] = ( " %s " % icon + first_line[2:-2] + "" ) if ( lines[2].strip().startswith("-") and lines[2].strip().endswith("-") ): lines[2] = "" elif lines[2].strip() != "": lines.insert(2, "") if lines[-1].strip() != "": lines.append("") lines.append("
      ") full_plan.append("\r\n".join(lines)) else: # No existing Case Plan found. / File is missing boilerplate. icon = "🚧" lines = [] first_line = tests[case_to_test_hash[case_index]] first_line = "%s %s" % (icon, first_line) lines.insert(0, first_line) full_plan.append("\r\n".join(lines)) full_plan.append("") if icon == "🔵": num_ready_cases += 1 elif icon == "⭕": num_boilerplate += 1 elif icon == "🚧": num_in_progress += 1 msg_ready_cases = "%s Case Plans with customized tables" % num_ready_cases if num_ready_cases == 1: msg_ready_cases = "1 Case Plan with a customized table" msg_boilerplate = "%s Case Plans using boilerplate code" % num_boilerplate if num_boilerplate == 1: msg_boilerplate = "1 Case Plan using boilerplate code" msg_in_progress = "%s Case Plans that are missing tables" % num_in_progress if num_in_progress == 1: msg_in_progress = "1 Case Plan that is missing a table" msg_r = " ".join(msg_ready_cases.split(" ")[1:]) msg_b = " ".join(msg_boilerplate.split(" ")[1:]) msg_i = " ".join(msg_in_progress.split(" ")[1:]) plan_head = [] if len(case_data_storage) > 0: plan_head.append( "

      Summary of existing Case Plans

      " ) plan_head.append("") plan_head.append("| | | |") plan_head.append("| - | -: | - |") plan_head.append("| 🔵 | %s | %s |" % (num_ready_cases, msg_r)) plan_head.append("| ⭕ | %s | %s |" % (num_boilerplate, msg_b)) plan_head.append("| 🚧 | %s | %s |" % (num_in_progress, msg_i)) plan_head.append("") plan_head.append("--------") else: show_no_case_plans_warning() send_window_to_front(root) return plan_head.append("") plan_head.append("

      🔎 (Click rows to expand) 🔍

      ") plan_head.append("") for row in full_plan: plan_head.append(row) full_plan = plan_head file_path = "case_summary.md" file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(full_plan)) file.close() if num_ready_cases < 10: msg_ready_cases = " %s" % msg_ready_cases if num_ready_cases < 100: msg_ready_cases = " %s" % msg_ready_cases if num_boilerplate < 10: msg_boilerplate = " %s" % msg_boilerplate if num_boilerplate < 100: msg_boilerplate = " %s" % msg_boilerplate if num_in_progress < 10: msg_in_progress = " %s" % msg_in_progress if num_in_progress < 100: msg_in_progress = " %s" % msg_in_progress gen_message = ( '🗂️ Summary generated at "case_summary.md":' '\n🔵 %s' '\n⭕ %s' '\n🚧 %s' % (msg_ready_cases, msg_boilerplate, msg_in_progress) ) print(gen_message) if num_ready_cases < 10: msg_ready_cases = " %s" % msg_ready_cases if num_ready_cases < 100: msg_ready_cases = " %s" % msg_ready_cases if num_boilerplate < 10: msg_boilerplate = " %s" % msg_boilerplate if num_boilerplate < 100: msg_boilerplate = " %s" % msg_boilerplate if num_in_progress < 10: msg_in_progress = " %s" % msg_in_progress if num_in_progress < 100: msg_in_progress = " %s" % msg_in_progress messagebox.showinfo( "Case Plans Summary generated!", '\nSummary generated at "case_summary.md"' '\n🔵 %s' '\n⭕ %s' '\n🚧 %s' % (msg_ready_cases, msg_boilerplate, msg_in_progress) ) send_window_to_front(root) def create_tkinter_gui(tests, command_string): root = tk.Tk() root.title("SeleniumBase Case Plans Generator") if shared_utils.is_windows(): root.minsize(820, 618) else: root.minsize(820, 652) tk.Label(root, text="").pack() run_display = ( "Select from %s tests found: " "(Boilerplate Case Plans will be generated as needed)" % len(tests) ) if len(tests) == 1: run_display = ( "Select from 1 test found: " "(Boilerplate Case Plans will be generated as needed)" ) run_display_2 = "(Tests with existing Case Plans are already checked)" tk.Label(root, text=run_display, bg="yellow", fg="green").pack() tk.Label(root, text=run_display_2, bg="yellow", fg="magenta").pack() text_area = ScrolledText( root, width=100, height=12, wrap="word", state=tk.DISABLED ) text_area.pack(side=tk.TOP, fill=tk.BOTH, expand=True) count = 0 ara = {} tests_with_case_plan = [] tests_without_case_plan = [] for row in tests: row += " " * 200 ara[count] = tk.IntVar() cb = None if shared_utils.is_windows(): cb = tk.Checkbutton( text_area, text=(row), bg="white", fg="black", anchor="w", pady=0, borderwidth=1, highlightthickness=1, variable=ara[count], ) else: cb = tk.Checkbutton( text_area, text=(row), bg="white", fg="black", anchor="w", pady=0, variable=ara[count], ) parts = row.strip().split("/") test_address = None folder_path = None if len(parts) == 1: test_address = parts[0] if len(parts) > 1: test_address = parts[-1] folder_path = "/".join(parts[0:-1]) test_id = get_test_id(test_address) case_id = test_id + ".md" case_path = None if len(parts) == 1: case_path = os.path.join("case_plans", case_id) else: case_path = os.path.join(folder_path, "case_plans", case_id) if os.path.exists(case_path): cb.select() tests_with_case_plan.append(row.strip()) else: tests_without_case_plan.append(row.strip()) text_area.window_create("end", window=cb) text_area.insert("end", "\n") count += 1 tk.Label(root, text="").pack() tk.Button( root, text=( "Generate boilerplate Case Plans " "for selected tests missing them"), fg="green", command=lambda: generate_case_plan_boilerplates( root, tests, ara, tests_with_case_plan, tests_without_case_plan, ), ).pack() tk.Label(root, text="").pack() try: tk.Button( root, text=("Generate Summary of existing Case Plans"), fg="teal", command=lambda: view_summary_of_existing_case_plans(root, tests), ).pack() except Exception: tk.Button( root, text=("Generate Summary of existing Case Plans"), fg="green", command=lambda: view_summary_of_existing_case_plans(root, tests), ).pack() tk.Label(root, text="\n").pack() # Bring form window to front send_window_to_front(root) # Use decoy to set correct focus on main window decoy = tk.Tk() decoy.geometry("1x1") decoy.iconify() decoy.update() decoy.deiconify() decoy.destroy() # Start tkinter root.mainloop() def main(): use_colors = True if shared_utils.is_linux(): use_colors = False c0, c1, c2, c3, c4, c5, cr = set_colors(use_colors) command_args = sys.argv[2:] command_string = " ".join(command_args) message = "" message += c2 message += "*" message += c4 message += " Starting the " message += c0 message += "Selenium" message += c1 message += "Base" message += c2 message += " " message += c3 message += "Case Plans" message += c4 message += " Generator" message += c2 message += "..." message += cr print(message) proc = subprocess.Popen( '"%s" -m pytest --collect-only -q --rootdir="./" %s' % (sys.executable, command_string), stdout=subprocess.PIPE, shell=True, ) (output, error) = proc.communicate() if error: error_msg = "Error collecting tests: %s" % str(error) error_msg = c5 + error_msg + cr print(error_msg) return tests = [] if shared_utils.is_windows(): output = output.decode("latin1") else: output = output.decode("utf-8") for row in output.replace("\r", "").split("\n"): if ("::") in row: tests.append(row) if not tests: error_msg = "No tests found! Exiting the Case Plans Generator..." error_msg = c5 + "ERROR: " + error_msg + cr print(error_msg) return create_tkinter_gui(tests, command_string) if __name__ == "__main__": print('To open the Case Plans Generator, type "sbase caseplans"') ================================================ FILE: seleniumbase/console_scripts/sb_commander.py ================================================ """ Launches SeleniumBase Commander | GUI for pytest. Usage: seleniumbase commander [OPTIONAL PATH or TEST FILE] sbase commander [OPTIONAL PATH or TEST FILE] seleniumbase gui [OPTIONAL PATH or TEST FILE] sbase gui [OPTIONAL PATH or TEST FILE] Examples: sbase gui sbase gui -k agent sbase gui -m marker2 sbase gui test_suite.py sbase gui offline_examples/ Output: Launches SeleniumBase Commander | GUI for pytest. """ import colorama import os import subprocess import sys import tkinter as tk from seleniumbase.fixtures import shared_utils from tkinter.scrolledtext import ScrolledText def set_colors(use_colors): c0 = "" c1 = "" c2 = "" c3 = "" c4 = "" c5 = "" cr = "" if use_colors: c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c1 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c2 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c3 = colorama.Fore.BLACK + colorama.Back.LIGHTGREEN_EX c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL return c0, c1, c2, c3, c4, c5, cr def send_window_to_front(root): root.lift() root.attributes("-topmost", True) root.after_idle(root.attributes, "-topmost", False) def do_pytest_run( root, tests, selected_tests, command_string, browser_string, rs_string, thread_string, verbose, demo_mode, mobile_mode, dashboard, html_report, headless, save_screenshots, additional_options, ): cleaned_tests = [] for test in tests: if test.startswith("(FILE) "): clean_test = test.split("(FILE) ")[1].split(" => ")[0] cleaned_tests.append(clean_test) else: cleaned_tests.append(test) tests = cleaned_tests total_tests = len(tests) total_selected_tests = 0 for selected_test in selected_tests: if selected_tests[selected_test].get(): total_selected_tests += 1 full_run_command = '"%s" -m pytest' % sys.executable if total_selected_tests == 0 or total_tests == total_selected_tests: if command_string: full_run_command += " " full_run_command += command_string else: for test_number, test in enumerate(tests): if selected_tests[test_number].get(): full_run_command += " " if ' ' not in test: full_run_command += test elif '"' not in test: full_run_command += '"%s"' % test else: full_run_command += test.replace(" ", "\\ ") if "(--edge)" in browser_string: full_run_command += " --edge" elif "(--firefox)" in browser_string: full_run_command += " --firefox" elif "(--safari)" in browser_string: full_run_command += " --safari" if "(--rs)" in rs_string: full_run_command += " --rs" elif "(--rs --crumbs)" in rs_string: full_run_command += " --rs --crumbs" elif "(--rcs)" in rs_string: full_run_command += " --rcs" elif "(--rcs --crumbs)" in rs_string: full_run_command += " --rcs --crumbs" if "(-n=2)" in thread_string: full_run_command += " -n=2" elif "(-n=3)" in thread_string: full_run_command += " -n=3" elif "(-n=4)" in thread_string: full_run_command += " -n=4" elif "(-n=5)" in thread_string: full_run_command += " -n=5" elif "(-n=6)" in thread_string: full_run_command += " -n=6" elif "(-n=7)" in thread_string: full_run_command += " -n=7" elif "(-n=8)" in thread_string: full_run_command += " -n=8" if demo_mode: full_run_command += " --demo" if mobile_mode: full_run_command += " --mobile" if dashboard: full_run_command += " --dashboard" if html_report: full_run_command += " --html=report.html" if headless: full_run_command += " --headless" elif shared_utils.is_linux(): full_run_command += " --gui" if save_screenshots: full_run_command += " --screenshot" capture_needed = False if "--capture" not in additional_options: capture_needed = True additional_options = additional_options.strip() if additional_options: full_run_command += " " full_run_command += additional_options if verbose: full_run_command += " -v" if capture_needed: full_run_command += " --capture=tee-sys" print(full_run_command) subprocess.Popen(full_run_command, shell=True) send_window_to_front(root) def create_tkinter_gui(tests, command_string, files, solo_tests): root = tk.Tk() root.title("SeleniumBase Commander | GUI for pytest") if shared_utils.is_windows(): root.minsize(820, 696) else: root.minsize(820, 702) tk.Label(root, text="").pack() options_list = [ "Use Chrome Browser (Default)", "Use Edge Browser (--edge)", "Use Firefox Browser (--firefox)", ] if shared_utils.is_mac(): options_list.append("Use Safari Browser (--safari)") brx = tk.StringVar(root) brx.set(options_list[0]) question_menu = tk.OptionMenu(root, brx, *options_list) question_menu.pack() options_list = [ "New Session Per Test (Default)", "Reuse Session for ALL tests in thread (--rs)", "Reuse Session and also clear cookies (--rs --crumbs)", "Reuse Session for tests with same CLASS (--rcs)", "Reuse Session for class and clear cookies (--rcs --crumbs)", ] rsx = tk.StringVar(root) rsx.set(options_list[0]) question_menu = tk.OptionMenu(root, rsx, *options_list) question_menu.pack() options_list = [ "Number of Threads: 1 (Default)", "Number of Threads: 2 (-n=2)", "Number of Threads: 3 (-n=3)", "Number of Threads: 4 (-n=4)", ] try: if int(os.cpu_count()) >= 8: options_list.append("Number of Threads: 5 (-n=5)") options_list.append("Number of Threads: 6 (-n=6)") options_list.append("Number of Threads: 7 (-n=7)") options_list.append("Number of Threads: 8 (-n=8)") except Exception: pass ntx = tk.StringVar(root) ntx.set(options_list[0]) question_menu = tk.OptionMenu(root, ntx, *options_list) question_menu.pack() vox = tk.IntVar() chk = tk.Checkbutton( root, text="Verbose Output (-v)", variable=vox, pady=0 ) chk.pack() chk.select() dmx = tk.IntVar() chk = tk.Checkbutton( root, text="Demo Mode (--demo)", variable=dmx, pady=0 ) chk.pack() mmx = tk.IntVar() chk = tk.Checkbutton( root, text="Mobile Mode (--mobile)", variable=mmx, pady=0 ) chk.pack() dbx = tk.IntVar() chk = tk.Checkbutton( root, text="Dashboard (--dashboard)", variable=dbx, pady=0 ) chk.pack() chk.select() hrx = tk.IntVar() chk = tk.Checkbutton( root, text="Report (--html=report.html)", variable=hrx, pady=0 ) chk.pack() chk.select() hbx = tk.IntVar() chk = tk.Checkbutton( root, text="Headless Browser (--headless)", variable=hbx, pady=0 ) chk.pack() ssx = tk.IntVar() chk = tk.Checkbutton( root, text="Save Screenshots (--screenshot)", variable=ssx, pady=0 ) chk.pack() tk.Label(root, text="").pack() run_display = ( "Select from %s rows (%s files with %s tests): " "(All tests will run if none are selected)" % (len(tests), len(files), len(solo_tests)) ) if len(solo_tests) == 1: run_display = "Only ONE TEST was found and will be run:" tests = solo_tests elif len(files) == 1: run_display = ( "Select from %s tests: " "(All tests will run if none are selected)" % (len(solo_tests)) ) tests = solo_tests tk.Label(root, text=run_display, bg="yellow", fg="magenta").pack() text_area = ScrolledText( root, width=100, height=12, wrap="word", state=tk.DISABLED ) text_area.pack(side=tk.TOP, fill=tk.BOTH, expand=True) count = 0 ara = {} for row in tests: row += " " * 200 ara[count] = tk.IntVar() cb = None if shared_utils.is_windows(): cb = tk.Checkbutton( text_area, text=(row), bg="white", fg="black", anchor="w", pady=0, borderwidth=1, highlightthickness=1, variable=ara[count], ) else: cb = tk.Checkbutton( text_area, text=(row), bg="white", fg="black", anchor="w", pady=0, variable=ara[count], ) text_area.window_create("end", window=cb) text_area.insert("end", "\n") count += 1 tk.Label(root, text="").pack() additional_options = "" aopts = tk.StringVar(value=additional_options) tk.Label( root, text='Additional "pytest" Options: (Eg. "--incognito --slow")', bg="yellow", fg="blue", ).pack() entry = tk.Entry(root, textvariable=aopts) entry.pack() entry.focus() entry.bind( "", ( lambda _: do_pytest_run( root, tests, ara, command_string, brx.get(), rsx.get(), ntx.get(), vox.get(), dmx.get(), mmx.get(), dbx.get(), hrx.get(), hbx.get(), ssx.get(), aopts.get(), ) ), ) tk.Button( root, text="Run Selected Tests", fg="green", command=lambda: do_pytest_run( root, tests, ara, command_string, brx.get(), rsx.get(), ntx.get(), vox.get(), dmx.get(), mmx.get(), dbx.get(), hrx.get(), hbx.get(), ssx.get(), aopts.get(), ), ).pack() tk.Label(root, text="\n").pack() # Bring form window to front send_window_to_front(root) # Use decoy to set correct focus on main window decoy = tk.Tk() decoy.geometry("1x1") decoy.iconify() decoy.update() decoy.deiconify() decoy.destroy() # Start tkinter root.mainloop() def main(): use_colors = True if shared_utils.is_linux(): use_colors = False c0, c1, c2, c3, c4, c5, cr = set_colors(use_colors) command_args = sys.argv[2:] command_string = " ".join(command_args) message = "" message += c2 message += "*" message += c4 message += " Starting the " message += c0 message += "Selenium" message += c1 message += "Base" message += c2 message += " " message += c3 message += "Commander" message += c4 message += " Desktop App" message += c2 message += "..." message += cr print(message) proc = subprocess.Popen( '"%s" -m pytest --collect-only -q --rootdir="./" %s' % (sys.executable, command_string), stdout=subprocess.PIPE, shell=True, ) (output, error) = proc.communicate() if error: error_msg = "Error collecting tests: %s" % str(error) error_msg = c5 + error_msg + cr print(error_msg) return tests = [] if shared_utils.is_windows(): output = output.decode("latin1") else: output = output.decode("utf-8") for row in output.replace("\r", "").split("\n"): if ("::") in row: tests.append(row) if not tests: error_msg = "No tests found! Exiting SeleniumBase Commander..." error_msg = c5 + "ERROR: " + error_msg + cr print(error_msg) return groups = [] for row in tests: if row.count("::") >= 1: g_name = "(FILE) %s" % row.split("::")[0] groups.append(g_name) files = [] used_files = [] for row in groups: if row not in used_files: used_files.append(row) plural = "s" if groups.count(row) == 1: plural = "" f_row = "%s => (%s Test%s)" % (row, groups.count(row), plural) files.append(f_row) solo_tests = tests tests = [*files, *tests] create_tkinter_gui(tests, command_string, files, solo_tests) if __name__ == "__main__": print('To open SBase Commander, type "sbase commander" or "sbase gui"') ================================================ FILE: seleniumbase/console_scripts/sb_install.py ================================================ """ Downloads the specified webdriver to "seleniumbase/drivers/" Usage: sbase get {chromedriver|geckodriver|edgedriver| iedriver|uc_driver|cft|chs} [OPTIONS] Options: VERSION Specify the version. Tries to detect the needed version. If using chromedriver or edgedriver, you can use the major version integer. -p OR --path Also copy the driver to /usr/local/bin Examples: sbase get chromedriver sbase get geckodriver sbase get edgedriver sbase get chromedriver 114 sbase get chromedriver 114.0.5735.90 sbase get chromedriver stable sbase get chromedriver beta sbase get chromedriver -p sbase get chromium sbase get cft 131 sbase get chs Output: Downloads the webdriver to seleniumbase/drivers/ (chromedriver is required for Chrome automation) (geckodriver is required for Firefox automation) (edgedriver is required for MS__Edge automation) """ import colorama import logging import os import platform import requests import shutil import subprocess import sys import time import tarfile import urllib3 import zipfile from contextlib import suppress from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils from seleniumbase import config as sb_config from seleniumbase import drivers # webdriver storage folder for SeleniumBase from seleniumbase.drivers import cft_drivers # chrome-for-testing from seleniumbase.drivers import chs_drivers # chrome-headless-shell from seleniumbase.drivers import chromium_drivers # base chromium urllib3.disable_warnings() ARCH = platform.architecture()[0] IS_ARM_MAC = shared_utils.is_arm_mac() IS_MAC = shared_utils.is_mac() IS_ARM_LINUX = shared_utils.is_arm_linux() IS_LINUX = shared_utils.is_linux() IS_WINDOWS = shared_utils.is_windows() DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__)) DRIVER_DIR_CFT = os.path.dirname(os.path.realpath(cft_drivers.__file__)) DRIVER_DIR_CHS = os.path.dirname(os.path.realpath(chs_drivers.__file__)) DRIVER_DIR_CHROMIUM = os.path.dirname( os.path.realpath(chromium_drivers.__file__) ) LOCAL_PATH = "/usr/local/bin/" # On Mac and Linux systems DEFAULT_CHROMEDRIVER_VERSION = "114.0.5735.90" # (If can't find LATEST_STABLE) DEFAULT_GECKODRIVER_VERSION = "v0.36.0" DEFAULT_EDGEDRIVER_VERSION = "115.0.1901.183" # (If can't find LATEST_STABLE) def invalid_run_command(): exp = " ** get / install **\n\n" exp += " Usage:\n" exp += " seleniumbase install [DRIVER_NAME] [OPTIONS]\n" exp += " OR sbase install [DRIVER_NAME] [OPTIONS]\n" exp += " OR seleniumbase get [DRIVER_NAME] [OPTIONS]\n" exp += " OR sbase get [DRIVER_NAME] [OPTIONS]\n" exp += " (Drivers: chromedriver, cft, uc_driver,\n" exp += " edgedriver, chs, geckodriver)\n" exp += " Options:\n" exp += " VERSION Specify the version.\n" exp += " Tries to detect the needed version.\n" exp += " If using chromedriver or edgedriver,\n" exp += " you can use the major version integer.\n" exp += "\n" exp += " -p OR --path Also copy the driver to /usr/local/bin\n" exp += " Examples:\n" exp += " sbase get chromedriver\n" exp += " sbase get geckodriver\n" exp += " sbase get edgedriver\n" exp += " sbase get chromedriver 114\n" exp += " sbase get chromedriver 114.0.5735.90\n" exp += " sbase get chromedriver stable\n" exp += " sbase get chromedriver beta\n" exp += " sbase get chromedriver -p\n" exp += " sbase get cft 131\n" exp += " sbase get chs\n" exp += " Output:\n" exp += " Downloads the webdriver to seleniumbase/drivers/\n" exp += " (chromedriver is required for Chrome automation)\n" exp += " (geckodriver is required for Firefox automation)\n" exp += " (edgedriver is required for MS__Edge automation)\n" exp += " (cft is for the `Chrome for Testing` binary exe)\n" exp += " (chs is for the `Chrome-Headless-Shell` binary.)\n" print("") raise Exception("%s\n\n%s" % (constants.Warnings.INVALID_RUN_COMMAND, exp)) def make_executable(file_path): # Set permissions to: "If you can read it, you can execute it." mode = os.stat(file_path).st_mode mode |= (mode & 0o444) >> 2 # copy R bits to X os.chmod(file_path, mode) def get_proxy_info(): use_proxy = None protocol = "http" proxy_string = None user_and_pass = None if " --proxy=" in " ".join(sys.argv): from seleniumbase.core import proxy_helper for arg in sys.argv: if arg.startswith("--proxy="): proxy_string = arg.split("--proxy=")[1] if "@" in proxy_string: # Format => username:password@hostname:port try: user_and_pass = proxy_string.split("@")[0] proxy_string = proxy_string.split("@")[1] except Exception: raise Exception( "The format for using a proxy server with auth " 'is: "username:password@hostname:port". If not ' 'using auth, the format is: "hostname:port".' ) if proxy_string.endswith(":443"): protocol = "https" elif "socks4" in proxy_string: protocol = "socks4" elif "socks5" in proxy_string: protocol = "socks5" proxy_string = proxy_helper.validate_proxy_string(proxy_string) if user_and_pass: proxy_string = "%s@%s" % (user_and_pass, proxy_string) use_proxy = True break return (use_proxy, protocol, proxy_string) def requests_get(url): use_proxy, protocol, proxy_string = get_proxy_info() proxies = None response = None if use_proxy: proxies = {protocol: proxy_string} try: response = requests.get(url, proxies=proxies, timeout=1.25) except Exception: # Prevent SSLCertVerificationError / CERTIFICATE_VERIFY_FAILED url = url.replace("https://", "http://") time.sleep(0.04) response = requests.get(url, proxies=proxies, timeout=2.75) return response def requests_get_with_retry(url): use_proxy, protocol, proxy_string = get_proxy_info() proxies = None response = None if use_proxy: proxies = {protocol: proxy_string} try: response = requests.get(url, proxies=proxies, timeout=1.35) except Exception: time.sleep(1) try: response = requests.get(url, proxies=proxies, timeout=2.45) except Exception: time.sleep(1) response = requests.get(url, proxies=proxies, timeout=3.55) return response def get_cft_known_good_versions(): if getattr(sb_config, "cft_kgv_json", None): return sb_config.cft_kgv_json cft_ngv_url = ( "https://googlechromelabs.github.io/" "chrome-for-testing/known-good-versions.json" ) sb_config.cft_kgv_json = requests_get(cft_ngv_url) return sb_config.cft_kgv_json def get_cft_latest_versions_per_milestone(): if getattr(sb_config, "cft_lvpm_json", None): return sb_config.cft_lvpm_json cft_lvpm_url = ( "https://googlechromelabs.github.io/" "chrome-for-testing/latest-versions-per-milestone.json" ) sb_config.cft_lvpm_json = requests_get(cft_lvpm_url) return sb_config.cft_lvpm_json def get_cft_latest_version_from_milestone(milestone): url_request = get_cft_latest_versions_per_milestone() return url_request.json()["milestones"][milestone]["version"] def get_chromium_channel_revision(platform_code, channel): """Snapshots only exist for revisions where a build occurred. Therefore, not all found revisions will lead to snapshots.""" platform_key = None if platform_code in ["Mac_Arm", "Mac"]: platform_key = "Mac" elif platform_code in ["Linux_x64"]: platform_key = "Linux" elif platform_code in ["Win_x64"]: platform_key = "Windows" elif platform_code in ["Win"]: platform_key = "Win32" channel_key = None if channel.lower() == "stable": channel_key = "Stable" elif channel.lower() == "beta": channel_key = "Beta" elif channel.lower() == "dev": channel_key = "Dev" elif channel.lower() == "canary": channel_key = "Canary" base_url = "https://chromiumdash.appspot.com/fetch_releases" url = f"{base_url}?channel={channel_key}&platform={platform_key}&num=1" url_request = requests_get_with_retry(url) data = None if url_request.ok: data = url_request.text else: raise Exception("Could not determine Chromium revision!") if data: try: import ast result = ast.literal_eval(data) revision = result[0]["chromium_main_branch_position"] return str(revision) except Exception: return get_latest_chromedriver_version(platform_code) else: return get_latest_chromedriver_version(platform_code) def get_chromium_latest_revision(platform_code): base_url = "https://storage.googleapis.com/chromium-browser-snapshots" url = f"{base_url}/{platform_code}/LAST_CHANGE" url_request = requests_get_with_retry(url) if url_request.ok: latest_revision = url_request.text else: raise Exception("Could not determine latest Chromium revision!") return latest_revision def get_latest_chromedriver_version(channel="Stable"): try: if getattr(sb_config, "cft_lkgv_json", None): return sb_config.cft_lkgv_json["channels"][channel]["version"] req = requests_get( "https://googlechromelabs.github.io/" "chrome-for-testing/last-known-good-versions.json" ) if req and req.ok: sb_config.cft_lkgv_json = req.json() return req.json()["channels"][channel]["version"] except Exception: pass # If a problem with Chrome-for-Testing JSON API: Fall back return DEFAULT_CHROMEDRIVER_VERSION def get_latest_stable_chromedriver_version(): return get_latest_chromedriver_version(channel="Stable") def get_latest_beta_chromedriver_version(): return get_latest_chromedriver_version(channel="Beta") def get_latest_dev_chromedriver_version(): return get_latest_chromedriver_version(channel="Dev") def get_latest_canary_chromedriver_version(): return get_latest_chromedriver_version(channel="Canary") def log_d(message): """If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True, output from driver downloads are logged instead of printed.""" if getattr(sb_config.settings, "HIDE_DRIVER_DOWNLOADS", None): logging.debug(message) else: print(message) def main(override=None, intel_for_uc=None, force_uc=None): if override: found_proxy = None if getattr(sb_config, "proxy_driver", None): if " --proxy=" in " ".join(sys.argv): for arg in sys.argv: if arg.startswith("--proxy="): found_proxy = arg break if override == "chromedriver": sys.argv = ["seleniumbase", "get", "chromedriver"] elif override.startswith("chromedriver "): extra = override.split("chromedriver ")[1] sys.argv = ["seleniumbase", "get", "chromedriver", extra] elif override == "edgedriver": sys.argv = ["seleniumbase", "get", "edgedriver"] elif override.startswith("edgedriver "): extra = override.split("edgedriver ")[1] sys.argv = ["seleniumbase", "get", "edgedriver", extra] elif override == "geckodriver": sys.argv = ["seleniumbase", "get", "geckodriver"] elif override.startswith("geckodriver "): extra = override.split("geckodriver ")[1] sys.argv = ["seleniumbase", "get", "geckodriver", extra] elif override == "iedriver": sys.argv = ["seleniumbase", "get", "iedriver"] elif override.startswith("iedriver "): extra = override.split("iedriver ")[1] sys.argv = ["seleniumbase", "get", "iedriver", extra] elif override == "chromium": sys.argv = ["seleniumbase", "get", "chromium"] elif override.startswith("chromium "): extra = override.split("chromium ")[1] sys.argv = ["seleniumbase", "get", "chromium", extra] elif override == "cft": sys.argv = ["seleniumbase", "get", "cft"] elif override.startswith("cft "): extra = override.split("cft ")[1] sys.argv = ["seleniumbase", "get", "cft", extra] elif override == "chs": sys.argv = ["seleniumbase", "get", "chs"] elif override.startswith("chs "): extra = override.split("chs ")[1] sys.argv = ["seleniumbase", "get", "chs", extra] if found_proxy: sys.argv.append(found_proxy) num_args = len(sys.argv) if ( "sbase" in sys.argv[0].lower() or ("seleniumbase" in sys.argv[0].lower()) ): if num_args < 3 or num_args > 5: invalid_run_command() else: invalid_run_command() name = sys.argv[2].lower() if force_uc: name = "uc_driver" file_name = None download_url = None headless_ie_url = None headless_ie_exists = False headless_ie_file_name = None downloads_folder = DRIVER_DIR if ( hasattr(sb_config, "settings") and getattr(sb_config.settings, "NEW_DRIVER_DIR", None) and os.path.exists(sb_config.settings.NEW_DRIVER_DIR) ): downloads_folder = sb_config.settings.NEW_DRIVER_DIR elif override == "cft" or name == "cft": downloads_folder = DRIVER_DIR_CFT elif override == "chs" or name == "chs": downloads_folder = DRIVER_DIR_CHS elif override == "chromium": downloads_folder = DRIVER_DIR_CHROMIUM expected_contents = None platform_code = None copy_to_path = False latest_version = "" use_version = "" new_file = "" f_name = "" c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTWHITE_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTWHITE_EX c6 = colorama.Fore.LIGHTYELLOW_EX + colorama.Back.CYAN cr = colorama.Style.RESET_ALL if IS_LINUX: c1 = "" c2 = "" c3 = "" c4 = "" c5 = "" c6 = "" cr = "" if name == "chromedriver" or name == "uc_driver": if name == "uc_driver" and IS_ARM_MAC: intel_for_uc = True # uc_driver is generated from chromedriver last = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE" use_version = DEFAULT_CHROMEDRIVER_VERSION # Until get correct VER if ( not override and ( num_args == 3 or (num_args == 4 and "-p" in sys.argv[3].lower()) ) ): major_chrome_version = None try: from seleniumbase.core import detect_b_ver br_app = "google-chrome" major_chrome_version = ( detect_b_ver.get_browser_version_from_os(br_app) ).split(".")[0] if int(major_chrome_version) < 72: major_chrome_version = None except Exception: major_chrome_version = None if major_chrome_version and major_chrome_version.isnumeric(): num_args += 1 sys.argv.insert(3, major_chrome_version) get_latest = False get_v_latest = False get_previous = False get_beta = False get_canary = False if num_args == 4 or num_args == 5: if "-p" not in sys.argv[3].lower(): use_version = sys.argv[3] uv_low = use_version.lower() if uv_low == "latest" or uv_low == "stable": uv_low = "latest" # If "stable", rename get_latest = True elif uv_low == "latest-1" or uv_low == "previous": uv_low = "latest-1" # If "previous", rename get_previous = True elif uv_low == "beta": get_beta = True elif uv_low == "dev": use_version = get_latest_dev_chromedriver_version() sys.argv[3] = use_version elif uv_low == "canary": get_canary = True elif uv_low.isdigit() and int(uv_low) > 69: get_v_latest = True else: copy_to_path = True if num_args == 5: if "-p" in sys.argv[4].lower(): copy_to_path = True else: invalid_run_command() if IS_MAC: if IS_ARM_MAC and not intel_for_uc: use_version = use_version.lower() if ( use_version == "latest" or use_version == "stable" or use_version == "latest-1" or use_version == "previous" or use_version == "beta" or use_version == "canary" ): use_version = get_latest_stable_chromedriver_version() if use_version == "latest-1" or use_version == "previous": use_version = str(int(use_version.split(".")[0]) - 1) elif use_version == "beta": use_version = str(int(use_version.split(".")[0]) + 1) elif use_version == "canary": use_version = str(int(use_version.split(".")[0]) + 2) if ( IS_ARM_MAC and not intel_for_uc and int(use_version.split(".")[0]) > 105 ): file_name = "chromedriver_mac_arm64.zip" else: file_name = "chromedriver_mac64.zip" elif IS_LINUX: file_name = "chromedriver_linux64.zip" elif IS_WINDOWS: file_name = "chromedriver_win32.zip" # Works for win32 / win_x64 if not get_latest and not get_v_latest and num_args < 4: get_latest = True else: raise Exception( "Cannot determine which version of chromedriver to download!" ) found_chromedriver = False cft = False if get_latest or get_previous or get_beta or get_canary: use_version = get_latest_stable_chromedriver_version() found_chromedriver = True if get_previous and int(use_version.split(".")[0]) >= 115: get_v_latest = True use_version = str(int(use_version.split(".")[0]) - 1) elif get_beta and int(use_version.split(".")[0]) >= 115: get_v_latest = True use_version = get_latest_beta_chromedriver_version() use_version = use_version.split(".")[0] elif get_canary and int(use_version.split(".")[0]) >= 115: get_v_latest = True use_version = get_latest_canary_chromedriver_version() use_version = use_version.split(".")[0] force_cft = False if ( use_version.split(".")[0].isnumeric() and int(use_version.split(".")[0]) >= 115 ): force_cft = True if (get_v_latest or force_cft): if get_v_latest: if not force_cft: url_req = requests_get(last) if url_req.ok: latest_version = url_req.text else: latest_version = get_latest_stable_chromedriver_version() force_cft = False if not force_cft and int(use_version) < 115: last = last + "_" + use_version url_request = requests_get(last) if url_request.ok: found_chromedriver = True use_version = url_request.text if use_version == latest_version: get_latest = True else: url_request = None cft = True if force_cft: url_request = get_cft_known_good_versions() if ( url_request.ok and '"version":"%s"' % use_version in url_request.text ): fver = use_version found_chromedriver = True else: url_request = get_cft_latest_versions_per_milestone() if not force_cft and url_request.ok: try: fver = get_cft_latest_version_from_milestone( use_version ) except KeyError: use_version = str(int(use_version) - 1) fver = get_cft_latest_version_from_milestone( use_version ) found_chromedriver = True use_version = str(fver) if use_version == latest_version: get_latest = True download_url = ( "https://chromedriver.storage.googleapis.com/" "%s/%s" % (use_version, file_name) ) plat_arch = "" if cft: if IS_MAC: if ( IS_ARM_MAC and not intel_for_uc ): platform_code = "mac-arm64" file_name = "chromedriver-mac-arm64.zip" else: platform_code = "mac-x64" file_name = "chromedriver-mac-x64.zip" elif IS_LINUX: platform_code = "linux64" file_name = "chromedriver-linux64.zip" elif IS_WINDOWS: if "64" in ARCH: platform_code = "win64" file_name = "chromedriver-win64.zip" else: platform_code = "win32" file_name = "chromedriver-win32.zip" plat_arch = file_name.split(".zip")[0] download_url = ( "https://storage.googleapis.com/chrome-for-testing-public/" "%s/%s/%s" % (use_version, platform_code, file_name) ) url_request = None if not found_chromedriver: url_req = requests_get(last) if url_req.ok: latest_version = url_req.text if use_version == latest_version: get_latest = True url_request = requests_get(download_url) if found_chromedriver or url_request.ok: p_version = use_version p_version = c3 + use_version + cr latest_stable = get_latest_stable_chromedriver_version() latest_beta = get_latest_beta_chromedriver_version() latest_dev = get_latest_dev_chromedriver_version() latest_canary = get_latest_canary_chromedriver_version() vint = True int_use_ver = None int_latest_ver = None try: int_use_ver = int(use_version.split(".")[0]) int_latest_ver = int(latest_stable.split(".")[0]) except Exception: vint = False on_cft = False if int_latest_ver > 115: on_cft = True if cft and on_cft and use_version == latest_stable: p_version = p_version + " " + c2 + "(Latest Stable)" + cr + " " elif cft and on_cft and use_version == latest_beta: p_version = p_version + " " + c2 + "(Latest Beta)" + cr + " " elif cft and on_cft and use_version == latest_dev: p_version = p_version + " " + c2 + "(Latest Dev)" + cr + " " elif cft and on_cft and use_version == latest_canary: p_version = p_version + " " + c2 + "(Latest Canary)" + cr + " " elif not vint: pass elif vint and cft and on_cft and int_use_ver == int_latest_ver: p_version = p_version + " " + c2 + "(Stable)" + cr elif vint and cft and on_cft and int_use_ver == int_latest_ver + 1: p_version = p_version + " " + c2 + "(Beta)" + cr elif vint and cft and on_cft and int_use_ver == int_latest_ver + 2: p_version = p_version + " " + c2 + "(Dev / Canary)" + cr elif vint and cft and on_cft and int_use_ver == int_latest_ver - 1: p_version = p_version + " " + c6 + "(Previous Version)" + cr elif cft and not on_cft: pass else: not_latest = c5 + "(" + c4 + "Legacy Version" + c5 + ")" + cr p_version = p_version + " " + not_latest msg = c2 + "chromedriver to download" + cr log_d("\n*** %s = %s" % (msg, p_version)) else: raise Exception("Could not find chromedriver to download!\n") if not get_latest: pass elif name == "chrome" or name == "cft": set_version = None found_version = None use_version = None major_version = None if num_args >= 4: set_version = sys.argv[3] if ( set_version and set_version.split(".")[0].isnumeric() and int(set_version.split(".")[0]) >= 113 ): major_version = set_version.split(".")[0] elif ( not set_version or set_version.lower() == "latest" or set_version.lower() == "stable" ): found_version = get_latest_stable_chromedriver_version() elif ( set_version and ( set_version.lower() == "latest-1" or set_version.lower() == "previous" ) ): found_version = get_latest_stable_chromedriver_version() major_version = str(int(found_version.split(".")[0]) - 1) found_version = None elif (set_version and set_version.lower() == "beta"): found_version = get_latest_beta_chromedriver_version() elif (set_version and set_version.lower() == "dev"): found_version = get_latest_dev_chromedriver_version() elif (set_version and set_version.lower() == "canary"): found_version = get_latest_canary_chromedriver_version() if found_version and found_version.split(".")[0].isnumeric(): major_version = found_version.split(".")[0] use_version = found_version if not use_version: use_version = get_cft_latest_version_from_milestone(major_version) msg = c2 + "Chrome for Testing to download" + cr p_version = c3 + use_version + cr log_d("\n*** %s = %s" % (msg, p_version)) if IS_MAC: if IS_ARM_MAC: platform_code = "mac-arm64" file_name = "chrome-mac-arm64.zip" else: platform_code = "mac-x64" file_name = "chrome-mac-x64.zip" elif IS_LINUX: platform_code = "linux64" file_name = "chrome-linux64.zip" elif IS_WINDOWS: if "64" in ARCH: platform_code = "win64" file_name = "chrome-win64.zip" else: platform_code = "win32" file_name = "chrome-win32.zip" plat_arch = file_name.split(".zip")[0] download_url = ( "https://storage.googleapis.com/chrome-for-testing-public/" "%s/%s/%s" % (use_version, platform_code, file_name) ) elif name == "chromium": if IS_MAC: if IS_ARM_MAC: platform_code = "Mac_Arm" else: platform_code = "Mac" file_name = "chrome-mac.zip" elif IS_LINUX: platform_code = "Linux_x64" file_name = "chrome-linux.zip" elif IS_WINDOWS: if "64" in ARCH: platform_code = "Win_x64" else: platform_code = "Win" file_name = "chrome-win.zip" revision = get_chromium_latest_revision(platform_code) msg = c2 + "Chromium revision to download" + cr p_version = c3 + revision + cr log_d("\n*** %s = %s" % (msg, p_version)) download_url = ( "https://storage.googleapis.com/chromium-browser-snapshots/" "%s/%s/%s" % (platform_code, revision, file_name) ) downloads_folder = DRIVER_DIR_CHROMIUM elif name == "chrome-headless-shell" or name == "chs": set_version = None found_version = None use_version = None major_version = None if num_args >= 4: set_version = sys.argv[3] if ( set_version and set_version.split(".")[0].isnumeric() and int(set_version.split(".")[0]) >= 113 ): major_version = set_version.split(".")[0] elif ( not set_version or set_version.lower() == "latest" or set_version.lower() == "stable" ): found_version = get_latest_stable_chromedriver_version() elif ( set_version and ( set_version.lower() == "latest-1" or set_version.lower() == "previous" ) ): found_version = get_latest_stable_chromedriver_version() major_version = str(int(found_version.split(".")[0]) - 1) found_version = None elif (set_version and set_version.lower() == "beta"): found_version = get_latest_beta_chromedriver_version() elif (set_version and set_version.lower() == "dev"): found_version = get_latest_dev_chromedriver_version() elif (set_version and set_version.lower() == "canary"): found_version = get_latest_canary_chromedriver_version() if found_version and found_version.split(".")[0].isnumeric(): major_version = found_version.split(".")[0] use_version = found_version if not use_version: use_version = get_cft_latest_version_from_milestone(major_version) msg = c2 + "Chrome-Headless-Shell to download" + cr p_version = c3 + use_version + cr log_d("\n*** %s = %s" % (msg, p_version)) if IS_MAC: if IS_ARM_MAC: platform_code = "mac-arm64" file_name = "chrome-headless-shell-mac-arm64.zip" else: platform_code = "mac-x64" file_name = "chrome-headless-shell-mac-x64.zip" elif IS_LINUX: platform_code = "linux64" file_name = "chrome-headless-shell-linux64.zip" elif IS_WINDOWS: if "64" in ARCH: platform_code = "win64" file_name = "chrome-headless-shell-win64.zip" else: platform_code = "win32" file_name = "chrome-headless-shell-win32.zip" plat_arch = file_name.split(".zip")[0] download_url = ( "https://storage.googleapis.com/chrome-for-testing-public/" "%s/%s/%s" % (use_version, platform_code, file_name) ) elif name == "geckodriver" or name == "firefoxdriver": use_version = DEFAULT_GECKODRIVER_VERSION found_geckodriver = False if num_args == 4 or num_args == 5: if "-p" not in sys.argv[3].lower(): use_version = sys.argv[3] if use_version.lower() == "latest": last = ( "https://api.github.com/repos/" "mozilla/geckodriver/releases/latest" ) url_request = requests_get(last) if url_request.ok: found_geckodriver = True use_version = url_request.json()["tag_name"] else: use_version = DEFAULT_GECKODRIVER_VERSION else: copy_to_path = True if num_args == 5: if "-p" in sys.argv[4].lower(): copy_to_path = True else: invalid_run_command() if IS_MAC: if IS_ARM_MAC: file_name = "geckodriver-%s-macos-aarch64.tar.gz" % use_version else: file_name = "geckodriver-%s-macos.tar.gz" % use_version elif IS_LINUX: if "64" in ARCH: if "aarch64" in platform.processor(): file_name = ( "geckodriver-%s-linux-aarch64.tar.gz" % use_version ) else: file_name = "geckodriver-%s-linux64.tar.gz" % use_version else: file_name = "geckodriver-%s-linux32.tar.gz" % use_version elif IS_WINDOWS: file_name = "geckodriver-%s-win64.zip" % use_version else: raise Exception( "Cannot determine which version of geckodriver to download!" ) download_url = ( "https://github.com/mozilla/geckodriver/" "releases/download/" "%s/%s" % (use_version, file_name) ) url_request = None if not found_geckodriver: url_request = requests_get(download_url) if found_geckodriver or url_request.ok: msg = c2 + "geckodriver to download" + cr p_version = c3 + use_version + cr log_d("\n*** %s = %s" % (msg, p_version)) else: raise Exception( "\nCould not find the specified geckodriver " "version to download!\n" ) elif name == "edgedriver" or name == "msedgedriver": name = "edgedriver" last = ( "https://msedgewebdriverstorage.blob.core.windows.net" "/edgewebdriver/LATEST_STABLE" ) if ( not override and ( num_args == 3 or (num_args == 4 and "-p" in sys.argv[3].lower()) ) ): use_version = "latest" major_edge_version = None try: from seleniumbase.core import detect_b_ver br_app = "edge" major_edge_version = ( detect_b_ver.get_browser_version_from_os(br_app) ).split(".")[0] if int(major_edge_version) < 80: major_edge_version = None except Exception: major_edge_version = None if major_edge_version and major_edge_version.isnumeric(): num_args += 1 sys.argv.insert(3, major_edge_version) use_version = major_edge_version get_latest = False if num_args == 3: get_latest = True if num_args == 4 and "-p" in sys.argv[3].lower(): get_latest = True if num_args == 4 or num_args == 5: if "-p" not in sys.argv[3].lower(): use_version = sys.argv[3] if use_version.lower() == "latest": use_version = DEFAULT_EDGEDRIVER_VERSION get_latest = True else: copy_to_path = True if num_args == 5: if "-p" in sys.argv[4].lower(): copy_to_path = True else: invalid_run_command() if get_latest: url_request = requests_get_with_retry(last) if url_request.ok: use_version = url_request.text.split("\r")[0].split("\n")[0] use_version = use_version.split(".")[0] else: use_version = DEFAULT_EDGEDRIVER_VERSION suffix = None if IS_WINDOWS and "64" in ARCH: file_name = "edgedriver_win64.zip" suffix = "WINDOWS" elif IS_WINDOWS: file_name = "edgedriver_win32.zip" suffix = "WINDOWS" elif IS_MAC: if IS_ARM_MAC and int(use_version.split(".")[0]) > 104: file_name = "edgedriver_mac64_m1.zip" else: file_name = "edgedriver_mac64.zip" suffix = "MACOS" elif IS_LINUX: file_name = "edgedriver_linux64.zip" suffix = "LINUX" else: raise Exception( "Cannot determine which version of EdgeDriver to download!" ) if use_version.isdigit(): edgedriver_st = "https://msedgedriver.microsoft.com/LATEST_RELEASE" use_version = "%s_%s_%s" % (edgedriver_st, use_version, suffix) url_request = requests_get_with_retry(use_version) if url_request.ok: use_version = url_request.text.split("\r")[0].split("\n")[0] if ( int(use_version.split(".")[0]) == 115 and use_version.startswith("115.0") and use_version != "115.0.1901.183" ): use_version = "115.0.1901.183" download_url = "https://msedgedriver.microsoft.com/%s/%s" % ( use_version, file_name, ) if not get_latest and not use_version == DEFAULT_EDGEDRIVER_VERSION: url_request = requests_get_with_retry(download_url) if not url_request.ok: raise Exception( "Could not find version [%s] of EdgeDriver!" % use_version ) msg = c2 + "edgedriver to download" + cr p_version = c3 + use_version + cr log_d("\n*** %s = %s" % (msg, p_version)) elif name == "iedriver": full_version = "4.14.0" use_version = full_version if IS_WINDOWS and "64" in ARCH: file_name = "IEDriverServer_x64_%s.zip" % full_version elif IS_WINDOWS: file_name = "IEDriverServer_Win32_%s.zip" % full_version else: raise Exception( "Sorry! IEDriver is only for " "Windows-based systems!" ) download_url = ( "https://github.com/SeleniumHQ/selenium/" "releases/download/selenium-" "%s/%s" % (full_version, file_name) ) headless_ie_version = "v1.4" headless_ie_file_name = "headless-selenium-for-win-v1-4.zip" headless_ie_url = ( "https://github.com/kybu/headless-selenium-for-win/" "releases/download/" "%s/%s" % (headless_ie_version, headless_ie_file_name) ) url_request = requests_get_with_retry(headless_ie_url) if url_request.ok: headless_ie_exists = True msg = c2 + "HeadlessIEDriver to download" + cr p_version = c3 + headless_ie_version + cr log_d("\n*** %s = %s" % (msg, p_version)) else: invalid_run_command() if file_name is None or download_url is None: invalid_run_command() file_path = os.path.join(downloads_folder, file_name) if not os.path.exists(downloads_folder): os.makedirs(downloads_folder) driver_name = None # The name of the driver executable driver_contents = [] # The contents of the driver zip file if headless_ie_exists: headless_ie_file_path = os.path.join( downloads_folder, headless_ie_file_name ) log_d( "\nDownloading %s from:\n%s ..." % (headless_ie_file_name, headless_ie_url) ) remote_file = requests_get_with_retry(headless_ie_url) with open(headless_ie_file_path, "wb") as file: file.write(remote_file.content) log_d("%sDownload Complete!%s\n" % (c1, cr)) zip_file_path = headless_ie_file_path zip_ref = zipfile.ZipFile(zip_file_path, "r") contents = zip_ref.namelist() h_ie_fn = headless_ie_file_name.split(".zip")[0] expected_contents = [ "%s/" % h_ie_fn, "%s/ruby_example/" % h_ie_fn, "%s/ruby_example/Gemfile" % h_ie_fn, "%s/ruby_example/Gemfile.lock" % h_ie_fn, "%s/ruby_example/ruby_example.rb" % h_ie_fn, "%s/desktop_utils.exe" % h_ie_fn, "%s/headless_ie_selenium.exe" % h_ie_fn, "%s/README.md" % h_ie_fn, ] if len(contents) > 8: raise Exception("Unexpected content in HeadlessIEDriver Zip file!") for content in contents: if content not in expected_contents: raise Exception( "Expected file [%s] missing from [%s]" % (content, expected_contents) ) # Zip file is valid. Proceed. driver_path = None driver_file = None filename = None for f_name in contents: # Remove existing version if exists str_name = str(f_name) new_file = os.path.join(downloads_folder, str_name) if str_name == "%s/headless_ie_selenium.exe" % h_ie_fn: driver_file = str_name driver_path = new_file filename = "headless_ie_selenium.exe" if os.path.exists(new_file): os.remove(new_file) if not driver_file or not driver_path or not filename: raise Exception("headless_ie_selenium.exe missing from Zip file!") log_d("Extracting %s from %s ..." % (filename, headless_ie_file_name)) zip_ref.extractall(downloads_folder) zip_ref.close() os.remove(zip_file_path) shutil.copy2(driver_path, os.path.join(downloads_folder, filename)) log_d("%sUnzip Complete!%s\n" % (c2, cr)) to_remove = [ "%s/%s/ruby_example/Gemfile" % (downloads_folder, h_ie_fn), "%s/%s/ruby_example/Gemfile.lock" % (downloads_folder, h_ie_fn), "%s/%s/ruby_example/ruby_example.rb" % (downloads_folder, h_ie_fn), "%s/%s/desktop_utils.exe" % (downloads_folder, h_ie_fn), "%s/%s/headless_ie_selenium.exe" % (downloads_folder, h_ie_fn), "%s/%s/README.md" % (downloads_folder, h_ie_fn), ] for file_to_remove in to_remove: if os.path.exists(file_to_remove): os.remove(file_to_remove) if os.path.exists("%s/%s/ruby_example/" % (downloads_folder, h_ie_fn)): # Only works if the directory is empty os.rmdir("%s/%s/ruby_example/" % (downloads_folder, h_ie_fn)) if os.path.exists(os.path.join(downloads_folder, h_ie_fn)): # Only works if the directory is empty os.rmdir(os.path.join(downloads_folder, h_ie_fn)) driver_path = os.path.join(downloads_folder, filename) log_d( "The file [%s] was saved to:\n%s%s%s\n" % (filename, c3, driver_path, cr) ) log_d("Making [%s %s] executable ..." % (driver_file, use_version)) make_executable(driver_path) log_d( "%s[%s %s] is now ready for use!%s" % (c1, driver_file, use_version, cr) ) log_d("\nDownloading %s from:\n%s ..." % (file_name, download_url)) remote_file = requests_get_with_retry(download_url) with open(file_path, "wb") as file: file.write(remote_file.content) if file_name.endswith(".zip"): zip_file_path = file_path zip_ref = zipfile.ZipFile(zip_file_path, "r") contents = zip_ref.namelist() log_d("%sDownload Complete!%s\n" % (c1, cr)) if ( len(contents) >= 1 and name in ["chromedriver", "uc_driver", "geckodriver"] ): for f_name in contents: if ( (name == "chromedriver" or name == "uc_driver") and ( f_name.split("/")[-1] == "chromedriver" or f_name.split("/")[-1] == "chromedriver.exe" ) ): driver_name = f_name.split("/")[-1] driver_contents = [driver_name] # Remove existing version if exists new_file = os.path.join(downloads_folder, str(f_name)) if name == "uc_driver": if new_file.endswith("drivers/chromedriver"): new_file = new_file.replace( "drivers/chromedriver", "drivers/uc_driver" ) elif new_file.endswith("drivers/chromedriver.exe"): new_file = new_file.replace( "drivers/chromedriver.exe", "drivers/uc_driver.exe" ) elif "drivers/%s/chromedriver" % plat_arch in new_file: new_file = new_file.replace( "drivers/%s/chromedriver" % plat_arch, "drivers/%s/uc_driver" % plat_arch ) elif "drivers/%s/chromedriver.exe" % plat_arch in new_file: new_file = new_file.replace( "drivers/%s/chromedriver.exe" % plat_arch, "drivers/%s/uc_driver.exe" % plat_arch ) if "Driver" in new_file or "driver" in new_file: if os.path.exists(new_file): os.remove(new_file) # Technically the old file now if driver_contents: contents = driver_contents log_d("Extracting %s from %s ..." % (contents, file_name)) if name == "uc_driver": f_name = "uc_driver" new_file = os.path.join(downloads_folder, f_name) if os.path.exists(new_file): os.remove(new_file) zipinfos = zip_ref.infolist() for zipinfo in zipinfos: if zipinfo.filename.split("/")[-1] == "chromedriver": zipinfo.filename = "uc_driver" zip_ref.extract(zipinfo, downloads_folder) elif zipinfo.filename.split("/")[-1] == "chromedriver.exe": zipinfo.filename = "uc_driver.exe" zip_ref.extract(zipinfo, downloads_folder) contents = zip_ref.namelist() if driver_contents: contents = driver_contents elif name == "chromedriver" or name == "uc_driver": zipinfos = zip_ref.infolist() for zipinfo in zipinfos: if zipinfo.filename.split("/")[-1] == "chromedriver": zipinfo.filename = "chromedriver" elif zipinfo.filename.split("/")[-1] == ( "chromedriver.exe" ): zipinfo.filename = "chromedriver.exe" if ( zipinfo.filename.split("/")[-1] == "chromedriver" or zipinfo.filename.split("/")[-1] == ( "chromedriver.exe" ) ): zip_ref.extract(zipinfo, downloads_folder) contents = zip_ref.namelist() if driver_contents: contents = driver_contents else: zip_ref.extractall(downloads_folder) zip_ref.close() os.remove(zip_file_path) log_d("%sUnzip Complete!%s\n" % (c2, cr)) for f_name in contents: if name == "uc_driver": if IS_WINDOWS: f_name = "uc_driver.exe" else: f_name = "uc_driver" new_file = os.path.join(downloads_folder, str(f_name)) pr_file = c3 + new_file + cr d_folder = os.sep.join(pr_file.split(os.sep)[:-1]) + os.sep d_file = pr_file.split(os.sep)[-1] d_ff = c3 + d_folder + cr + "\n" + c3 + d_file + cr log_d("The file [%s] was saved to:\n%s\n" % (f_name, d_ff)) log_d("Making [%s %s] executable ..." % (f_name, use_version)) make_executable(new_file) log_d( "%s[%s %s] is now ready for use!%s" % (c1, f_name, use_version, cr) ) if copy_to_path and os.path.exists(LOCAL_PATH): path_file = LOCAL_PATH + f_name shutil.copy2(new_file, path_file) make_executable(path_file) log_d("Also copied to: %s%s%s" % (c3, path_file, cr)) log_d("") elif ( name == "edgedriver" or name == "msedgedriver" or name == "iedriver" ): if IS_MAC or IS_LINUX: # Mac / Linux expected_contents = [ "Driver_Notes/", "Driver_Notes/EULA", "Driver_Notes/LICENSE", "Driver_Notes/credits.html", "msedgedriver", "libc++.dylib", ] else: # Windows expected_contents = [ "Driver_Notes/", "Driver_Notes/credits.html", "Driver_Notes/EULA", "Driver_Notes/LICENSE", "msedgedriver.exe", ] if name == "iedriver": expected_contents = ["IEDriverServer.exe"] if len(contents) > 5: raise Exception("Unexpected content in EdgeDriver Zip file!") for content in contents: if content not in expected_contents: raise Exception( "Expected file [%s] missing from [%s]" % (content, expected_contents) ) # Zip file is valid. Proceed. driver_path = None driver_file = None for f_name in contents: # Remove existing version if exists str_name = str(f_name) new_file = os.path.join(downloads_folder, str_name) if ( ((IS_MAC or IS_LINUX) and str_name == "msedgedriver") or ( str_name == "msedgedriver.exe" or str_name == "IEDriverServer.exe" ) ): driver_file = str_name driver_path = new_file if os.path.exists(new_file): os.remove(new_file) if not driver_file or not driver_path: if str_name == "IEDriverServer.exe": raise Exception("IEDriverServer missing from Zip file!") raise Exception("msedgedriver missing from Zip file!") log_d("Extracting %s from %s ..." % (contents, file_name)) zip_ref.extractall(downloads_folder) zip_ref.close() os.remove(zip_file_path) log_d("%sUnzip Complete!%s\n" % (c2, cr)) to_remove = [ "%s/Driver_Notes/credits.html" % downloads_folder, "%s/Driver_Notes/EULA" % downloads_folder, "%s/Driver_Notes/LICENSE" % downloads_folder, ] for file_to_remove in to_remove: if os.path.exists(file_to_remove): os.remove(file_to_remove) if os.path.exists(os.path.join(downloads_folder, "Driver_Notes/")): # Only works if the directory is empty os.rmdir(os.path.join(downloads_folder, "Driver_Notes/")) driver_base = os.sep.join(driver_path.split(os.sep)[:-1]) driver_file = driver_path.split(os.sep)[-1] pr_driver_base = c3 + driver_base + cr pr_sep = c3 + os.sep + cr pr_driver_file = c3 + driver_file + cr log_d( "The file [%s] was saved to:\n%s%s\n%s\n" % (driver_file, pr_driver_base, pr_sep, pr_driver_file) ) log_d("Making [%s %s] executable ..." % (driver_file, use_version)) make_executable(driver_path) log_d( "%s[%s %s] is now ready for use!%s" % (c1, driver_file, use_version, cr) ) if copy_to_path and os.path.exists(LOCAL_PATH): path_file = LOCAL_PATH + f_name shutil.copy2(new_file, path_file) make_executable(path_file) log_d("Also copied to: %s%s%s" % (c3, path_file, cr)) log_d("") elif name == "chrome" or name == "cft": # Zip file is valid. Proceed. driver_path = None driver_file = None base_path = os.sep.join(zip_file_path.split(os.sep)[:-1]) folder_name = contents[0].split("/")[0] folder_path = os.path.join(base_path, folder_name) if IS_MAC or IS_LINUX: if ( "chrome-" in folder_path and "drivers" in folder_path and os.path.exists(folder_path) ): shutil.rmtree(folder_path) subprocess.run( ["unzip", zip_file_path, "-d", downloads_folder] ) elif IS_WINDOWS: subprocess.run( [ "powershell", "Expand-Archive", "-Path", zip_file_path, "-DestinationPath", downloads_folder, "-Force", ] ) else: zip_ref.extractall(downloads_folder) zip_ref.close() with suppress(Exception): os.remove(zip_file_path) log_d("%sUnzip Complete!%s\n" % (c2, cr)) pr_base_path = c3 + base_path + cr pr_sep = c3 + os.sep + cr pr_folder_name = c3 + folder_name + cr log_d( "Chrome for Testing was saved inside:\n%s%s\n%s\n" % (pr_base_path, pr_sep, pr_folder_name) ) elif name == "chromium": # Zip file is valid. Proceed. driver_path = None driver_file = None base_path = os.sep.join(zip_file_path.split(os.sep)[:-1]) folder_name = contents[0].split("/")[0] folder_path = os.path.join(base_path, folder_name) if IS_MAC or IS_LINUX: if ( "chromium" in folder_path and "drivers" in folder_path and os.path.exists(folder_path) ): shutil.rmtree(folder_path) subprocess.run( ["unzip", zip_file_path, "-d", downloads_folder] ) elif IS_WINDOWS: subprocess.run( [ "powershell", "Expand-Archive", "-Path", zip_file_path, "-DestinationPath", downloads_folder, "-Force", ] ) else: zip_ref.extractall(downloads_folder) zip_ref.close() with suppress(Exception): os.remove(zip_file_path) log_d("%sUnzip Complete!%s\n" % (c2, cr)) pr_base_path = c3 + base_path + cr pr_sep = c3 + os.sep + cr pr_folder_name = c3 + folder_name + cr log_d( "Chromium was saved inside:\n%s%s\n%s\n" % (pr_base_path, pr_sep, pr_folder_name) ) elif name == "chrome-headless-shell" or name == "chs": # Zip file is valid. Proceed. driver_path = None driver_file = None base_path = os.sep.join(zip_file_path.split(os.sep)[:-1]) folder_name = contents[0].split("/")[0] folder_path = os.path.join(base_path, folder_name) if IS_MAC or IS_LINUX: if ( "chrome-headless-shell-" in folder_path and "drivers" in folder_path and os.path.exists(folder_path) ): shutil.rmtree(folder_path) subprocess.run( ["unzip", zip_file_path, "-d", downloads_folder] ) elif IS_WINDOWS: subprocess.run( [ "powershell", "Expand-Archive", "-Path", zip_file_path, "-DestinationPath", downloads_folder, "-Force", ] ) else: zip_ref.extractall(downloads_folder) zip_ref.close() with suppress(Exception): os.remove(zip_file_path) log_d("%sUnzip Complete!%s\n" % (c2, cr)) pr_base_path = c3 + base_path + cr pr_sep = c3 + os.sep + cr pr_folder_name = c3 + folder_name + cr log_d( "Chrome-Headless-Shell was saved inside:\n%s%s\n%s\n" % (pr_base_path, pr_sep, pr_folder_name) ) elif len(contents) == 0: raise Exception("Zip file %s is empty!" % zip_file_path) else: raise Exception("Expecting only one file in %s!" % zip_file_path) elif file_name.endswith(".tar.gz"): tar_file_path = file_path tar = tarfile.open(file_path) contents = tar.getnames() if len(contents) == 1: log_d("%sDownload Complete!%s\n" % (c1, cr)) for f_name in contents: # Remove existing version if exists new_file = os.path.join(downloads_folder, str(f_name)) if "Driver" in new_file or "driver" in new_file: if os.path.exists(new_file): os.remove(new_file) # Technically the old file now log_d("Extracting %s from %s ..." % (contents, file_name)) if sys.version_info < (3, 12): tar.extractall(downloads_folder) else: tar.extractall(downloads_folder, filter="fully_trusted") tar.close() os.remove(tar_file_path) log_d("%sUnzip Complete!%s\n" % (c2, cr)) for f_name in contents: new_file = os.path.join(downloads_folder, str(f_name)) pr_file = c3 + new_file + cr log_d("The file [%s] was saved to:\n%s\n" % (f_name, pr_file)) log_d("Making [%s %s] executable ..." % (f_name, use_version)) make_executable(new_file) log_d( "%s[%s %s] is now ready for use!%s" % (c1, f_name, use_version, cr) ) if copy_to_path and os.path.exists(LOCAL_PATH): path_file = LOCAL_PATH + f_name shutil.copy2(new_file, path_file) make_executable(path_file) log_d("Also copied to: %s%s%s" % (c3, path_file, cr)) log_d("") elif len(contents) == 0: raise Exception("Tar file %s is empty!" % tar_file_path) else: raise Exception("Expecting only one file in %s!" % tar_file_path) else: # Not a .zip file or a .tar.gz file. Just a direct download. if "Driver" in file_name or "driver" in file_name: log_d("%sDownload Complete!%s\n" % (c1, cr)) log_d("Making [%s] executable ..." % file_name) make_executable(file_path) log_d("%s[%s] is now ready for use!%s" % (c1, file_name, cr)) log_d("Location of [%s]:\n%s\n" % (file_name, file_path)) if __name__ == "__main__": main() ================================================ FILE: seleniumbase/console_scripts/sb_mkchart.py ================================================ """ Creates a new SeleniumBase chart presentation with boilerplate code. Usage: seleniumbase mkchart [FILE.py] [LANG] or sbase mkchart [FILE.py] [LANG] Example: sbase mkchart new_chart.py --en Language Options: --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish Output: Creates a new SeleniumBase chart presentation. If the file already exists, an error is raised. By default, the slides are written in English, and use a "sky" theme with "slide" transition. The chart can be used as a basic boilerplate. """ import colorama import os import sys def invalid_run_command(msg=None): exp = " ** mkchart **\n\n" exp += " Usage:\n" exp += " seleniumbase mkchart [FILE.py] [LANG]\n" exp += " OR sbase mkchart [FILE.py] [LANG]\n" exp += " Example:\n" exp += " sbase mkchart new_chart.py --en\n" exp += " Language Options:\n" exp += " --en / --English | --zh / --Chinese\n" exp += " --nl / --Dutch | --fr / --French\n" exp += " --it / --Italian | --ja / --Japanese\n" exp += " --ko / --Korean | --pt / --Portuguese\n" exp += " --ru / --Russian | --es / --Spanish\n" exp += " Output:\n" exp += " Creates a new SeleniumBase chart presentation.\n" exp += " If the file already exists, an error is raised.\n" exp += " By default, the slides are written in English,\n" exp += ' and use a "sky" theme with "slide" transition.\n' exp += " The chart can be used as a basic boilerplate.\n" if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) elif msg == "help": print("\n%s" % exp) sys.exit() else: raise Exception("INVALID RUN COMMAND!\n\n%s\n%s\n" % (exp, msg)) def main(): c1 = "" c5 = "" c7 = "" cr = "" if "linux" not in sys.platform: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL help_me = False error_msg = None invalid_cmd = None language = "English" command_args = sys.argv[2:] file_name = command_args[0] if file_name == "-h" or file_name == "--help": invalid_run_command("help") elif not file_name.endswith(".py"): error_msg = 'File name must end with ".py"!' elif "*" in file_name or len(str(file_name)) < 4: error_msg = "Invalid file name!" elif file_name.startswith("-"): error_msg = 'File name cannot start with "-"!' elif "/" in str(file_name) or "\\" in str(file_name): error_msg = "File must be created in the current directory!" elif os.path.exists(os.getcwd() + "/" + file_name): error_msg = 'File "%s" already exists in this directory!' % file_name if error_msg: error_msg = c5 + "ERROR: " + error_msg + cr invalid_run_command(error_msg) if len(command_args) >= 2: options = command_args[1:] for option in options: option = option.lower() if option == "-h" or option == "--help": help_me = True elif option == "--en" or option == "--english": language = "English" elif option == "--zh" or option == "--chinese": language = "Chinese" elif option == "--nl" or option == "--dutch": language = "Dutch" elif option == "--fr" or option == "--french": language = "French" elif option == "--it" or option == "--italian": language = "Italian" elif option == "--ja" or option == "--japanese": language = "Japanese" elif option == "--ko" or option == "--korean": language = "Korean" elif option == "--pt" or option == "--portuguese": language = "Portuguese" elif option == "--ru" or option == "--russian": language = "Russian" elif option == "--es" or option == "--spanish": language = "Spanish" else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break if help_me: invalid_run_command(invalid_cmd) dir_name = os.getcwd() file_path = "%s/%s" % (dir_name, file_name) html_name = file_name.replace(".py", ".html") class_name = "MyTestClass" item = "Item" select_option = "Select option" chart_options = '"pie", "bar", "column", "line", "area"' if language == "Chinese": class_name = "我的测试类" item = "目的" select_option = "选择选项" chart_options = '"饼图", "条形图", "柱形图", "折线图", "面积图"' elif language == "Dutch": class_name = "MijnTestklasse" item = "Voorwerp" select_option = "Optie selecteren" chart_options = '"cirkel", "staaf", "kolom", "lijn", "vlak"' elif language == "French": class_name = "MaClasseDeTest" item = "Objet" select_option = "Sélectionner option" chart_options = '"secteurs" "barres" "colonnes" "linéaire" "aires"' elif language == "Italian": class_name = "MiaClasseDiTest" item = "Oggetto" select_option = "Selezionare opzione" chart_options = '"torta", "barre", "colonne", "linee", "area"' elif language == "Japanese": class_name = "私のテストクラス" item = "物体" select_option = "でオプションを選択" chart_options = '"円", "棒", "縦棒", "折れ線", "面"' elif language == "Korean": class_name = "테스트_클래스" item = "물체" select_option = "옵션 선택" chart_options = '"원형", "막대", "열", "선", "영역"' elif language == "Portuguese": class_name = "MinhaClasseDeTeste" item = "Objeto" select_option = "Selecionar opção" chart_options = '"pizza", "barras", "colunas", "linhas", "área"' elif language == "Russian": class_name = "МойТестовыйКласс" item = "Вещь" select_option = "Выбрать опцию" chart_options = '"круговую" "бар" "столбчатую" "линейную" "области"' elif language == "Spanish": class_name = "MiClaseDePrueba" item = "Objeto" select_option = "Seleccionar opción" chart_options = '"circular", "barras", "columnas", "líneas", "área"' import_line = "from seleniumbase import BaseCase" main_line = "BaseCase.main(__name__, __file__)" parent_class = "BaseCase" if language != "English": from seleniumbase.translate.master_dict import MD_F import_line = MD_F.get_import_line(language) parent_class = MD_F.get_lang_parent_class(language) class_line = "class %s(%s):" % (class_name, parent_class) settings = 'theme="sky", transition="slide"' chart_settings = 'title="Chart 1"' add_slide = '"

      Chart Demo

      " + self.extract_chart()' data = [] data.append("%s" % import_line) data.append("%s" % main_line) data.append("") data.append("") data.append("%s" % class_line) data.append(" def test_chart_presentation(self):") data.append(" self.create_presentation(%s)" % settings) data.append("") data.append(" # %s => %s" % (select_option, chart_options)) data.append(" self.create_pie_chart(%s)" % chart_settings) data.append(' self.add_data_point("%s A", 36)' % item) data.append(' self.add_data_point("%s B", 33)' % item) data.append(' self.add_data_point("%s C", 27)' % item) data.append(' self.add_data_point("%s D", 21)' % item) data.append(' self.add_data_point("%s E", 18)' % item) data.append(' self.add_data_point("%s F", 15)' % item) data.append(" self.add_slide(%s)" % add_slide) data.append("") data.append(' self.begin_presentation(filename="%s")' % html_name) data.append("") new_data = [] if language == "English": new_data = data else: from seleniumbase.translate.master_dict import MD from seleniumbase.translate.master_dict import MD_L_Codes md = MD.md lang_codes = MD_L_Codes.lang nl_code = lang_codes[language] dl_code = lang_codes["English"] for line in data: found_swap = False replace_count = line.count("self.") # Total possible replacements for key in md.keys(): original = "self." + md[key][dl_code] + "(" if original in line: replacement = "self." + md[key][nl_code] + "(" new_line = line.replace(original, replacement) found_swap = True replace_count -= 1 if replace_count == 0: break # Done making replacements else: # There might be another method to replace in the line. # Example: self.assert_true("Name" in self.get_title()) line = new_line continue if main_line in line: new_main = "%s.main(__name__, __file__)" % parent_class new_line = line.replace(main_line, new_main) found_swap = True if found_swap: if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] new_data.append(new_line) continue new_data.append(line) data = new_data file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if " " not in file_name: os.system("sbase print %s -n" % file_name) elif '"' not in file_name: os.system('sbase print "%s" -n' % file_name) else: os.system("sbase print '%s' -n" % file_name) success = ( "\n" + c1 + '* Chart Presentation: "' + file_name + '" was created! *' "" + cr + "\n" ) print(success) if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_mkdir.py ================================================ """ Creates a new folder for running SeleniumBase scripts. Usage: seleniumbase mkdir [DIRECTORY] [OPTIONS] OR sbase mkdir [DIRECTORY] [OPTIONS] Example: sbase mkdir ui_tests Options: -b / --basic (Only config files. No tests added.) --gha (Include GitHub Actions YML with defaults.) Output: Creates a new folder for running SBase scripts. The new folder contains default config files, sample tests for helping new users get started, and Python boilerplates for setting up customized test frameworks. """ import colorama import os import sys def invalid_run_command(msg=None): exp = " ** mkdir **\n\n" exp += " Usage:\n" exp += " seleniumbase mkdir [DIRECTORY] [OPTIONS]\n" exp += " OR sbase mkdir [DIRECTORY] [OPTIONS]\n" exp += " Example:\n" exp += " sbase mkdir ui_tests\n" exp += " Options:\n" exp += " -b / --basic (Only config files. No tests added.)\n" exp += " --gha (Include GitHub Actions YML with defaults.)\n" exp += " Output:\n" exp += " Creates a new folder for running SBase scripts.\n" exp += " The new folder contains default config files,\n" exp += " sample tests for helping new users get started,\n" exp += " and Python boilerplates for setting up customized\n" exp += " test frameworks.\n" if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) elif msg == "help": print("\n%s" % exp) sys.exit() else: raise Exception("INVALID RUN COMMAND!\n\n%s\n%s\n" % (exp, msg)) def main(): c1 = "" c5 = "" c7 = "" cr = "" if "linux" not in sys.platform: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL gha = False basic = False help_me = False error_msg = None invalid_cmd = None command_args = sys.argv[2:] dir_name = command_args[0] if dir_name == "-h" or dir_name == "--help": invalid_run_command("help") elif len(str(dir_name)) < 2: error_msg = "Directory name length must be at least 2 characters long!" elif "/" in str(dir_name) or "\\" in str(dir_name): error_msg = 'Directory name must not include slashes ("/", "\\")!' elif dir_name.startswith("-"): error_msg = 'Directory name cannot start with "-"!' elif os.path.exists(os.getcwd() + "/" + dir_name): error_msg = ( 'Directory "%s" already exists in this directory!' % dir_name ) if error_msg: error_msg = c5 + "ERROR: " + error_msg + cr invalid_run_command(error_msg) if len(command_args) >= 2: options = command_args[1:] for option in options: option = option.lower() if option == "-h" or option == "--help": help_me = True elif option == "-b" or option == "--basic": basic = True elif option == "--gha": gha = True else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break if help_me: invalid_run_command(invalid_cmd) os.mkdir(dir_name) data = [] seleniumbase_req = "seleniumbase" try: from seleniumbase import __version__ seleniumbase_req = "seleniumbase>=%s" % str(__version__) except Exception: pass data.append(seleniumbase_req) data.append("") file_path = "%s/%s" % (dir_name, "requirements.txt") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("[pytest]") data.append("addopts = --capture=tee-sys -p no:cacheprovider") data.append("norecursedirs = .* build dist recordings temp assets") data.append("filterwarnings =") data.append(" ignore::pytest.PytestWarning") data.append(" ignore:.*U.*mode is deprecated:DeprecationWarning") data.append("junit_family = legacy") data.append("python_files = test_*.py *_test.py *_tests.py *_suite.py") data.append("python_classes = Test* *Test* *Test *Tests *Suite") data.append("python_functions = test_*") data.append("markers =") data.append(" marker1: custom marker") data.append(" marker2: custom marker") data.append(" marker3: custom marker") data.append(" marker_test_suite: custom marker") data.append(" expected_failure: custom marker") data.append(" local: custom marker") data.append(" remote: custom marker") data.append(" offline: custom marker") data.append(" develop: custom marker") data.append(" qa: custom marker") data.append(" ci: custom marker") data.append(" e2e: custom marker") data.append(" ready: custom marker") data.append(" smoke: custom marker") data.append(" deploy: custom marker") data.append(" active: custom marker") data.append(" master: custom marker") data.append(" release: custom marker") data.append(" staging: custom marker") data.append(" production: custom marker") data.append("") file_path = "%s/%s" % (dir_name, "pytest.ini") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("[flake8]") data.append("exclude=recordings,temp") data.append("ignore=W503") data.append("") data.append("[nosetests]") data.append("nocapture=1") data.append("logging-level=INFO") data.append("") data.append("[behave]") data.append("show_skipped=false") data.append("show_timings=false") file_path = "%s/%s" % (dir_name, "setup.cfg") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("") file_path = "%s/%s" % (dir_name, "__init__.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("*.py[cod]") data.append("*.egg") data.append("*.egg-info") data.append("dist") data.append("build") data.append(".eggs") data.append("eggs") data.append("parts") data.append("bin") data.append("var") data.append("sdist") data.append("develop-eggs") data.append(".installed.cfg") data.append("lib") data.append("lib64") data.append("__pycache__") data.append(".env") data.append(".venv") data.append("env/") data.append("venv/") data.append("ENV/") data.append("VENV/") data.append("env.bak/") data.append("venv.bak/") data.append(".sbase") data.append(".sbase*") data.append("seleniumbase_env") data.append("seleniumbase_venv") data.append("sbase_env") data.append("sbase_venv") data.append("pyvenv.cfg") data.append(".Python") data.append("include") data.append("pip-delete-this-directory.txt") data.append("pip-selfcheck.json") data.append("ipython.1.gz") data.append("nosetests.1") data.append(".noseids") data.append("pip-log.txt") data.append(".swp") data.append(".coverage") data.append(".tox") data.append("coverage.xml") data.append("nosetests.xml") data.append(".cache/*") data.append(".pytest_cache/*") data.append(".pytest_config") data.append("junit") data.append("test-results.xml") data.append(".idea") data.append(".project") data.append(".pydevproject") data.append(".vscode") data.append("chromedriver") data.append("geckodriver") data.append("msedgedriver") data.append("operadriver") data.append("uc_driver") data.append("MicrosoftWebDriver.exe") data.append("headless_ie_selenium.exe") data.append("IEDriverServer.exe") data.append("chromedriver.exe") data.append("geckodriver.exe") data.append("msedgedriver.exe") data.append("operadriver.exe") data.append("uc_driver.exe") data.append("chrome-mac.zip") data.append("chrome-linux.zip") data.append("chrome-win.zip") data.append("chrome-mac") data.append("chrome-linux") data.append("chrome-win") data.append("chrome-mac-arm64.zip") data.append("chrome-mac-x64.zip") data.append("chrome-linux64.zip") data.append("chrome-win64.zip") data.append("chrome-win32.zip") data.append("chrome-mac-arm64") data.append("chrome-mac-x64") data.append("chrome-linux64") data.append("chrome-win64") data.append("chrome-win32") data.append("chrome-headless-shell-mac-arm64.zip") data.append("chrome-headless-shell-mac-x64.zip") data.append("chrome-headless-shell-linux64.zip") data.append("chrome-headless-shell-win64.zip") data.append("chrome-headless-shell-win32.zip") data.append("chrome-headless-shell-mac-arm64") data.append("chrome-headless-shell-mac-x64") data.append("chrome-headless-shell-linux64") data.append("chrome-headless-shell-win64") data.append("chrome-headless-shell-win32") data.append("libc++.dylib") data.append("logs") data.append("latest_logs") data.append("log_archives") data.append("archived_logs") data.append("geckodriver.log") data.append("ghostdriver.log") data.append("pytestdebug.log") data.append("reports/*.xml") data.append("latest_report") data.append("report_archives") data.append("archived_reports") data.append("html_report.html") data.append("last_report.html") data.append("report.html") data.append("report.xml") data.append("dashboard.html") data.append("dashboard.json") data.append("dash_pie.json") data.append("dashboard.lock") data.append("allure_report") data.append("allure-report") data.append("allure_results") data.append("allure-results") data.append("saved_charts") data.append("saved_presentations") data.append("tours_exported") data.append("images_exported") data.append("saved_cookies") data.append("recordings") data.append("visual_baseline") data.append(".DS_Store") data.append("selenium-server-standalone.jar") data.append("proxy.zip") data.append("proxy.lock") data.append("verbose_hub_server.dat") data.append("verbose_node_server.dat") data.append("ip_of_grid_hub.dat") data.append("downloaded_files") data.append("archived_files") data.append("assets") data.append("temp") data.append("temp_*/") data.append("node_modules") file_path = "%s/%s" % (dir_name, ".gitignore") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if gha: dir_name_b = dir_name + "/" + ".github" os.mkdir(dir_name_b) dir_name_c = dir_name_b + "/" + "workflows" os.mkdir(dir_name_c) data = [] data.append("name: CI build") data.append("on:") data.append(" push:") data.append(" branches:") data.append(" pull_request:") data.append(" branches:") data.append(" workflow_dispatch:") data.append(" branches:") data.append("jobs:") data.append(" build:") data.append(" env:") data.append(' PY_COLORS: "1"') data.append(" strategy:") data.append(" fail-fast: false") data.append(" max-parallel: 15") data.append(" matrix:") data.append(" os: [ubuntu-latest]") data.append(' python-version: ["3.x"]') data.append(" runs-on: ${{ matrix.os }}") data.append(" steps:") data.append(" - uses: actions/checkout@v6") data.append(" - name: Set up Python ${{ matrix.python-version }}") data.append(" uses: actions/setup-python@v6") data.append(" with:") data.append(" python-version: ${{ matrix.python-version }}") data.append(" - name: Install dependencies") data.append(" run: |") data.append(" python -m pip install --upgrade pip") data.append(" pip install -r requirements.txt") data.append(" - name: Install Chrome") data.append(" if: matrix.os == 'ubuntu-latest'") data.append(" run: sudo apt install google-chrome-stable") data.append(" - name: Download chromedriver") data.append(" run: sbase get chromedriver") data.append(" - name: Run pytest") data.append(" run: pytest -v -s --rs --crumbs --reruns=1") data.append(" - name: Upload artifacts") data.append(" if: ${{ always() }}") data.append(" uses: actions/upload-artifact@v6") data.append(" with:") data.append(" name: seleniumbase-artifacts") data.append(" path: ./latest_logs/") data.append(" if-no-files-found: ignore") data.append("") file_path = "%s/%s" % (dir_name_c, "python-package.yml") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if basic: data = [] data.append(" %s/" % dir_name) if gha: data.append(" ├── .github") data.append(" │ └── workflows/") data.append(" │ └── python-package.yml") data.append(" ├── __init__.py") data.append(" ├── pytest.ini") data.append(" ├── requirements.txt") data.append(" └── setup.cfg") file_path = "%s/%s" % (dir_name, "outline.rst") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() os.system("sbase print %s -n" % file_path) os.remove(file_path) success = ( "\n" + c1 + '* Directory "' + dir_name + '" was created ' "with config files! *" + cr + "\n" ) print(success) return data = [] data.append("from seleniumbase import BaseCase") data.append("BaseCase.main(__name__, __file__)") data.append("") data.append("") data.append("class MyTestClass(BaseCase):") data.append(" def test_swag_labs(self):") data.append(' self.open("https://www.saucedemo.com")') data.append(' self.type("#user-name", "standard_user")') data.append(' self.type("#password", "secret_sauce\\n")') data.append(' self.assert_element("div.inventory_list")') data.append(' self.assert_text("Products", "span.title")') data.append(" self.click('button[name*=\"backpack\"]')") data.append(' self.click("#shopping_cart_container a")') data.append(' self.assert_text("Your Cart", "span.title")') data.append(' self.assert_text("Backpack", "div.cart_item")') data.append(' self.click("button#checkout")') data.append(' self.type("#first-name", "SeleniumBase")') data.append(' self.type("#last-name", "Automation")') data.append(' self.type("#postal-code", "77123")') data.append(' self.click("input#continue")') data.append(' self.assert_text("Checkout: Overview")') data.append(' self.assert_text("Backpack", "div.cart_item")') data.append(' self.click("button#finish")') data.append( ' self.assert_exact_text("Thank you for your order!", "h2")' ) data.append(" self.assert_element('img[alt=\"Pony Express\"]')") data.append(' self.js_click("a#logout_sidebar_link")') data.append(' self.assert_element("div#login_button_container")') data.append("") file_path = "%s/%s" % (dir_name, "my_first_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("from seleniumbase import BaseCase") data.append("BaseCase.main(__name__, __file__)") data.append("") data.append("") data.append("class DemoSiteTests(BaseCase):") data.append(" def test_demo_site(self):") data.append(' self.open("https://seleniumbase.io/demo_page.html")') data.append(' self.assert_title("Web Testing Page")') data.append(' self.assert_element("tbody#tbodyId")') data.append(' self.assert_text("Demo Page", "h1")') data.append(' self.type("#myTextInput", "This is Automated")') data.append(' self.type("textarea.area1", "Testing Time!\\n")') data.append(' self.type(\'[name="preText2"]\', "Typing Text!")') data.append( ' self.assert_text("This is Automated", "#myTextInput")' ) data.append( ' self.assert_text("Testing Time!\\n", "textarea.area1")' ) data.append( ' self.assert_text("Typing Text!", \'[name="preText2"]\')' ) data.append(' self.assert_text("Automation Practice", "h3")') data.append( ' self.hover_and_js_click("#myDropdown", "#dropOption2")' ) data.append(' self.assert_text("Link Two Selected", "h3")') data.append(' self.assert_text("This Text is Green", "#pText")') data.append(' self.click("#myButton")') data.append(' self.assert_text("This Text is Purple", "#pText")') data.append(" self.assert_element('svg[name=\"svgName\"]')") data.append(" self.assert_element('progress[value=\"50\"]')") data.append(' self.press_right_arrow("#myslider", times=5)') data.append(" self.assert_element('progress[value=\"100\"]')") data.append(" self.assert_element('meter[value=\"0.25\"]')") data.append( ' self.select_option_by_text("#mySelect", "Set to 75%")' ) data.append(" self.assert_element('meter[value=\"0.75\"]')") data.append(' self.assert_false(self.is_element_visible("img"))') data.append(' self.switch_to_frame("#myFrame1")') data.append(' self.assert_true(self.is_element_visible("img"))') data.append(" self.switch_to_default_content()") data.append( ' self.assert_false(self.is_text_visible("iFrame Text"))' ) data.append(' self.switch_to_frame("#myFrame2")') data.append( ' self.assert_true(self.is_text_visible("iFrame Text"))' ) data.append(" self.switch_to_default_content()") data.append(' self.assert_false(self.is_selected("#radioButton2"))') data.append(' self.click("#radioButton2")') data.append(' self.assert_true(self.is_selected("#radioButton2"))') data.append(' self.assert_false(self.is_selected("#checkBox1"))') data.append(' self.click("#checkBox1")') data.append(' self.assert_true(self.is_selected("#checkBox1"))') data.append(' self.assert_false(self.is_selected("#checkBox2"))') data.append(' self.assert_false(self.is_selected("#checkBox3"))') data.append(' self.assert_false(self.is_selected("#checkBox4"))') data.append(' self.click_visible_elements("input.checkBoxClassB")') data.append(' self.assert_true(self.is_selected("#checkBox2"))') data.append(' self.assert_true(self.is_selected("#checkBox3"))') data.append(' self.assert_true(self.is_selected("#checkBox4"))') data.append(' self.assert_false(self.is_element_visible(".fBox"))') data.append(' self.switch_to_frame("#myFrame3")') data.append(' self.assert_true(self.is_element_visible(".fBox"))') data.append(' self.assert_false(self.is_selected(".fBox"))') data.append(' self.click(".fBox")') data.append(' self.assert_true(self.is_selected(".fBox"))') data.append(" self.switch_to_default_content()") data.append( ' self.assert_element_not_visible("div#drop2 img#logo")' ) data.append(' self.drag_and_drop("img#logo", "div#drop2")') data.append(' self.assert_element("div#drop2 img#logo")') data.append(' self.assert_link_text("seleniumbase.com")') data.append(' self.assert_link_text("SeleniumBase on GitHub")') data.append(' self.assert_link_text("seleniumbase.io")') data.append(' self.click_link("SeleniumBase Demo Page")') data.append(' self.assert_exact_text("Demo Page", "h1")') data.append(' self.highlight("h2")') data.append(' if self.headed:') data.append(" self.activate_demo_mode()") data.append(' self.type("input", "Have a Nice Day!")') data.append(' self.assert_text("SeleniumBase", "h2")') data.append("") file_path = "%s/%s" % (dir_name, "test_demo_site.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("from parameterized import parameterized") data.append("from seleniumbase import BaseCase") data.append("BaseCase.main(__name__, __file__)") data.append("") data.append("") data.append("class SearchTests(BaseCase):") data.append(" @parameterized.expand(") data.append(" [") data.append( " " '["SeleniumBase Commander", "Commander", "GUI / Commander"],' ) data.append( ' ["SeleniumBase Recorder", "Recorder", "Recorder Mode"],' ) data.append( ' ["SeleniumBase Syntax", "Syntax", "Syntax Formats"],' ) data.append(" ]") data.append(" )") data.append( " def test_parameterized_search(" "self, search_term, keyword, title_text):" ) data.append( ' self.open("https://seleniumbase.io/help_docs/how_it_works/")' ) data.append( ' self.type(\'[aria-label="Search"]\', search_term)' ) data.append(' self.click(\'mark:contains("%s")\' % keyword)') data.append(' self.assert_title_contains(title_text)') data.append(' self.save_screenshot_to_logs()') data.append("") file_path = "%s/%s" % (dir_name, "parameterized_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() dir_name_2 = dir_name + "/" + "boilerplates" os.mkdir(dir_name_2) data = [] data.append("") file_path = "%s/%s" % (dir_name_2, "__init__.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("from seleniumbase import BaseCase") data.append("BaseCase.main(__name__, __file__)") data.append("") data.append("") data.append("class BaseTestCase(BaseCase):") data.append(" def setUp(self):") data.append(" super().setUp()") data.append(" # <<< Run custom code AFTER the super() line >>>") data.append("") data.append(" def tearDown(self):") data.append(" self.save_teardown_screenshot()") data.append(" if self.has_exception():") data.append(" # <<< Run custom code if the test failed. >>>") data.append(" pass") data.append(" else:") data.append(" # <<< Run custom code if the test passed. >>>") data.append(" pass") data.append(" # (Wrap unreliable code in a try/except block.)") data.append(" # <<< Run custom code BEFORE the super() line >>>") data.append(" super().tearDown()") data.append("") data.append(" def login(self):") data.append(" # <<< Placeholder. Add your code here. >>>") data.append(" pass") data.append("") data.append(" def example_method(self):") data.append(" # <<< Placeholder. Add your code here. >>>") data.append(" pass") data.append("") file_path = "%s/%s" % (dir_name_2, "base_test_case.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("class Page(object):") data.append(' html = "html"') data.append("") file_path = "%s/%s" % (dir_name_2, "page_objects.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("from .base_test_case import BaseTestCase") data.append("from .page_objects import Page") data.append("") data.append("") data.append("class MyTestClass(BaseTestCase):") data.append(" def test_boilerplate(self):") data.append(" self.login()") data.append(" self.example_method()") data.append(" self.assert_element(Page.html)") data.append("") file_path = "%s/%s" % (dir_name_2, "boilerplate_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("from seleniumbase import BaseCase") data.append("BaseCase.main(__name__, __file__)") data.append("") data.append("") data.append("class DataPage:") data.append(" def go_to_data_url(self, sb):") data.append(' sb.open("data:text/html,

      Hello!

      ")') data.append("") data.append(" def add_input_text(self, sb, text):") data.append(' sb.type("input", text)') data.append("") data.append("") data.append("class ObjTests(BaseCase):") data.append(" def test_data_url_page(self):") data.append(" DataPage().go_to_data_url(self)") data.append(' self.assert_text("Hello!", "p")') data.append(' DataPage().add_input_text(self, "Goodbye!")') data.append("") file_path = "%s/%s" % (dir_name_2, "classic_obj_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("class DataPage:") data.append(" def go_to_data_url(self, sb):") data.append(' sb.open("data:text/html,

      Hello!

      ")') data.append("") data.append(" def add_input_text(self, sb, text):") data.append(' sb.type("input", text)') data.append("") data.append("") data.append("class ObjTests:") data.append(" def test_data_url_page(self, sb):") data.append(" DataPage().go_to_data_url(sb)") data.append(' sb.assert_text("Hello!", "p")') data.append(' DataPage().add_input_text(sb, "Goodbye!")') data.append("") file_path = "%s/%s" % (dir_name_2, "sb_fixture_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() dir_name_3 = dir_name_2 + "/" + "samples" os.mkdir(dir_name_3) data = [] data.append("") file_path = "%s/%s" % (dir_name_3, "__init__.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("from seleniumbase import BaseCase") data.append("from .google_objects import HomePage, ResultsPage") data.append('BaseCase.main(__name__, __file__, "--uc")') data.append("") data.append("") data.append("class GoogleTests(BaseCase):") data.append(" def test_google_dot_com(self):") data.append(" if self.headless:") data.append(' self.skip("Skipping test in headless mode.")') data.append(" if not self.undetectable:") data.append(" self.get_new_driver(undetectable=True)") data.append(' self.open("https://google.com/ncr")') data.append(' self.assert_title_contains("Google")') data.append(" self.save_screenshot_to_logs()") data.append(' self.type(HomePage.search_box, "github.com")') data.append(" self.assert_element(HomePage.search_button)") data.append(" self.assert_element(HomePage.feeling_lucky_button)") data.append(" self.click(HomePage.search_button)") data.append( ' self.assert_text("github.com", ResultsPage.search_results)' ) data.append("") file_path = "%s/%s" % (dir_name_3, "google_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append("class HomePage(object):") data.append(" dialog_box = '[role=\"dialog\"] div'") data.append(" search_box = '[title=\"Search\"]'") data.append(" search_button = 'input[value=\"Google Search\"]'") data.append( ' feeling_lucky_button = """input[value="I\'m Feeling Lucky"]"""' ) data.append("") data.append("") data.append("class ResultsPage(object):") data.append(' search_results = "div#center_col"') data.append("") file_path = "%s/%s" % (dir_name_3, "google_objects.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append('""" Classic Page Object Model with BaseCase inheritance """') data.append("") data.append("from seleniumbase import BaseCase") data.append("BaseCase.main(__name__, __file__)") data.append("") data.append("") data.append("class LoginPage:") data.append(" def login_to_swag_labs(self, sb, username):") data.append(' sb.open("https://www.saucedemo.com/")') data.append(' sb.type("#user-name", username)') data.append(' sb.type("#password", "secret_sauce")') data.append(" sb.click('input[type=\"submit\"]')") data.append("") data.append("") data.append("class MyTests(BaseCase):") data.append(" def test_swag_labs_login(self):") data.append( ' LoginPage().login_to_swag_labs(self, "standard_user")' ) data.append(' self.assert_element("#inventory_container")') data.append( " self.assert_element('div:contains(\"Sauce Labs Backpack\")')" ) data.append(' self.js_click("a#logout_sidebar_link")') data.append(' self.assert_element("div#login_button_container")') data.append("") file_path = "%s/%s" % (dir_name_3, "swag_labs_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append('""" Classic Page Object Model with the "sb" fixture """') data.append("") data.append("") data.append("class LoginPage:") data.append(" def login_to_swag_labs(self, sb, username):") data.append(' sb.open("https://www.saucedemo.com/")') data.append(' sb.type("#user-name", username)') data.append(' sb.type("#password", "secret_sauce")') data.append(" sb.click('input[type=\"submit\"]')") data.append("") data.append("") data.append("class MyTests:") data.append(" def test_swag_labs_login(self, sb):") data.append(' LoginPage().login_to_swag_labs(sb, "standard_user")') data.append(' sb.assert_element("div.inventory_list")') data.append( " sb.assert_element('div:contains(\"Sauce Labs Backpack\")')" ) data.append(' sb.js_click("a#logout_sidebar_link")') data.append(' sb.assert_element("div#login_button_container")') data.append("") file_path = "%s/%s" % (dir_name_3, "sb_swag_test.py") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() data = [] data.append(" %s/" % dir_name) if gha: data.append(" ├── .github") data.append(" │ └── workflows/") data.append(" │ └── python-package.yml") data.append(" ├── __init__.py") data.append(" ├── my_first_test.py") data.append(" ├── parameterized_test.py") data.append(" ├── pytest.ini") data.append(" ├── requirements.txt") data.append(" ├── setup.cfg") data.append(" ├── test_demo_site.py") data.append(" └── boilerplates/") data.append(" ├── __init__.py") data.append(" ├── base_test_case.py") data.append(" ├── boilerplate_test.py") data.append(" ├── classic_obj_test.py") data.append(" ├── page_objects.py") data.append(" ├── sb_fixture_test.py") data.append(" └── samples/") data.append(" ├── __init__.py") data.append(" ├── google_objects.py") data.append(" ├── google_test.py") data.append(" ├── sb_swag_test.py") data.append(" └── swag_labs_test.py") file_path = "%s/%s" % (dir_name, "outline.rst") file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if " " not in file_path: os.system("sbase print %s -n" % file_path) elif '"' not in file_path: os.system('sbase print "%s" -n' % file_path) else: os.system("sbase print '%s' -n" % file_path) os.remove(file_path) success = ( "\n" + c1 + '* Directory "' + dir_name + '" was created ' "with config files and sample tests! *" + cr + "\n" ) print(success) if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_mkfile.py ================================================ """ Creates a new SeleniumBase test file with boilerplate code. Usage: seleniumbase mkfile [FILE.py] [OPTIONS] or sbase mkfile [FILE.py] [OPTIONS] Example: sbase mkfile new_test.py Options: --uc (UC Mode boilerplate using SB context manager) -b / --basic (Basic boilerplate / single-line test) -r / --rec (Adds Pdb+ breakpoint for Recorder Mode) --url=URL (Makes the test start on a specific page) Language Options: --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish Syntax Formats: --bc / --basecase (BaseCase class inheritance) --pf / --pytest-fixture (sb pytest fixture) --cf / --class-fixture (class + sb pytest fixture) --cm / --context-manager (SB context manager) --dc / --driver-context (DriverContext manager) --dm / --driver-manager (Driver manager) Output: Creates a new SBase test file with boilerplate code. If the file already exists, an error is raised. By default, uses English with BaseCase inheritance, and creates a boilerplate with common SeleniumBase methods: "open", "type", "click", "assert_element", and "assert_text". If using the basic boilerplate option, only the "open" method is included. Only the BaseCase format supports Languages or Recorder Mode. UC Mode automatically uses English with SB() format. """ import colorama import os import sys def invalid_run_command(msg=None): exp = " ** mkfile **\n\n" exp += " Usage:\n" exp += " seleniumbase mkfile [FILE.py] [OPTIONS]\n" exp += " OR sbase mkfile [FILE.py] [OPTIONS]\n" exp += " Example:\n" exp += " sbase mkfile new_test.py\n" exp += " Options:\n" exp += " --uc (UC Mode boilerplate using SB context manager)\n" exp += " -b / --basic (Basic boilerplate / single-line test)\n" exp += " -r / --rec (Adds Pdb+ breakpoint for Recorder Mode)\n" exp += " --url=URL (Makes the test start on a specific page)\n" exp += " Language Options:\n" exp += " --en / --English | --zh / --Chinese\n" exp += " --nl / --Dutch | --fr / --French\n" exp += " --it / --Italian | --ja / --Japanese\n" exp += " --ko / --Korean | --pt / --Portuguese\n" exp += " --ru / --Russian | --es / --Spanish\n" exp += " Syntax Formats:\n" exp += " --bc / --basecase (BaseCase class inheritance)\n" exp += " --pf / --pytest-fixture (sb pytest fixture)\n" exp += " --cf / --class-fixture (class + sb pytest fixture)\n" exp += " --cm / --context-manager (SB context manager)\n" exp += " --dc / --driver-context (DriverContext manager)\n" exp += " --dm / --driver-manager (Driver manager)\n" exp += " Output:\n" exp += " Creates a new SBase test file with boilerplate code.\n" exp += " If the file already exists, an error is raised.\n" exp += " By default, uses English with BaseCase inheritance,\n" exp += " and creates a boilerplate with common SeleniumBase\n" exp += ' methods: "open", "type", "click", "assert_element",\n' exp += ' and "assert_text". If using the basic boilerplate\n' exp += ' option, only the "open" method is included. Only the\n' exp += " BaseCase format supports Languages or Recorder Mode.\n" exp += " UC Mode automatically uses English with SB() format.\n" if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) elif msg == "help": print("\n%s" % exp) sys.exit() else: raise Exception("INVALID RUN COMMAND!\n\n%s\n%s\n" % (exp, msg)) def main(): c1 = "" c5 = "" c7 = "" cr = "" if "linux" not in sys.platform: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL basic = False use_uc = False help_me = False recorder = False error_msg = None start_page = None invalid_cmd = None syntax = "BaseCase" language = "English" command_args = sys.argv[2:] file_name = command_args[0] if file_name == "-h" or file_name == "--help": invalid_run_command("help") elif not file_name.endswith(".py"): error_msg = 'File name must end with ".py"!' elif "*" in file_name or len(str(file_name)) < 4: error_msg = "Invalid file name!" elif file_name.startswith("-"): error_msg = 'File name cannot start with "-"!' elif "/" in str(file_name) or "\\" in str(file_name): error_msg = "File must be created in the current directory!" elif os.path.exists(os.getcwd() + "/" + file_name): error_msg = 'File "%s" already exists in this directory!' % file_name if error_msg: error_msg = c5 + "ERROR: " + error_msg + cr invalid_run_command(error_msg) if len(command_args) >= 2: options = command_args[1:] for option in options: option = option.lower() if option == "-h" or option == "--help": help_me = True elif option.startswith("--url=") and len(option) > 6: from seleniumbase.fixtures import page_utils start_page = option.split("--url=")[1] if not page_utils.is_valid_url(start_page): if page_utils.is_valid_url("https://" + start_page): start_page = "https://" + start_page else: raise Exception("Invalid URL: %s" % start_page) basic = True elif option == "-b" or option == "--basic": basic = True elif option == "-r" or option == "--rec": recorder = True elif option == "--record" or option == "--recorder": recorder = True elif use_uc: # UC must use English & ContextManager formats continue elif option == "--en" or option == "--english": language = "English" elif option == "--zh" or option == "--chinese": language = "Chinese" elif option == "--nl" or option == "--dutch": language = "Dutch" elif option == "--fr" or option == "--french": language = "French" elif option == "--it" or option == "--italian": language = "Italian" elif option == "--ja" or option == "--japanese": language = "Japanese" elif option == "--ko" or option == "--korean": language = "Korean" elif option == "--pt" or option == "--portuguese": language = "Portuguese" elif option == "--ru" or option == "--russian": language = "Russian" elif option == "--es" or option == "--spanish": language = "Spanish" elif option == "--bc" or option == "--basecase": syntax = "BaseCase" elif option == "--pf" or option == "--pytest-fixture": syntax = "PytestFixture" elif option == "--cf" or option == "--class-fixture": syntax = "ClassFixture" elif option == "--cm" or option == "--context-manager": syntax = "ContextManager" elif option == "--dc" or option == "--driver-context": syntax = "DriverContext" elif option == "--dm" or option == "--driver-manager": syntax = "DriverManager" elif option == "--uc": basic = True language = "English" syntax = "ContextManager" use_uc = True else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break if help_me: invalid_run_command(invalid_cmd) dir_name = os.getcwd() file_path = "%s/%s" % (dir_name, file_name) body = "body" para1 = "html body > p" para2 = "p" hello = "Hello" goodbye = "Goodbye" class_name = "MyTestClass" if language == "Chinese": hello = "你好" goodbye = "再见" class_name = "我的测试类" elif language == "Dutch": hello = "Hallo" goodbye = "Dag" class_name = "MijnTestklasse" elif language == "French": hello = "Bonjour" goodbye = "Au revoir" class_name = "MaClasseDeTest" elif language == "Italian": hello = "Ciao" goodbye = "Addio" class_name = "MiaClasseDiTest" elif language == "Japanese": hello = "こんにちは" goodbye = "さようなら" class_name = "私のテストクラス" elif language == "Korean": hello = "여보세요" goodbye = "안녕" class_name = "테스트_클래스" elif language == "Portuguese": hello = "Olá" goodbye = "Tchau" class_name = "MinhaClasseDeTeste" elif language == "Russian": hello = "Привет" goodbye = "До свидания" class_name = "МойТестовыйКласс" elif language == "Spanish": hello = "Hola" goodbye = "Adiós" class_name = "MiClaseDePrueba" url = "" if start_page: url = start_page elif basic: url = "about:blank" elif language not in ["English", "Dutch", "French", "Italian"]: url = "data:text/html,

      %s
      " % hello else: url = "data:text/html,

      %s
      " % hello import_line = "from seleniumbase import BaseCase" main_line = "BaseCase.main(__name__, __file__)" parent_class = "BaseCase" if language != "English": from seleniumbase.translate.master_dict import MD_F import_line = MD_F.get_import_line(language) parent_class = MD_F.get_lang_parent_class(language) class_line = "class %s(%s):" % (class_name, parent_class) data = [] data.append("%s" % import_line) if not recorder: data.append(main_line) data.append("") data.append("") data.append("%s" % class_line) data.append(" def test_base(self):") if not recorder: data.append(' self.open("%s")' % url) else: data.append(" if self.recorder_ext and not self.xvfb:") data.append(" import pdb; pdb.set_trace()") if not basic and not recorder: data.append( ' self.type("input", "%s")' " # selector, text" % goodbye ) data.append(' self.click("%s") # selector' % para1) data.append(' self.assert_element("%s") # selector' % body) data.append( ' self.assert_text("%s", "%s")' " # text, selector" % (hello, para2) ) data.append("") new_data = [] if language == "English" and syntax == "BaseCase": new_data = data elif language == "English" and syntax == "PytestFixture": data = [] data.append("def test_base(sb):") data.append(' sb.open("data:text/html,

      Hello
      ")') if not basic: data.append(' sb.type("input", "Goodbye") # selector, text') data.append(' sb.click("html body > p") # selector') data.append(' sb.assert_element("body") # selector') data.append(' sb.assert_text("Hello", "p") # text, selector') data.append("") new_data = data elif language == "English" and syntax == "ClassFixture": data = [] data.append("class %s:" % class_name) data.append(" def test_base(self, sb):") data.append(' sb.open("data:text/html,

      Hello
      ")') if not basic: data.append( ' sb.type("input", "Goodbye") # selector, text' ) data.append(' sb.click("html body > p") # selector') data.append(' sb.assert_element("body") # selector') data.append( ' sb.assert_text("Hello", "p") # text, selector' ) data.append("") new_data = data elif language == "English" and syntax == "ContextManager": data = [] data.append("from seleniumbase import SB") data.append("") if use_uc: data.append('with SB(uc=True) as sb:') else: data.append('with SB(browser="chrome") as sb:') if use_uc: data.append(' url = "%s"' % url) data.append(" sb.uc_open_with_reconnect(url, 4)") data.append(" sb.uc_gui_click_captcha()") else: data.append(' sb.open("%s")' % url) if not basic: data.append(' sb.type("input", "Goodbye") # selector, text') data.append(' sb.click("html body > p") # selector') data.append(' sb.assert_element("input") # selector') data.append(' sb.assert_text("Hello", "p") # text, selector') data.append(' sb.highlight("p") # selector') data.append(" sb.sleep(0.5) # seconds") data.append("") new_data = data elif language == "English" and syntax == "DriverContext": data = [] data.append("from seleniumbase import DriverContext") data.append("") data.append('with DriverContext(browser="chrome") as driver:') data.append(' driver.get("%s")' % url) if not basic: data.append(' driver.type("input", "Goodbye") # sel, text') data.append(' driver.click("html body > p") # selector') data.append(' driver.assert_element("input") # selector') data.append(' driver.assert_text("Hello", "p") # text, sel') data.append(' driver.highlight("p") # selector') data.append(" driver.sleep(0.5) # seconds") data.append("") new_data = data elif language == "English" and syntax == "DriverManager": data = [] data.append("from seleniumbase import Driver") data.append("") data.append('driver = Driver(browser="chrome")') data.append("try:") data.append(' driver.get("%s")' % url) if not basic: data.append(' driver.type("input", "Goodbye") # sel, text') data.append(' driver.click("html body > p") # selector') data.append(' driver.assert_element("input") # selector') data.append(' driver.assert_text("Hello", "p") # text, sel') data.append(' driver.highlight("p") # selector') data.append(" driver.sleep(0.5) # seconds") data.append("finally:") data.append(" driver.quit()") data.append("") new_data = data else: from seleniumbase.translate.master_dict import MD from seleniumbase.translate.master_dict import MD_L_Codes md = MD.md lang_codes = MD_L_Codes.lang nl_code = lang_codes[language] dl_code = lang_codes["English"] for line in data: found_swap = False replace_count = line.count("self.") # Total possible replacements for key in md.keys(): original = "self." + md[key][dl_code] + "(" if original in line: replacement = "self." + md[key][nl_code] + "(" new_line = line.replace(original, replacement) found_swap = True replace_count -= 1 if replace_count == 0: break # Done making replacements else: # There might be another method to replace in the line. # Example: self.assert_true("Name" in self.get_title()) line = new_line continue if main_line in line: new_main = "%s.main(__name__, __file__)" % parent_class new_line = line.replace(main_line, new_main) found_swap = True if found_swap: if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] new_data.append(new_line) continue new_data.append(line) data = new_data file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if " " not in file_name: os.system("sbase print %s -n" % file_name) elif '"' not in file_name: os.system('sbase print "%s" -n' % file_name) else: os.system("sbase print '%s' -n" % file_name) success = ( "\n" + c1 + '* Test file: "' + file_name + '" was created! *' "" + cr + "\n" ) print(success) if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_mkpres.py ================================================ """ Creates a new SeleniumBase presentation with boilerplate code. Usage: seleniumbase mkpres [FILE.py] [LANG] or sbase mkpres [FILE.py] [LANG] Example: sbase mkpres new_presentation.py --en Language Options: --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish Output: Creates a new presentation with 3 example slides. If the file already exists, an error is raised. By default, the slides are written in English, and use "serif" theme with "slide" transition. The slides can be used as a basic boilerplate. """ import colorama import os import sys def invalid_run_command(msg=None): exp = " ** mkpres **\n\n" exp += " Usage:\n" exp += " seleniumbase mkpres [FILE.py] [LANG]\n" exp += " OR sbase mkpres [FILE.py] [LANG]\n" exp += " Example:\n" exp += " sbase mkpres new_presentation.py --en\n" exp += " Language Options:\n" exp += " --en / --English | --zh / --Chinese\n" exp += " --nl / --Dutch | --fr / --French\n" exp += " --it / --Italian | --ja / --Japanese\n" exp += " --ko / --Korean | --pt / --Portuguese\n" exp += " --ru / --Russian | --es / --Spanish\n" exp += " Output:\n" exp += " Creates a new presentation with 3 example slides.\n" exp += " If the file already exists, an error is raised.\n" exp += " By default, the slides are written in English,\n" exp += ' and use "serif" theme with "slide" transition.\n' exp += " The slides can be used as a basic boilerplate.\n" if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) elif msg == "help": print("\n%s" % exp) sys.exit() else: raise Exception("INVALID RUN COMMAND!\n\n%s\n%s\n" % (exp, msg)) def main(): c1 = "" c5 = "" c7 = "" cr = "" if "linux" not in sys.platform: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL help_me = False error_msg = None invalid_cmd = None language = "English" command_args = sys.argv[2:] file_name = command_args[0] if file_name == "-h" or file_name == "--help": invalid_run_command("help") elif not file_name.endswith(".py"): error_msg = 'File name must end with ".py"!' elif "*" in file_name or len(str(file_name)) < 4: error_msg = "Invalid file name!" elif file_name.startswith("-"): error_msg = 'File name cannot start with "-"!' elif "/" in str(file_name) or "\\" in str(file_name): error_msg = "File must be created in the current directory!" elif os.path.exists(os.getcwd() + "/" + file_name): error_msg = 'File "%s" already exists in this directory!' % file_name if error_msg: error_msg = c5 + "ERROR: " + error_msg + cr invalid_run_command(error_msg) if len(command_args) >= 2: options = command_args[1:] for option in options: option = option.lower() if option == "-h" or option == "--help": help_me = True elif option == "--en" or option == "--english": language = "English" elif option == "--zh" or option == "--chinese": language = "Chinese" elif option == "--nl" or option == "--dutch": language = "Dutch" elif option == "--fr" or option == "--french": language = "French" elif option == "--it" or option == "--italian": language = "Italian" elif option == "--ja" or option == "--japanese": language = "Japanese" elif option == "--ko" or option == "--korean": language = "Korean" elif option == "--pt" or option == "--portuguese": language = "Portuguese" elif option == "--ru" or option == "--russian": language = "Russian" elif option == "--es" or option == "--spanish": language = "Spanish" else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break if help_me: invalid_run_command(invalid_cmd) dir_name = os.getcwd() file_path = "%s/%s" % (dir_name, file_name) html_name = file_name.replace(".py", ".html") hello = "Hello" press_right_arrow = "Press Right Arrow" add_text = "Add Text" goodbye = "Goodbye" class_name = "MyTestClass" if language == "Chinese": hello = "你好" press_right_arrow = "按向右箭头" add_text = "添加文本" goodbye = "再见" class_name = "我的测试类" elif language == "Dutch": hello = "Hallo" press_right_arrow = "Druk op pijl rechts" add_text = "Tekst Toevoegen" goodbye = "Dag" class_name = "MijnTestklasse" elif language == "French": hello = "Bonjour" press_right_arrow = "Appuyer sur flèche droite" add_text = "Ajouter Texte" goodbye = "Au revoir" class_name = "MaClasseDeTest" elif language == "Italian": hello = "Ciao" press_right_arrow = "Premere la freccia destra" add_text = "Aggiungi Testo" goodbye = "Addio" class_name = "MiaClasseDiTest" elif language == "Japanese": hello = "こんにちは" press_right_arrow = "右矢印を押します" add_text = "テキストを追加" goodbye = "さようなら" class_name = "私のテストクラス" elif language == "Korean": hello = "여보세요" press_right_arrow = "오른쪽 화살표를 누르십시오" add_text = "텍스트를 추가" goodbye = "안녕" class_name = "테스트_클래스" elif language == "Portuguese": hello = "Olá" press_right_arrow = "Pressione a seta direita" add_text = "Adicionar Texto" goodbye = "Tchau" class_name = "MinhaClasseDeTeste" elif language == "Russian": hello = "Привет" press_right_arrow = "Нажмите стрелку вправо" add_text = "Добавить Текст" goodbye = "До свидания" class_name = "МойТестовыйКласс" elif language == "Spanish": hello = "Hola" press_right_arrow = "Presione la flecha derecha" add_text = "Agregar Texto" goodbye = "Adiós" class_name = "MiClaseDePrueba" import_line = "from seleniumbase import BaseCase" main_line = "BaseCase.main(__name__, __file__)" parent_class = "BaseCase" if language != "English": from seleniumbase.translate.master_dict import MD_F import_line = MD_F.get_import_line(language) parent_class = MD_F.get_lang_parent_class(language) class_line = "class %s(%s):" % (class_name, parent_class) settings = 'theme="serif", transition="slide"' img_src_1 = 'src="https://seleniumbase.github.io/cdn/gif/chart_pres.gif"' img_src_2 = 'src="https://seleniumbase.github.io/cdn/img/sb_logo_10.png"' hello_page = ( "\n '

      %s



      '" "\n '

      %s

      '" "" % (hello, press_right_arrow) ) add_text_page = ( "\n '

      * %s *

      '" "\n ''" "" % (add_text, img_src_1) ) goodbye_page = ( "\n '

      %s

      '" "\n ''" "" % (goodbye, img_src_2) ) data = [] data.append("%s" % import_line) data.append("%s" % main_line) data.append("") data.append("") data.append("%s" % class_line) data.append(" def test_presentation(self):") data.append(" self.create_presentation(%s)" % settings) data.append(" self.add_slide(%s)" % hello_page) data.append(" self.add_slide(%s)" % add_text_page) data.append(" self.add_slide(%s)" % goodbye_page) data.append(' self.begin_presentation(filename="%s")' % html_name) data.append("") new_data = [] if language == "English": new_data = data else: from seleniumbase.translate.master_dict import MD from seleniumbase.translate.master_dict import MD_L_Codes md = MD.md lang_codes = MD_L_Codes.lang nl_code = lang_codes[language] dl_code = lang_codes["English"] for line in data: found_swap = False replace_count = line.count("self.") # Total possible replacements for key in md.keys(): original = "self." + md[key][dl_code] + "(" if original in line: replacement = "self." + md[key][nl_code] + "(" new_line = line.replace(original, replacement) found_swap = True replace_count -= 1 if replace_count == 0: break # Done making replacements else: # There might be another method to replace in the line. # Example: self.assert_true("Name" in self.get_title()) line = new_line continue if main_line in line: new_main = "%s.main(__name__, __file__)" % parent_class new_line = line.replace(main_line, new_main) found_swap = True if found_swap: if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] new_data.append(new_line) continue new_data.append(line) data = new_data file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if " " not in file_name: os.system("sbase print %s -n" % file_name) elif '"' not in file_name: os.system('sbase print "%s" -n' % file_name) else: os.system("sbase print '%s' -n" % file_name) success = ( "\n" + c1 + '* Presentation: "' + file_name + '" was created! *' "" + cr + "\n" ) print(success) if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_mkrec.py ================================================ """ ** mkrec / record / codegen ** Creates a new SeleniumBase test file using the Recorder. Usage: seleniumbase mkrec [FILE.py] [OPTIONS] sbase mkrec [FILE.py] [OPTIONS] seleniumbase record [FILE.py] [OPTIONS] sbase record [FILE.py] [OPTIONS] seleniumbase codegen [FILE.py] [OPTIONS] sbase codegen [FILE.py] [OPTIONS] Examples: sbase mkrec new_test.py sbase mkrec new_test.py --url=seleniumbase.io sbase codegen new_test.py sbase codegen new_test.py --url=wikipedia.org Options: --url=URL (Sets the initial start page URL.) --edge (Use Edge browser instead of Chrome.) --gui / --headed (Use headed mode on Linux.) --uc / --undetected (Use undetectable mode.) --overwrite (Overwrite file when it exists.) --behave (Also output Behave/Gherkin files.) Output: Creates a new SeleniumBase test using the Recorder. If the filename already exists, an error is raised. """ import colorama import shutil import os import sys def invalid_run_command(msg=None): exp = " ** mkrec / record / codegen **\n\n" exp += " Usage:\n" exp += " seleniumbase mkrec [FILE.py]\n" exp += " OR: sbase mkrec [FILE.py]\n" exp += " Examples:\n" exp += " sbase mkrec new_test.py\n" exp += " sbase mkrec new_test.py --url=wikipedia.org\n" exp += " Options:\n" exp += " --url=URL (Sets the initial start page URL.)\n" exp += " --edge (Use Edge browser instead of Chrome.)\n" exp += " --gui / --headed (Use headed mode on Linux.)\n" exp += " --uc / --undetected (Use undetectable mode.)\n" exp += " --overwrite (Overwrite file when it exists.)\n" exp += " --behave (Also output Behave/Gherkin files.)\n" exp += " Output:\n" exp += " Creates a new SeleniumBase test using the Recorder.\n" exp += " If the filename already exists, an error is raised.\n" if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) elif msg == "help": print("\n%s" % exp) sys.exit() else: raise Exception("INVALID RUN COMMAND!\n\n%s\n%s\n" % (exp, msg)) def set_colors(use_colors): c0 = "" c1 = "" c2 = "" c5 = "" c7 = "" cr = "" if use_colors: c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c1 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c2 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTYELLOW_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL return c0, c1, c2, c5, c7, cr def main(): help_me = False error_msg = None invalid_cmd = None use_edge = False use_opera = False use_brave = False use_comet = False use_atlas = False use_chromium = False use_uc = False esc_end = False start_page = None next_is_url = False use_colors = True force_gui = False rec_behave = False sys_executable = sys.executable if " " in sys_executable: sys_executable = "python" if "linux" in sys.platform: use_colors = False c0, c1, c2, c5, c7, cr = set_colors(use_colors) command_args = sys.argv[2:] file_name = command_args[0] if file_name == "-h" or file_name == "--help": invalid_run_command("help") elif not file_name.endswith(".py"): error_msg = 'File name must end with ".py"!' elif "*" in file_name or len(str(file_name)) < 4: error_msg = "Invalid file name!" elif file_name.startswith("-"): error_msg = 'File name cannot start with "-"!' elif "/" in str(file_name) or "\\" in str(file_name): error_msg = "File must be created in the current directory!" elif file_name == "abc.py": error_msg = '"abc.py" is a reserved Python module! Use another name!' if error_msg: error_msg = c5 + "ERROR: " + error_msg + cr invalid_run_command(error_msg) dir_name = os.getcwd() file_path = os.path.join(dir_name, file_name) if ( "--overwrite" in " ".join(command_args).lower() and os.path.exists(file_path) ): os.remove(file_path) if os.path.exists(file_path): error_msg = 'File "%s" already exists in this directory!' % file_name error_msg = c5 + "ERROR: " + error_msg + cr invalid_run_command(error_msg) if len(command_args) >= 2: options = command_args[1:] for option in options: if option.lower() == "-h" or option.lower() == "--help": help_me = True elif option.lower() == "--edge": use_edge = True elif option.lower() == "--opera": use_opera = True elif option.lower() == "--brave": use_brave = True elif option.lower() == "--comet": use_comet = True elif option.lower() == "--atlas": use_atlas = True elif option.lower() == "--use-chromium": use_chromium = True elif option.lower() == "--ee": esc_end = True elif option.lower() in ("--gui", "--headed"): if "linux" in sys.platform: force_gui = True elif option.lower() in ( "--uc", "--cdp", "--undetected", "--undetectable" ): use_uc = True elif option.lower() in ("--rec-behave", "--behave", "--gherkin"): rec_behave = True elif option.lower().startswith("--url="): start_page = option[len("--url="):] elif option.lower() == "--url": next_is_url = True elif next_is_url: start_page = option next_is_url = False elif option.lower() == "--overwrite": pass # Already handled if file existed else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break if help_me: invalid_run_command(invalid_cmd) data = [] data.append("from seleniumbase import BaseCase") data.append("") data.append("") data.append("class RecorderTest(BaseCase):") data.append(" def test_recording(self):") if use_uc: data.append(" if self.undetectable:") if ( start_page and ( start_page.startswith("http:") or start_page.startswith("https:") or start_page.startswith("file:") ) ): used_sp = start_page if '"' not in start_page: used_sp = '"%s"' % start_page elif "'" not in start_page: used_sp = "'%s'" % start_page data.append( " self.activate_cdp_mode(\n" " %s,\n" " recorder=True,\n" " )" % used_sp ) else: data.append(" self.disconnect()") data.append(" if self.recorder_ext:") data.append(" # When done recording actions,") data.append(' # type "c", and press [Enter].') data.append(" import pdb; pdb.set_trace()") data.append("") if esc_end: msg = ">>> Use [SHIFT + ESC] in the browser to end recording!" d2 = [] d2.append("from seleniumbase import BaseCase") d2.append("") d2.append("") d2.append("class RecorderTest(BaseCase):") d2.append(" def test_recording(self):") d2.append(" if self.recorder_ext:") d2.append(" print(") d2.append(' "\\n\\n%s\\n"' % msg) d2.append(" )") d2.append(' script = self._get_rec_shift_esc_script()') d2.append(' esc = "return document.sb_esc_end;"') d2.append(" start_time = self.time()") d2.append(" last_handles_num = self._get_num_handles()") d2.append(" for i in range(1200):") d2.append(" try:") d2.append(" self.execute_script(script)") d2.append(" handles_num = self._get_num_handles()") d2.append(" if handles_num < 1:") d2.append(" return") d2.append(" elif handles_num != last_handles_num:") d2.append(" self.switch_to_window(-1)") d2.append(" last_handles_num = handles_num") d2.append(' if self.execute_script(esc) == "yes":') d2.append(" return") d2.append(" elif self.time() - start_time > 600:") d2.append(" return") d2.append(" self.sleep(0.5)") d2.append(" except Exception:") d2.append(" return") d2.append("") data = d2 file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() success = ( "\n" + c0 + "* RECORDING initialized:" + cr + " " "" + c1 + file_name + "" + cr + "\n" ) print(success) run_cmd = None if ( not start_page or ( use_uc and ( start_page.startswith("http:") or start_page.startswith("https:") or start_page.startswith("file:") ) and not esc_end ) ): run_cmd = "%s -m pytest %s --rec -q -s" % (sys_executable, file_name) else: run_cmd = "%s -m pytest %s --rec -q -s --url=%s" % ( sys_executable, file_name, start_page ) if '"' not in start_page: run_cmd = '%s -m pytest %s --rec -q -s --url="%s"' % ( sys_executable, file_name, start_page ) elif "'" not in start_page: run_cmd = "%s -m pytest %s --rec -q -s --url='%s'" % ( sys_executable, file_name, start_page ) if use_edge: run_cmd += " --edge" elif use_opera: run_cmd += " --opera" elif use_brave: run_cmd += " --brave" elif use_comet: run_cmd += " --comet" elif use_atlas: run_cmd += " --atlas" elif use_chromium: run_cmd += " --use-chromium" if force_gui: run_cmd += " --gui" if use_uc: run_cmd += " --uc" if rec_behave: run_cmd += " --rec-behave" print(run_cmd) os.system(run_cmd) if os.path.exists(file_path): os.remove(file_path) recorded_filename = file_name[:-3] + "_rec.py" recordings_dir = os.path.join(dir_name, "recordings") recorded_file = os.path.join(recordings_dir, recorded_filename) prefix = "%s -m " % sys_executable if " " not in recorded_file: os.system("%sseleniumbase print %s -n" % (prefix, recorded_file)) elif '"' not in recorded_file: os.system('%sseleniumbase print "%s" -n' % (prefix, recorded_file)) else: os.system("%sseleniumbase print '%s' -n" % (prefix, recorded_file)) shutil.copy(recorded_file, file_path) success = ( "\n" + c2 + "***" + cr + " RECORDING COPIED to: " "" + c1 + file_name + cr + "\n" ) print(success) if rec_behave: recorded_filename = file_name[:-3] + "_rec.feature" recordings_dir = os.path.join(dir_name, "recordings") features_dir = os.path.join(recordings_dir, "features") recorded_file = os.path.join(features_dir, recorded_filename) if " " not in recorded_file: os.system("%sseleniumbase print %s -n" % (prefix, recorded_file)) elif '"' not in recorded_file: os.system('%sseleniumbase print "%s" -n' % (prefix, recorded_file)) else: os.system("%sseleniumbase print '%s' -n" % (prefix, recorded_file)) success = ( "\n" + c2 + "***" + cr + " BEHAVE RECORDING at: " "" + c1 + os.path.relpath(recorded_file) + cr + "\n" ) print(success) if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_objectify.py ================================================ """ Converts a SeleniumBase Python file into one that uses the Page Object Pattern. Usage: seleniumbase objectify [SELENIUMBASE_PYTHON_FILE].py Output: A modified version of the file where the selectors have been replaced with variable names defined in "page_objects.py", supporting the Page Object Pattern. """ import os import re import sys PAGE_OBJECTS_FILE = "page_objects.py" # Don't change this. It's hard-coded. p_o_import = "from .page_objects import " if not os.path.exists("__init__.py"): p_o_import = "from page_objects import " def invalid_run_command(shell_command): if shell_command == "objectify": invalid_objectify_run_command() elif shell_command == "inject-objects": invalid_inject_objects_run_command() elif shell_command == "extract-objects": invalid_extract_objects_run_command() elif shell_command == "revert-objects": invalid_revert_objects_run_command() else: invalid_objectify_run_command() def invalid_objectify_run_command(): exp = " ** objectify **\n\n" exp += " Usage:\n" exp += " seleniumbase objectify [SELENIUMBASE_PYTHON_FILE]\n" exp += " Options:\n" exp += " -c, --comments (Add object selectors to the comments.)\n" exp += " (Default: No added comments.)\n" exp += " Output:\n" exp += " Converts a SeleniumBase Python file into one that uses\n" exp += " the Page Object Pattern by converting page selectors\n" exp += ' into objects stored in a "page_objects.py" file that is\n' exp += " autogenerated and stored in the same folder as tests.\n" exp += ' (seleniumbase "objectify" has the same outcome as\n' exp += ' combining "extract-objects" with "inject-objects")\n' raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) def invalid_inject_objects_run_command(): exp = " ** inject-objects **\n\n" exp += " Usage:\n" exp += " seleniumbase inject-objects [SELENIUMBASE_PYTHON_FILE]\n" exp += " Options:\n" exp += " -c, --comments (Add object selectors to the comments.)\n" exp += " (Default: No added comments.)\n" exp += " Output:\n" exp += ' Takes the page objects found in the "page_objects.py"\n' exp += " file and uses those to replace matching selectors in\n" exp += " the selected seleniumbase Python file.\n" raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) def invalid_extract_objects_run_command(): exp = " ** extract-objects **\n\n" exp += " Usage:\n" exp += " seleniumbase extract-objects [SELENIUMBASE_PYTHON_FILE]\n" exp += " Output:\n" exp += " Creates page objects based on selectors found in a\n" exp += " seleniumbase Python file and saves those objects to the\n" exp += ' "page_objects.py" file in the same folder as the tests.\n' raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) def invalid_revert_objects_run_command(): exp = " ** revert-objects **\n\n" exp += " Usage:\n" exp += " seleniumbase revert-objects [SELENIUMBASE_PYTHON_FILE]\n" exp += " Options:\n" exp += " -c, --comments (Keep existing comments for the lines.)\n" exp += " (Default: No comments are kept.)\n" exp += " Output:\n" exp += ' Reverts the changes made by "seleniumbase objectify" or\n' exp += ' "seleniumbase inject-objects" when run against a\n' exp += " seleniumbase Python file. Objects will get replaced by\n" exp += ' selectors stored in the "page_objects.py" file.\n' raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) def remove_extra_slashes(selector): if selector.count('\\"') > 0: if selector.count('\\"') == selector.count('"'): selector = selector.replace('\\"', '"') elif selector.count('\\"') == selector[1:-1].count('"') and ( "'" not in selector[1:-1] ): selector = "'" + selector[1:-1].replace('\\"', '"') + "'" else: pass if selector.count("\\'") > 0: if selector.count("\\'") == selector.count("'"): selector = selector.replace("\\'", "'") elif selector.count("\\'") == selector[1:-1].count("'") and ( '"' not in selector[1:-1] ): selector = '"' + selector[1:-1].replace("\\'", "'") + '"' else: pass return selector def create_objects_file(selector_list_dict=None): data = [] if selector_list_dict: data.append("# PAGE OBJECTS FILE >>> (autogenerated)") data.append("") for key in selector_list_dict.keys(): if key == "None": if len(selector_list_dict["None"]) > 0: for pair in selector_list_dict["None"]: data.append("%s = %s" % (pair[0], pair[1])) data.append("") else: pass else: data.append("") data.append("class %s(object):" % key) for pair in selector_list_dict[key]: data.append(" %s = %s" % (pair[0], pair[1])) data.append("") else: data.append("") data.append("class Page(object):") data.append(' html = "html"') data.append("") file_path = PAGE_OBJECTS_FILE file = open(file_path, mode="w+", encoding="utf-8") file.writelines("\r\n".join(data)) file.close() if not selector_list_dict: print('\n>>> ["%s"] was created!' % file_path) else: print('\n>>> ["%s"] was updated!' % file_path) def scan_objects_file(): if not os.path.exists(PAGE_OBJECTS_FILE): create_objects_file() page_selectors = {} with open(PAGE_OBJECTS_FILE, mode="r", encoding="utf-8") as f: all_code = f.read() var_names = [] selectors = [] current_class = "None" selector_list_dict = {} # Key = class name / Values are name-value tuples selector_list_dict[current_class] = [] selector_list_dict["Page"] = [] code_lines = all_code.split("\n") for line in code_lines: line = line.rstrip() # Handle: class CLASSNAME(object): OR class CLASSNAME(): data = re.match(r"""^(\s*)class\s+([\S]+)\((object|)\):\s*$""", line) if data: whitespace = data.group(1) name = "%s" % data.group(2) current_class = name selector_list_dict[current_class] = [] page_selectors["class %s" % name] = "." # Handle: SELECTOR_NAME = "SELECTOR" data = re.match(r"""^(\s*)(\S+)\s*=\s*([\S\s]+)\s*$""", line) if data: whitespace = data.group(1) name = data.group(2) selector = data.group(3) selector = remove_extra_slashes(selector) page_selectors[name] = selector var_names.append(name) selectors.append(selector) if whitespace == "": current_class = "None" if (len(selector) > 2 and selector[0] == "'") and ( selector[-1] == "'" and '"' not in selector[1:-1] ): selector = '"' + selector[1:-1] + '"' if "\\[" in selector or "\\]" in selector or "\\." in selector: if selector[0] != "r": selector = "r" + selector selector_list_dict[current_class].append((name, selector)) if "class Page" not in page_selectors.keys(): page_selectors["class Page"] = "." if len(selector_list_dict["Page"]) == 0: selector_list_dict["Page"].append(("html", '"html"')) return var_names, selectors, selector_list_dict def optimize_selector(selector): if (len(selector) > 2 and selector[0] == "'") and ( selector[-1] == "'" and '"' not in selector[1:-1] ): selector = '"' + selector[1:-1] + '"' if "\\[" in selector or "\\]" in selector or "\\." in selector: if selector[0] != "r": selector = "r" + selector return selector def get_next_var_name(existing_names): base_name = "css_" for i in range(1, 99999): new_name = "%s%s" % (base_name, str(i)) if new_name not in existing_names: return new_name raise Exception("Out of range! (Selector name generation)") def process_test_file( code_lines, selector_dict=None, object_dict=None, add_comments=False ): seleniumbase_lines = [] page_selectors = [] changed = [] # The classes of page_objects.py to add to the test import for line in code_lines: line = line.rstrip() # Keep lines with "%s" in them as they were if r"%s" in line: seleniumbase_lines.append(line) continue # Handle self.drag_and_drop(SELECTOR, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.drag_and_drop""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.drag_and_drop""" r"""\(([\S]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector1 = "%s" % data.group(2) selector1 = remove_extra_slashes(selector1) page_selectors.append(selector1) selector2 = "%s" % data.group(3) selector2 = remove_extra_slashes(selector2) page_selectors.append(selector2) comments = data.group(4) command = """%sself.drag_and_drop(%s, %s)%s""" % ( whitespace, selector1, selector2, comments, ) if selector_dict: if add_comments: comments = " # %s, %s" % (selector1, selector2) selector1 = optimize_selector(selector1) selector2 = optimize_selector(selector2) if selector1 in selector_dict.keys() and ( selector2 in selector_dict.keys() ): selector_object1 = selector_dict[selector1] selector_object2 = selector_dict[selector2] changed.append(selector_object1.split(".")[0]) changed.append(selector_object2.split(".")[0]) command = """%sself.drag_and_drop(%s, %s)%s""" % ( whitespace, selector_object1, selector_object2, comments, ) if object_dict: if not add_comments: comments = "" object_name1 = selector1 object_name2 = selector2 if object_name1 in object_dict.keys() and ( object_name2 in object_dict.keys() ): selector_object1 = object_dict[object_name1] selector_object2 = object_dict[object_name2] changed.append(object_name1.split(".")[0]) changed.append(object_name2.split(".")[0]) command = """%sself.drag_and_drop(%s, %s)%s""" % ( whitespace, selector_object1, selector_object2, comments, ) seleniumbase_lines.append(command) continue # Handle self.hover_and_click(SELECTOR, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.hover_and_click""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.hover_and_click""" r"""\(([\S]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector1 = "%s" % data.group(2) selector1 = remove_extra_slashes(selector1) page_selectors.append(selector1) selector2 = "%s" % data.group(3) selector2 = remove_extra_slashes(selector2) page_selectors.append(selector2) comments = data.group(4) command = """%sself.hover_and_click(%s, %s)%s""" % ( whitespace, selector1, selector2, comments, ) if selector_dict: if add_comments: comments = " # %s, %s" % (selector1, selector2) selector1 = optimize_selector(selector1) selector2 = optimize_selector(selector2) if selector1 in selector_dict.keys() and ( selector2 in selector_dict.keys() ): selector_object1 = selector_dict[selector1] selector_object2 = selector_dict[selector2] changed.append(selector_object1.split(".")[0]) changed.append(selector_object2.split(".")[0]) command = """%sself.hover_and_click(%s, %s)%s""" % ( whitespace, selector_object1, selector_object2, comments, ) if object_dict: if not add_comments: comments = "" object_name1 = selector1 object_name2 = selector2 if object_name1 in object_dict.keys() and ( object_name2 in object_dict.keys() ): selector_object1 = object_dict[object_name1] selector_object2 = object_dict[object_name2] changed.append(object_name1.split(".")[0]) changed.append(object_name2.split(".")[0]) command = """%sself.hover_and_click(%s, %s)%s""" % ( whitespace, selector_object1, selector_object2, comments, ) seleniumbase_lines.append(command) continue # Handle self.*_click(SELECTOR, OTHER_ARGS) if not object_dict: data = re.match( r"""^(\s*)self\.(\S*)_click""" r"""\((r?['"][\S\s]+['"]),([\S\s]*)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.(\S*)_click""" r"""\(([\S]+),([\S\s]*)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) action = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) other_args = data.group(4) page_selectors.append(selector) comments = data.group(5) command = """%sself.%s_click(%s,%s)%s""" % ( whitespace, action, selector, other_args, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.%s_click(%s,%s)%s""" % ( whitespace, action, selector_object, other_args, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.%s_click(%s,%s)%s""" % ( whitespace, action, selector_object, other_args, comments, ) seleniumbase_lines.append(command) continue # Handle self.*_click(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.(\S*)_click""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.(\S*)_click""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) action = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.%s_click(%s)%s""" % ( whitespace, action, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.%s_click(%s)%s""" % ( whitespace, action, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.%s_click(%s)%s""" % ( whitespace, action, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.click(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.click""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.click""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.click(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.click(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.click(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.js_click_*(SELECTOR) * = all/if_present/if_visible if not object_dict: data = re.match( r"""^(\s*)self\.js_click_(\S*)""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.js_click_(\S*)""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) by_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.js_click_%s(%s)%s""" % ( whitespace, by_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.js_click_%s(%s)%s""" % ( whitespace, by_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.js_click_%s(%s)%s""" % ( whitespace, by_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.click(SELECTOR, OTHER_ARGS) if not object_dict: data = re.match( r"""^(\s*)self\.click""" r"""\((r?['"][\S\s]+['"]),([\S\s]*)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.click""" r"""\(([\S]+),([\S\s]*)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) other_args = data.group(3) page_selectors.append(selector) comments = data.group(4) command = """%sself.click(%s,%s)%s""" % ( whitespace, selector, other_args, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.click(%s,%s)%s""" % ( whitespace, selector_object, other_args, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.click(%s,%s)%s""" % ( whitespace, selector_object, other_args, comments, ) seleniumbase_lines.append(command) continue # Handle self.click_if_visible(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.click_if_visible""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.click_if_visible""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.click_if_visible(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.click_if_visible(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.click_if_visible(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.hover(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.hover""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.hover""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.hover(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.hover(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.hover(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.*_element(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.(\S*)_element""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.(\S*)_element""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) action = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.%s_element(%s)%s""" % ( whitespace, action, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.%s_element(%s)%s""" % ( whitespace, action, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.%s_element(%s)%s""" % ( whitespace, action, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.*_elements(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.(\S*)_elements""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.(\S*)_elements""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) action = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.%s_elements(%s)%s""" % ( whitespace, action, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.%s_elements(%s)%s""" % ( whitespace, action, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.%s_elements(%s)%s""" % ( whitespace, action, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.set_text_content(SELECTOR, OTHER_ARGS) if not object_dict: data = re.match( r"""^(\s*)self\.set_text_content""" r"""\((r?['"][\S\s]+['"]),([\S\s]*)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.set_text_content""" r"""\(([\S]+),([\S\s]*)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) other_args = data.group(3) page_selectors.append(selector) comments = data.group(4) command = """%sself.set_text_content(%s,%s)%s""" % ( whitespace, selector, other_args, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.set_text_content(%s,%s)%s""" % ( whitespace, selector_object, other_args, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.set_text_content(%s,%s)%s""" % ( whitespace, selector_object, other_args, comments, ) seleniumbase_lines.append(command) continue # Handle self.highlight(SELECTOR, OTHER_ARGS) if not object_dict: data = re.match( r"""^(\s*)self\.highlight""" r"""\((r?['"][\S\s]+['"]),([\S\s]*)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.highlight""" r"""\(([\S]+),([\S\s]*)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) other_args = data.group(3) page_selectors.append(selector) comments = data.group(4) command = """%sself.highlight(%s,%s)%s""" % ( whitespace, selector, other_args, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.highlight(%s,%s)%s""" % ( whitespace, selector_object, other_args, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.highlight(%s,%s)%s""" % ( whitespace, selector_object, other_args, comments, ) seleniumbase_lines.append(command) continue # Handle self.highlight(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.highlight""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.highlight""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.highlight(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.highlight(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.highlight(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.check_if_unchecked(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.check_if_unchecked""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.check_if_unchecked""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.check_if_unchecked(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.check_if_unchecked(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.check_if_unchecked(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.uncheck_if_checked(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.uncheck_if_checked""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.uncheck_if_checked""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.uncheck_if_checked(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.uncheck_if_checked(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.uncheck_if_checked(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.select_if_unselected(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.select_if_unselected""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.select_if_unselected""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.select_if_unselected(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.select_if_unselected(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.select_if_unselected(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.unselect_if_selected(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.unselect_if_selected""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.unselect_if_selected""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.unselect_if_selected(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.unselect_if_selected(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.unselect_if_selected(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.switch_to_frame(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.switch_to_frame""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.switch_to_frame""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(3) command = """%sself.switch_to_frame(%s)%s""" % ( whitespace, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.switch_to_frame(%s)%s""" % ( whitespace, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.switch_to_frame(%s)%s""" % ( whitespace, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle with self.frame_switch(SELECTOR): if not object_dict: data = re.match( r"""^(\s*)(\S*)\sself\.frame_switch""" r"""\((r?['"][\S\s]+['"])\):([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)(\S*)\sself\.frame_switch""" r"""\(([\S]+)\):([\S\s]*)$""", line, ) if data: whitespace = data.group(1) if_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%s%s self.frame_switch(%s):%s""" % ( whitespace, if_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%s%s self.frame_switch(%s):%s""" % ( whitespace, if_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%s%s self.frame_switch(%s):%s""" % ( whitespace, if_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.assert_element_*(SELECTOR) *= present/not_visible/absent if not object_dict: data = re.match( r"""^(\s*)self\.assert_element_(\S*)""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.assert_element_(\S*)""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) by_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.assert_element_%s(%s)%s""" % ( whitespace, by_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.assert_element_%s(%s)%s""" % ( whitespace, by_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.assert_element_%s(%s)%s""" % ( whitespace, by_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.wait_for_element_*(SELECTOR) * = present/visible if not object_dict: data = re.match( r"""^(\s*)self\.wait_for_element_(\S*)""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.wait_for_element_(\S*)""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) by_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.wait_for_element_%s(%s)%s""" % ( whitespace, by_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.wait_for_element_%s(%s)%s""" % ( whitespace, by_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.wait_for_element_%s(%s)%s""" % ( whitespace, by_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.update_text(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.update_text""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.update_text""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.update_text(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.update_text(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.update_text(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.type(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.type""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.type""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.type(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.type(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.type(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.input(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.input""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.input""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.input(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.input(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.input(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.write(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.write""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.write""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.write(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.write(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.write(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.add_text(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.add_text""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.add_text""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.add_text(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.add_text(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.add_text(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.send_keys(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.send_keys""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.send_keys""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.send_keys(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.send_keys(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.send_keys(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.set_value(SELECTOR, TEXT) if not object_dict: data = re.match( r"""^(\s*)self\.set_value""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.set_value""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(3) comments = data.group(4) command = """%sself.set_value(%s, %s)%s""" % ( whitespace, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.set_value(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.set_value(%s, %s)%s""" % ( whitespace, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # Handle self.press_*_arrow(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.press_(\S*)_arrow""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.press_(\S*)_arrow""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) arrow = "%s" % data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.press_%s_arrow(%s)%s""" % ( whitespace, arrow, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.press_%s_arrow(%s)%s""" % ( whitespace, arrow, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.press_%s_arrow(%s)%s""" % ( whitespace, arrow, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.press_*_arrow(SELECTOR, TIMES) if not object_dict: data = re.match( r"""^(\s*)self\.press_(\S*)_arrow""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.press_(\S*)_arrow""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) arrow = "%s" % data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) times = data.group(4) comments = data.group(5) command = """%sself.press_%s_arrow(%s, %s)%s""" % ( whitespace, arrow, selector, times, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.press_%s_arrow(%s, %s)%s""" % ( whitespace, arrow, selector_object, times, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.press_%s_arrow(%s, %s)%s""" % ( whitespace, arrow, selector_object, times, comments, ) seleniumbase_lines.append(command) continue # Handle self.assert_text(TEXT, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.assert_text""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.assert_text""" r"""\(([\S\s]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) text = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.assert_text(%s, %s)%s""" % ( whitespace, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.assert_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.assert_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.assert_exact_text(TEXT, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.assert_exact_text""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.assert_exact_text""" r"""\(([\S\s]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) text = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.assert_exact_text(%s, %s)%s""" % ( whitespace, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.assert_exact_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.assert_exact_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.find_text(TEXT, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.find_text""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.find_text""" r"""\(([\S\s]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) text = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.find_text(%s, %s)%s""" % ( whitespace, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.find_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.find_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle if/elif self.is_text_*(TEXT, SELECTOR): * = present/visible if not object_dict: data = re.match( r"""^(\s*)(\S*)\sself\.is_text_(\S*)""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\):([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)(\S*)\sself\.is_text_(\S*)""" r"""\(([\S\s]+),\s?([\S]+)\):([\S\s]*)$""", line, ) if data: whitespace = data.group(1) if_type = data.group(2) by_type = data.group(3) text = data.group(4) selector = "%s" % data.group(5) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(6) command = """%s%s self.is_text_%s(%s, %s):%s""" % ( whitespace, if_type, by_type, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%s%s self.is_text_%s(%s, %s):%s""" % ( whitespace, if_type, by_type, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%s%s self.is_text_%s(%s, %s):%s""" % ( whitespace, if_type, by_type, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.wait_for_text(TEXT, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.wait_for_text""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.wait_for_text""" r"""\(([\S\s]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) text = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.wait_for_text(%s, %s)%s""" % ( whitespace, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.wait_for_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.wait_for_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.wait_for_text_visible(TEXT, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.wait_for_text_visible""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.wait_for_text_visible""" r"""\(([\S\s]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) text = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.wait_for_text(%s, %s)%s""" % ( whitespace, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.wait_for_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.wait_for_text(%s, %s)%s""" % ( whitespace, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.wait_for_text_not_visible(TEXT, SELECTOR) if not object_dict: data = re.match( r"""^(\s*)self\.wait_for_text_not_visible""" r"""\(([\S\s]+),\s?(r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.wait_for_text_not_visible""" r"""\(([\S\s]+),\s?([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) text = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.wait_for_text_not_visible(%s, %s)%s""" % ( whitespace, text, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = "%sself.wait_for_text_not_visible(%s, %s)%s" % ( whitespace, text, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = "%sself.wait_for_text_not_visible(%s, %s)%s" % ( whitespace, text, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle if/elif self.is_element_*(SELECTOR): * = present/visible if not object_dict: data = re.match( r"""^(\s*)(\S*)\sself\.is_element_(\S*)""" r"""\((r?['"][\S\s]+['"])\):([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)(\S*)\sself\.is_element_(\S*)""" r"""\(([\S]+)\):([\S\s]*)$""", line, ) if data: whitespace = data.group(1) if_type = data.group(2) by_type = data.group(3) selector = "%s" % data.group(4) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(5) command = """%s%s self.is_element_%s(%s):%s""" % ( whitespace, if_type, by_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%s%s self.is_element_%s(%s):%s""" % ( whitespace, if_type, by_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%s%s self.is_element_%s(%s):%s""" % ( whitespace, if_type, by_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle if/elif self.is_selected(SELECTOR): if not object_dict: data = re.match( r"""^(\s*)(\S*)\sself\.is_selected""" r"""\((r?['"][\S\s]+['"])\):([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)(\S*)\sself\.is_selected""" r"""\(([\S]+)\):([\S\s]*)$""", line, ) if data: whitespace = data.group(1) if_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%s%s self.is_selected(%s):%s""" % ( whitespace, if_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%s%s self.is_selected(%s):%s""" % ( whitespace, if_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%s%s self.is_selected(%s):%s""" % ( whitespace, if_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.assert*(self.is_selected(SELECTOR)) if not object_dict: data = re.match( r"""^(\s*)self\.assert(\S*)\(self\.is_selected""" r"""\((r?['"][\S\s]+['"])\)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.assert(\S*)\(self\.is_selected""" r"""\(([\S]+)\)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) a_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sself.assert%s(self.is_selected(%s))%s""" % ( whitespace, a_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.assert%s(self.is_selected(%s))%s""" % ( whitespace, a_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.assert%s(self.is_selected(%s))%s""" % ( whitespace, a_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.assert*(self.is_element_*(SELECTOR)) if not object_dict: data = re.match( r"""^(\s*)self\.assert(\S*)\(self\.is_element_(\S*)""" r"""\((r?['"][\S\s]+['"])\)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.assert(\S*)\(self\.is_element_(\S*)""" r"""\(([\S]+)\)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) a_type = data.group(2) v_type = data.group(3) selector = "%s" % data.group(4) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(5) command = """%sself.assert%s(self.is_element_%s(%s))%s""" % ( whitespace, a_type, v_type, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = "%sself.assert%s(self.is_element_%s(%s))%s" % ( whitespace, a_type, v_type, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = "%sself.assert%s(self.is_element_%s(%s))%s" % ( whitespace, a_type, v_type, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.set_attribute(SELECTOR, ATTRIBUTE, VALUE) if not object_dict: data = re.match( r"""^(\s*)self\.set_attribute""" r"""\((r?['"][\S\s]+['"])""" r""",\s?([\S\s]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.set_attribute""" r"""\(([\S]+),\s?([\S\s]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) attribute = data.group(3) value = data.group(4) comments = data.group(5) command = """%sself.set_attribute(%s, %s, %s)%s""" % ( whitespace, selector, attribute, value, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.set_attribute(%s, %s, %s)%s""" % ( whitespace, selector_object, attribute, value, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.set_attribute(%s, %s, %s)%s""" % ( whitespace, selector_object, attribute, value, comments, ) seleniumbase_lines.append(command) continue # Handle self.set_attributes(SELECTOR, ATTRIBUTE, VALUE) if not object_dict: data = re.match( r"""^(\s*)self\.set_attributes""" r"""\((r?['"][\S\s]+['"]),""" r"""\s?([\S\s]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.set_attributes""" r"""\(([\S]+),\s?([\S\s]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) selector = "%s" % data.group(2) selector = remove_extra_slashes(selector) page_selectors.append(selector) attribute = data.group(3) value = data.group(4) comments = data.group(5) command = """%sself.set_attributes(%s, %s, %s)%s""" % ( whitespace, selector, attribute, value, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.set_attributes(%s, %s, %s)%s""" % ( whitespace, selector_object, attribute, value, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.set_attributes(%s, %s, %s)%s""" % ( whitespace, selector_object, attribute, value, comments, ) seleniumbase_lines.append(command) continue # Handle VAR = self.get_attribute(SELECTOR, ATTRIBUTE) if not object_dict: data = re.match( r"""^(\s*)(\S*)\s?=\s?self\.get_attribute""" r"""\((r?['"][\S\s]+['"]),\s?(['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)(\S*)\s?=\s?self\.get_attribute""" r"""\(([\S]+),\s?(['"][\S\s]+['"])\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) var_name = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) attribute = data.group(4) comments = data.group(5) command = """%s%s = self.get_attribute(%s, %s)%s""" % ( whitespace, var_name, selector, attribute, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%s%s = self.get_attribute(%s, %s)%s""" % ( whitespace, var_name, selector_object, attribute, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%s%s = self.get_attribute(%s, %s)%s""" % ( whitespace, var_name, selector_object, attribute, comments, ) seleniumbase_lines.append(command) continue # Handle VAR = self.get_text(SELECTOR) if not object_dict: data = re.match( r"""^(\s*)(\S*)\s?=\s?self\.get_text""" r"""\((r?['"][\S\s]+['"])\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)(\S*)\s?=\s?self\.get_text""" r"""\(([\S]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) var_name = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%s%s = self.get_text(%s)%s""" % ( whitespace, var_name, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%s%s = self.get_text(%s)%s""" % ( whitespace, var_name, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%s%s = self.get_text(%s)%s""" % ( whitespace, var_name, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle if VAR [in|not in] self.get_text(SELECTOR): if not object_dict: data = re.match( r"""^(\s*)if\s(\S*\s?\S*)\sin\s?self\.get_text""" r"""\((r?['"][\S\s]+['"])\):([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)if\s(\S*\s?\S*)\sin\s?self\.get_text""" r"""\(([\S]+)\):([\S\s]*)$""", line, ) if data: whitespace = data.group(1) var_prefix = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) comments = data.group(4) command = """%sif %s in self.get_text(%s):%s""" % ( whitespace, var_prefix, selector, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sif %s in self.get_text(%s):%s""" % ( whitespace, var_prefix, selector_object, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sif %s in self.get_text(%s):%s""" % ( whitespace, var_prefix, selector_object, comments, ) seleniumbase_lines.append(command) continue # Handle self.*_element(SELECTOR, OTHER_ARGS) * = remove/hide if not object_dict: data = re.match( r"""^(\s*)self\.(\S*)_element""" r"""\((r?['"][\S\s]+['"]),([\S\s]*)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.(\S*)_element""" r"""\(([\S]+),([\S\s]*)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) action = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) other_args = data.group(4) page_selectors.append(selector) comments = data.group(5) command = """%sself.%s_element(%s,%s)%s""" % ( whitespace, action, selector, other_args, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.%s_element(%s,%s)%s""" % ( whitespace, action, selector_object, other_args, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.%s_element(%s,%s)%s""" % ( whitespace, action, selector_object, other_args, comments, ) seleniumbase_lines.append(command) continue # Handle self.*_elements(SELECTOR, OTHER_ARGS) * = remove/hide if not object_dict: data = re.match( r"""^(\s*)self\.(\S*)_elements""" r"""\((r?['"][\S\s]+['"]),([\S\s]*)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.(\S*)_elements""" r"""\(([\S]+),([\S\s]*)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) action = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) other_args = data.group(4) page_selectors.append(selector) comments = data.group(5) command = """%sself.%s_elements(%s,%s)%s""" % ( whitespace, action, selector, other_args, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.%s_elements(%s,%s)%s""" % ( whitespace, action, selector_object, other_args, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.%s_elements(%s,%s)%s""" % ( whitespace, action, selector_object, other_args, comments, ) seleniumbase_lines.append(command) continue # Handle self.select_option_by_*(SELECTOR, TEXT) * = index/value/text if not object_dict: data = re.match( r"""^(\s*)self\.select_option_by_(\S*)""" r"""\((r?['"][\S\s]+['"]),\s?([\S\s]+)\)([\S\s]*)$""", line, ) else: data = re.match( r"""^(\s*)self\.select_option_by_(\S*)""" r"""\(([\S]+),\s?([\S\s]+)\)([\S\s]*)$""", line, ) if data: whitespace = data.group(1) by_type = data.group(2) selector = "%s" % data.group(3) selector = remove_extra_slashes(selector) page_selectors.append(selector) text = data.group(4) comments = data.group(5) command = """%sself.select_option_by_%s(%s, %s)%s""" % ( whitespace, by_type, selector, text, comments, ) if selector_dict: if add_comments: comments = " # %s" % selector selector = optimize_selector(selector) if selector in selector_dict.keys(): selector_object = selector_dict[selector] changed.append(selector_object.split(".")[0]) command = """%sself.select_option_by_%s(%s, %s)%s""" % ( whitespace, by_type, selector_object, text, comments, ) if object_dict: if not add_comments: comments = "" object_name = selector if object_name in object_dict.keys(): selector_object = object_dict[object_name] changed.append(object_name.split(".")[0]) command = """%sself.select_option_by_%s(%s, %s)%s""" % ( whitespace, by_type, selector_object, text, comments, ) seleniumbase_lines.append(command) continue # seleniumbase_lines.append("### " + line) # untouched lines (Debug) seleniumbase_lines.append(line) continue return seleniumbase_lines, page_selectors, changed def extract_objects(): main(shell_command="extract-objects") def inject_objects(): main(shell_command="inject-objects") def objectify(): main(shell_command="objectify") def revert_objects(): main(shell_command="revert-objects") def main(shell_command): expected_arg = "[A SeleniumBase Python file]" num_args = len(sys.argv) command_args = sys.argv[2:] add_comments = False if shell_command == "objectify" or ( shell_command == "inject-objects" or (shell_command == "revert-objects") ): if len(command_args) >= 2: options = command_args[1:] for option in options: if option == "-c" or option == "--comments": add_comments = True else: invalid_run_command(shell_command) if ( sys.argv[0].split("/")[-1] == "seleniumbase" or (sys.argv[0].split("\\")[-1] == "seleniumbase") or (sys.argv[0].split("/")[-1] == "sbase") or (sys.argv[0].split("\\")[-1] == "sbase") ): if num_args < 3: invalid_run_command(shell_command) elif num_args > 3: if shell_command == "extract-objects": invalid_run_command(shell_command) else: pass else: pass else: invalid_run_command(shell_command) seleniumbase_file = command_args[0] if not seleniumbase_file.endswith(".py"): raise Exception( "\n\n`%s` is not a Python file!\n\n" "Expecting: %s\n" % (seleniumbase_file, expected_arg) ) with open(seleniumbase_file, mode="r", encoding="utf-8") as f: all_code = f.read() if "def test_" not in all_code: raise Exception( "\n\n`%s` is not a valid SeleniumBase unittest file!\n" "\nExpecting: %s\n" % (seleniumbase_file, expected_arg) ) code_lines = all_code.split("\n") seleniumbase_lines, page_selectors, changed = process_test_file(code_lines) var_names, existing_selectors, selector_list_dict = scan_objects_file() new_page_selectors = [] for selector in page_selectors: selector = optimize_selector(selector) if selector not in existing_selectors: new_page_selectors.append(selector) var_name = get_next_var_name(var_names) var_names.append(var_name) selector_list_dict["Page"].append((var_name, selector)) existing_selectors.append(selector) # print(new_page_selectors) # (For debugging) # print(selector_list_dict) # (For debugging) if shell_command == "extract-objects" or shell_command == "objectify": create_objects_file(selector_list_dict) if shell_command == "extract-objects": print("") return selector_dict = {} # Key: selector, Value: object object_dict = {} # Key: object, Value: selector for key in selector_list_dict.keys(): for pair in selector_list_dict[key]: selector_dict[pair[1]] = "%s.%s" % (str(key), str(pair[0])) object_name = "%s.%s" % (str(key), str(pair[0])) object_dict[object_name] = pair[1] good_sel_dict = {} aa, bb, cc = scan_objects_file() for s_key in selector_dict.keys(): if s_key in bb: good_sel_dict[s_key] = selector_dict[s_key] if shell_command == "inject-objects" or shell_command == "objectify": seleniumbase_lines, page_selectors, changed = process_test_file( code_lines, selector_dict=good_sel_dict, add_comments=add_comments ) added_classes = [] for item in changed: if item not in added_classes: added_classes.append(item) for line in seleniumbase_lines: if p_o_import in line: token = line.split(p_o_import)[1].strip() if token in added_classes: # Don't import page_objects classes if already imported added_classes.remove(token) if added_classes: sb_lines = [] fit_in = False for line in seleniumbase_lines: if line.startswith("from") and "import" in line and not fit_in: fit_in = True for add_me in added_classes: import_line = "%s%s" % (p_o_import, add_me) sb_lines.append(import_line) sb_lines.append(line) seleniumbase_lines = sb_lines if shell_command == "revert-objects": seleniumbase_lines, page_selectors, changed = process_test_file( code_lines, object_dict=object_dict, add_comments=add_comments ) removed_classes = [] for item in changed: if item not in removed_classes: removed_classes.append(item) if removed_classes: sb_lines = [] for line in seleniumbase_lines: if p_o_import in line: token = line.split(p_o_import)[1].strip() if token in removed_classes: continue sb_lines.append(line) seleniumbase_lines = sb_lines seleniumbase_code = "" for line in seleniumbase_lines: seleniumbase_code += line seleniumbase_code += "\n" seleniumbase_code = seleniumbase_code[:-1] # print (seleniumbase_code) # (For debugging) # Create SeleniumBase test file base_file_name = seleniumbase_file.split(".py")[0] converted_file_name = base_file_name + ".py" # Change end to make a copy out_file = open(converted_file_name, mode="w+", encoding="utf-8") out_file.writelines(seleniumbase_code) out_file.close() print('\n>>> ["%s"] was updated!\n' % converted_file_name) if __name__ == "__main__": invalid_objectify_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_print.py ================================================ """ Prints the code/text of any file with syntax-highlighting Usage: seleniumbase print [FILE] [OPTIONS] OR: sbase print [FILE] [OPTIONS] Options: -n (Add line Numbers to the rows) Output: Prints the code/text of any file with syntax-highlighting. """ import colorama import os import sys def invalid_run_command(msg=None): exp = " ** print **\n\n" exp += " Usage:\n" exp += " seleniumbase print [FILE] [OPTIONS]\n" exp += " OR: sbase print [FILE] [OPTIONS]\n" exp += " Options:\n" exp += " -n (Add line Numbers to the rows)\n" exp += " Output:\n" exp += " Prints the code/text of any file\n" exp += " with syntax-highlighting.\n" if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) else: raise Exception("INVALID RUN COMMAND!\n%s\n\n%s" % (msg, exp)) def sc_ranges(): # Get the ranges of special double-width characters. special_char_ranges = [ {"from": ord("\u4e00"), "to": ord("\u9FFF")}, {"from": ord("\u3040"), "to": ord("\u30ff")}, {"from": ord("\uac00"), "to": ord("\ud7a3")}, {"from": ord("\uff01"), "to": ord("\uff60")}, ] return special_char_ranges def is_char_wide(char): # Returns True if the special character is Chinese, Japanese, or Korean. sc = any( [range["from"] <= ord(char) <= range["to"] for range in sc_ranges()] ) return sc def get_width(line): # Return the true width of the line. Not the same as line length. # Chinese/Japanese/Korean characters take up two spaces of width. line_length = len(line) for char in line: if is_char_wide(char): line_length += 1 return line_length def main(): c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL line_numbers = False word_wrap = True # Always use word wrap now help_me = False invalid_cmd = None is_python_file = False code_lang = None command_args = sys.argv[2:] file_to_print = command_args[0] if file_to_print.lower().endswith(".py"): is_python_file = True code_lang = "python" elif file_to_print.lower().endswith(".js"): code_lang = "javascript" elif file_to_print.lower().endswith(".md"): code_lang = "markdown" elif file_to_print.lower().endswith(".html"): code_lang = "html" elif file_to_print.lower().endswith(".css"): code_lang = "css" elif file_to_print.lower().endswith(".go"): code_lang = "go" elif file_to_print.lower().endswith(".java"): code_lang = "java" elif file_to_print.lower().endswith(".feature"): code_lang = "gherkin" elif file_to_print.lower().endswith(".txt"): code_lang = "javascript" elif file_to_print.lower().endswith(".yml"): code_lang = "javascript" elif file_to_print.lower().endswith(".in"): code_lang = "javascript" elif "." not in file_to_print: code_lang = "markdown" else: code_lang = file_to_print.split(".")[-1].lower() if len(command_args) >= 2: options = command_args[1:] for option in options: option = option.lower() if option == "-n": line_numbers = True elif option == "-w": word_wrap = True else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break if help_me: invalid_run_command(invalid_cmd) all_code = None with open( file_to_print, mode="r+", encoding="utf-8", errors="ignore" ) as f: all_code = f.read() all_code = all_code.replace("\t", " ") code_lines = all_code.split("\n") console_width = None # width of console output when running script used_width = None # code_width and few spaces on right for padding magic_syntax = None # the syntax generated by rich.syntax.Syntax() try: console_width = os.get_terminal_size().columns if console_width: console_width = int(console_width) except Exception: console_width = None from seleniumbase.console_scripts import rich_helper the_code = "\n".join(code_lines) code_width = 1 w = 0 # line number whitespace if line_numbers: w = 4 num_lines = len(code_lines) if num_lines >= 10: w = 5 if num_lines >= 100: w = 6 if num_lines >= 1000: w = 7 if num_lines >= 10000: w = 8 if is_python_file: new_sb_lines = [] for line in code_lines: if line.endswith(" # noqa") and line.count(" # noqa") == 1: line = line.replace(" # noqa", "") line_length2 = len(line) # Normal Python string length used line_length = get_width(line) # Special characters count 2X if line_length > code_width: code_width = line_length if console_width: # If line is larger than console_width, try to optimize it. # Smart Python word wrap to be used with valid indentation. if line_length + w > console_width: # 5 is line number ws if line.strip().startswith("#"): new_sb_lines.append(line) continue elif ( line.count(" # ") == 1 and get_width(line.split(" # ")[0]) + w <= console_width ): # Line is short enough once comment is removed line = line.split(" # ")[0] new_sb_lines.append(line) continue elif ( line.count(" # ") == 1 and get_width(line.split(" # ")[0]) + w <= console_width ): # L-Length good if removing bad flake8 comment line = line.split(" # ")[0] new_sb_lines.append(line) continue if line.startswith("from") and " import " in line: line1 = line.split(" import ")[0] + " \\" line2 = " import " + line.split(" import ")[1] new_sb_lines.append(line1) new_sb_lines.append(line2) continue if ( line.count("(") >= 1 and line.count("(") == line.count(")") ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " first_paren = line.find("(") line1 = line[:first_paren + 1] line2 = new_ws + line[first_paren + 1:] if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('", "') == 1: line2a = line2.split('", "')[0] + '",' line2b = ( new_ws + '"' + (line2.split('", "')[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count("', '") == 1: line2a = line2.split("', '")[0] + "'," line2b = ( new_ws + "'" + (line2.split("', '")[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count("://") == 1 and ( line2.count('")') == 1 ): line2a = line2.split("://")[0] + '://"' line2b = ( new_ws + '"' + (line2.split("://")[1]) ) new_sb_lines.append(line2a) if get_width(line2b) + w > ( console_width ): if line2b.count("/") > 0: slash_one = line2b.find("/") slash_one_p1 = slash_one + 1 line2b1 = ( line2b[: slash_one + 1] + '"' ) line2b2 = ( new_ws + '"' + (line2b[slash_one_p1:]) ) new_sb_lines.append(line2b1) if line2b2.count(") # ") == 1: line2b2 = ( line2b2.split(") # ")[ 0 ] + ")" ) new_sb_lines.append(line2b2) continue new_sb_lines.append(line2b) continue elif line2.count("://") == 1 and ( line2.count("')") == 1 ): line2a = line2.split("://")[0] + "://'" line2b = ( new_ws + "'" + (line2.split("://")[1]) ) new_sb_lines.append(line2a) if get_width(line2b) + w > ( console_width ): if line2b.count("/") > 0: slash_one = line2b.find("/") slash_one_p1 = slash_one + 1 line2b1 = ( line2b[: slash_one + 1] + "'" ) line2b2 = ( new_ws + "'" + (line2b[slash_one_p1:]) ) new_sb_lines.append(line2b1) if line2b2.count(") # ") == 1: line2b2 = ( line2b2.split(") # ")[ 0 ] + ")" ) new_sb_lines.append(line2b2) continue new_sb_lines.append(line2b) continue elif line2.count(", ") == 1: line2a = line2.split(", ")[0] + "," line2b = new_ws + line2.split(", ")[1] new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count('="') == 1 and ( line2.lstrip().startswith("'") ): line2a = line2.split('="')[0] + "='" line2b = ( new_ws + "'\"" + (line2.split('="')[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count("='") == 1 and ( line2.lstrip().startswith('"') ): line2a = line2.split("='")[0] + '="' line2b = ( new_ws + "\"'" + (line2.split("='")[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) elif get_width(line2) + 4 + w <= console_width: line2 = " " + line2 new_sb_lines.append(line1) new_sb_lines.append(line2) else: new_sb_lines.append(line) continue if line.count('("') == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split('("')[0] + "(" line2 = new_ws + '"' + line.split('("')[1] if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('" in self.') == 1: line2a = ( line2.split('" in self.')[0] + '" in' ) line2b = ( new_ws + "self." + (line2.split('" in self.')[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) elif get_width(line2) + 4 + w <= console_width: line2 = " " + line2 new_sb_lines.append(line1) new_sb_lines.append(line2) else: new_sb_lines.append(line) continue if line.count("('") == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("('")[0] + "(" line2 = new_ws + "'" + line.split("('")[1] if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("' in self.") == 1: line2a = ( line2.split("' in self.")[0] + "' in" ) line2b = ( new_ws + "self." + (line2.split("' in self.")[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) elif get_width(line2) + 4 + w <= console_width: line2 = " " + line2 new_sb_lines.append(line1) new_sb_lines.append(line2) else: new_sb_lines.append(line) continue if line.count('= "') == 1 and line.count("://") == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("://")[0] + '://" \\' line2 = new_ws + '"' + line.split("://")[1] new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("/") > 0: slash_one = line2.find("/") slash_one_p1 = slash_one + 1 line2a = line2[: slash_one + 1] + '" \\' line2b = ( new_ws + '"' + line2[slash_one_p1:] ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) continue if line.count("= '") == 1 and line.count("://") == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("://")[0] + "://' \\" line2 = new_ws + "'" + line.split("://")[1] new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("/") > 0: slash_one = line2.find("/") slash_one_p1 = slash_one + 1 line2a = line2[: slash_one + 1] + "' \\" line2b = ( new_ws + "'" + line2[slash_one_p1:] ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) continue if line.count("(self.") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("(self.")[0] + "(" line2 = new_ws + "self." + line.split("(self.")[1] if get_width(line1) + w <= console_width: new_sb_lines.append(line1) new_sb_lines.append(line2) continue if line.count(" == ") == 1 and not ( line.endswith(":") or (": #") in line ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" == ")[0] + " == (" line2 = new_ws + line.split(" == ")[1] + ")" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2) continue if line.count(" == ") == 1 and line.endswith(":"): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" == ")[0] + " == (" line2 = new_ws + line.split(" == ")[1][:-1] + "):" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2) continue if ( line.count(" == ") == 1 and (line.count(": #") == 1) and (line.find(" == ") < line.find(": #")) ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " comments = " #" + line.split(": #")[1] line0 = line.split(": #")[0] + ":" line1 = line0.split(" == ")[0] + " == (" line2 = new_ws + line0.split(" == ")[1][:-1] + "):" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) if ( get_width(line2 + comments) + w <= console_width ): new_sb_lines.append(line2 + comments) else: new_sb_lines.append(line2) continue if line.count(" % ") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" % ")[0] + " \\" line2 = new_ws + "% " + line.split(" % ")[1] if get_width(line1) + w <= console_width: new_sb_lines.append(line1) new_sb_lines.append(line2) continue if line.count(" = ") == 1 and (" # ") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" = ")[0] + " = (" line2 = new_ws + line.split(" = ")[1] + ")" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2) continue elif get_width(line1) + w <= console_width: if line2.count(" % ") == 1 and not ( line2.endswith(":") ): whitespace = line_length2 - len( line2.lstrip() ) line2a = line2.split(" % ")[0] + " \\" line2b = ( new_ws + "% " + line2.split(" % ")[1] ) if get_width(line2a) + w <= console_width: if ( get_width(line2b) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue if ( line.count(" = ") == 1 and (line.count(" # ") == 1) and (line.find(" = ") < line.find(" # ")) ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " comments = " # " + line.split(" # ")[1] line0 = line.split(" # ")[0] line1 = line0.split(" = ")[0] + " = (" line2 = new_ws + line0.split(" = ")[1] + ")" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) if ( get_width(line2 + comments) + w <= console_width ): new_sb_lines.append(line2 + comments) else: new_sb_lines.append(line2) continue new_sb_lines.append(line) if new_sb_lines: code_lines = new_sb_lines the_code = "\n".join(code_lines) if code_lang != "python": for line in code_lines: line_length = get_width(line) if line_length > code_width: code_width = line_length extra_r_spaces = 2 if console_width and (code_width + extra_r_spaces < console_width): used_width = code_width + extra_r_spaces the_code = rich_helper.fix_emoji_spacing(the_code) the_theme = "monokai" if file_to_print.split(os.sep)[-1].startswith("."): the_theme = "tango" magic_syntax = rich_helper.process_syntax( the_code, code_lang, theme=the_theme, line_numbers=line_numbers, code_width=used_width, word_wrap=word_wrap, ) # ---------------------------------------- dash_length = 62 # May change if used_width and used_width + w < console_width: dash_length = used_width + w elif console_width: dash_length = console_width dashes = "-" * dash_length print(dashes) print_success = False if code_lang == "markdown" and not line_numbers: all_code = rich_helper.fix_emoji_spacing(all_code) if "*" not in all_code and "*" not in all_code: if "*" not in all_code and "*" not in all_code: all_code = all_code.replace("", "**") all_code = all_code.replace("", "**") if "`" not in all_code and "`" not in all_code: if "`" not in all_code and "`" not in all_code: all_code = all_code.replace('', "``") all_code = all_code.replace("", "``") all_code = all_code.replace("", "``") # Display ALL tags as an

      because the font size is fixed all_code = all_code.replace("\n

      ", "\n# ").replace("

      ", "") all_code = all_code.replace("\n

      ", "\n# ").replace("

      ", "") all_code = all_code.replace("\n

      ", "\n# ").replace("

      ", "") all_code = all_code.replace("\n

      ", "\n# ").replace("

      ", "") all_code = rich_helper.get_code_without_tag(all_code, "summary") all_code = rich_helper.get_code_without_tag(all_code, "details") all_code = rich_helper.get_code_without_tag(all_code, "span") all_code = rich_helper.get_code_without_tag(all_code, "div") all_code = rich_helper.get_code_without_tag(all_code, "img") all_code = rich_helper.get_code_without_tag(all_code, "li") all_code = rich_helper.get_code_without_tag(all_code, "ul") all_code = rich_helper.get_code_without_tag(all_code, "a") all_code = rich_helper.get_code_without_tag(all_code, "p") all_code = all_code.replace("
      ", "\n") print_success = rich_helper.display_markdown(all_code) if all_code.endswith("\n"): print() # Because "rich" skips the last line if new-line elif magic_syntax: print_success = rich_helper.display_code(magic_syntax) if not magic_syntax or not print_success: for line in code_lines: print(line) print(dashes) # ---------------------------------------- if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/console_scripts/sb_recorder.py ================================================ """ ** recorder ** Launches the SeleniumBase Recorder Desktop App. Usage: seleniumbase recorder [OPTIONS] sbase recorder [OPTIONS] Options: --uc / --undetected (Use undetectable mode.) --cdp (Same as "--uc" and "--undetectable".) --behave (Also output Behave/Gherkin files.) Output: Launches the SeleniumBase Recorder Desktop App. """ import colorama import os import subprocess import sys import tkinter as tk from seleniumbase import config as sb_config from seleniumbase.fixtures import page_utils from seleniumbase.fixtures import shared_utils from tkinter import messagebox sb_config.rec_subprocess_p = None sb_config.rec_subprocess_used = False sys_executable = sys.executable if " " in sys_executable: sys_executable = "python" def set_colors(use_colors): c0 = "" c1 = "" c2 = "" c3 = "" c4 = "" cr = "" if use_colors: c0 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c1 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c2 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c3 = colorama.Fore.LIGHTRED_EX + colorama.Back.LIGHTYELLOW_EX c4 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL return c0, c1, c2, c3, c4, cr def send_window_to_front(window): window.lift() window.attributes("-topmost", True) window.after_idle(window.attributes, "-topmost", False) def show_already_recording_warning(): messagebox.showwarning( "SeleniumBase Recorder: Already Running!", "Please finalize the active recording from the terminal\n" 'where you opened the Recorder: Type "c" and hit Enter.', ) def file_name_error(file_name): error_msg = None if not file_name.endswith(".py"): error_msg = 'File name must end with ".py"!' elif "*" in file_name or len(str(file_name)) < 4: error_msg = "Invalid file name!" elif file_name.startswith("-"): error_msg = 'File name cannot start with "-"!' elif "/" in str(file_name) or "\\" in str(file_name): error_msg = "File must be created in the current directory!" return error_msg def do_recording(file_name, url, overwrite_enabled, use_chrome, window): poll = None if sb_config.rec_subprocess_used: poll = sb_config.rec_subprocess_p.poll() if not sb_config.rec_subprocess_used or poll is not None: pass else: show_already_recording_warning() send_window_to_front(window) poll = sb_config.rec_subprocess_p.poll() if poll is None: return file_name = file_name.strip() error_msg = file_name_error(file_name) if error_msg: messagebox.showwarning( "Invalid filename", "Invalid filename: %s" % error_msg ) return url = url.strip() if not page_utils.is_valid_url(url): if page_utils.is_valid_url("https://" + url): url = "https://" + url if not page_utils.is_valid_url(url): messagebox.showwarning( "Invalid URL", "Enter a valid URL! (Eg. seleniumbase.io)" ) else: if os.path.exists(os.getcwd() + "/" + file_name): if not overwrite_enabled: msgbox = tk.messagebox.askquestion( "Overwrite?", 'Are you sure you want to overwrite "%s"?' % file_name, icon="warning", ) if msgbox == "yes": os.remove(file_name) else: tk.messagebox.showinfo("Cancelled", "Recording Cancelled!") return else: os.remove(file_name) add_on = "" command_args = sys.argv[2:] if ( "--rec-behave" in command_args or "--behave" in command_args or "--gherkin" in command_args ): add_on = " --rec-behave" command = ( "%s -m seleniumbase mkrec %s --url=%s --gui" % (sys_executable, file_name, url) ) if '"' not in url: command = ( '%s -m seleniumbase mkrec %s --url="%s" --gui' % (sys_executable, file_name, url) ) elif "'" not in url: command = ( "%s -m seleniumbase mkrec %s --url='%s' --gui" % (sys_executable, file_name, url) ) if not use_chrome: command += " --edge" elif "--opera" in command_args: command += " --opera" elif "--brave" in command_args: command += " --brave" elif "--comet" in command_args: command += " --comet" elif "--atlas" in command_args: command += " --atlas" elif "--use-chromium" in command_args: command += " --use-chromium" if ( "--uc" in command_args or "--cdp" in command_args or "--undetected" in command_args or "--undetectable" in command_args ): command += " --uc" if "--ee" in command_args: command += " --ee" command += add_on poll = None if sb_config.rec_subprocess_used: poll = sb_config.rec_subprocess_p.poll() if not sb_config.rec_subprocess_used or poll is not None: sb_config.rec_subprocess_p = subprocess.Popen(command, shell=True) sb_config.rec_subprocess_used = True else: show_already_recording_warning() send_window_to_front(window) def do_playback(file_name, use_chrome, window, demo_mode=False): file_name = file_name.strip() error_msg = file_name_error(file_name) if error_msg: messagebox.showwarning( "Invalid filename", "Invalid filename: %s" % error_msg ) return if not os.path.exists(os.getcwd() + "/" + file_name): messagebox.showwarning( "File does not exist", 'File "%s" does not exist in the current directory!' % file_name, ) return command = "%s -m pytest %s -q -s" % (sys_executable, file_name) if shared_utils.is_linux(): command += " --gui" if not use_chrome: command += " --edge" if demo_mode: command += " --demo" command_args = sys.argv[2:] if ( "--uc" in command_args or "--cdp" in command_args or "--undetected" in command_args or "--undetectable" in command_args ): command += " --uc" poll = None if sb_config.rec_subprocess_used: poll = sb_config.rec_subprocess_p.poll() if not sb_config.rec_subprocess_used or poll is not None: print(command) subprocess.Popen(command, shell=True) else: messagebox.showwarning( "SeleniumBase Recorder: Already Running!", "Please finalize the active recording from the terminal\n" 'where you opened the Recorder: Type "c" and hit Enter.', ) send_window_to_front(window) def create_tkinter_gui(): default_file_name = "new_recording.py" window = tk.Tk() window.title("SeleniumBase Recorder App") window.geometry("344x388") frame = tk.Frame(window) frame.pack() tk.Label(window, text="").pack() fname = tk.StringVar(value=default_file_name) tk.Label(window, text="Enter filename to save recording as:").pack() entry = tk.Entry(window, textvariable=fname) entry.pack() cbx = tk.IntVar() chk = tk.Checkbutton(window, text="Overwrite existing files", variable=cbx) chk.pack() chk.select() use_stealth = False command_args = sys.argv[2:] if ( "--uc" in command_args or "--cdp" in command_args or "--undetected" in command_args or "--undetectable" in command_args ): use_stealth = True browser_display = "Use Chrome over Edge" if "--opera" in command_args: browser_display = "Use Opera over Edge" elif "--brave" in command_args: browser_display = "Use Brave over Edge" elif "--comet" in command_args: browser_display = "Use Comet over Edge" elif "--atlas" in command_args: browser_display = "Use Atlas over Edge" cbb = tk.IntVar() if not use_stealth: chkb = tk.Checkbutton(window, text=browser_display, variable=cbb) chkb.pack() if "--edge" not in command_args: chkb.select() else: chkb = tk.Checkbutton( window, text="Stealthy Chrome Mode", variable=cbb ) chkb.pack() chkb.select() chkb.config(state=tk.DISABLED) tk.Label(window, text="").pack() url = tk.StringVar() tk.Label(window, text="Enter the URL to start recording on:").pack() entry = tk.Entry(window, textvariable=url) entry.pack() entry.focus() entry.bind( "", ( lambda _: do_recording( fname.get(), url.get(), cbx.get(), cbb.get(), window ) ), ) tk.Button( window, text="Record", fg="red", command=lambda: do_recording( fname.get(), url.get(), cbx.get(), cbb.get(), window ), ).pack() tk.Label(window, text="").pack() tk.Label(window, text="Playback recording (Normal Mode):").pack() tk.Button( window, text="Playback", fg="green", command=lambda: do_playback(fname.get(), cbb.get(), window), ).pack() tk.Label(window, text="").pack() tk.Label(window, text="Playback recording (Demo Mode):").pack() try: tk.Button( window, text="Playback (Demo Mode)", fg="teal", command=lambda: do_playback( fname.get(), cbb.get(), window, demo_mode=True ), ).pack() except Exception: tk.Button( window, text="Playback (Demo Mode)", fg="blue", command=lambda: do_playback( fname.get(), cbb.get(), window, demo_mode=True ), ).pack() # Bring form window to front send_window_to_front(window) # Use decoy to set correct focus on main window decoy = tk.Tk() decoy.geometry("1x1") decoy.iconify() decoy.update() decoy.deiconify() decoy.destroy() # Start tkinter window.mainloop() end_program() def recorder_still_running(): poll = None if sb_config.rec_subprocess_used: try: poll = sb_config.rec_subprocess_p.poll() except Exception: return False else: return False if poll is not None: return False return True def show_still_running_warning(): """Give the user a chance to end the recording safely via the pytest pdb Debug Mode so that processes such as chromedriver and Python don't remain open and hanging in the background.""" messagebox.showwarning( "SeleniumBase Recorder: Still Running!", "Please finalize the active recording from the terminal\n" 'where you opened the Recorder: Type "c" and hit Enter.\n' "(Then you can safely close this alert.)", ) def end_program(): if recorder_still_running(): show_still_running_warning() def main(): use_colors = True if shared_utils.is_linux(): use_colors = False c0, c1, c2, c3, c4, cr = set_colors(use_colors) message = "" message += c2 message += "*" message += c4 message += " Starting the " message += c0 message += "Selenium" message += c1 message += "Base" message += c2 message += " " message += c3 message += "Recorder" message += c4 message += " Desktop App" message += c2 message += "..." message += cr print(message) create_tkinter_gui() if __name__ == "__main__": print('To open the Recorder Desktop App: "seleniumbase recorder"') ================================================ FILE: seleniumbase/core/__init__.py ================================================ ================================================ FILE: seleniumbase/core/application_manager.py ================================================ import time class ApplicationManager: """Generating application strings for the Testcase Database.""" @classmethod def generate_application_string(cls, test): """Generate a string based on some of the given information that's pulled from the test object: app_env, start_time.""" app_env = "test" if hasattr(test, "env"): app_env = test.env elif hasattr(test, "environment"): app_env = test.environment start_time = int(time.time() * 1000) return "%s.%s" % (app_env, start_time) ================================================ FILE: seleniumbase/core/browser_launcher.py ================================================ import fasteners import json import logging import os import platform import re import shutil import subprocess import sys import time import types import urllib3 import warnings from contextlib import suppress from filelock import FileLock from selenium import webdriver from selenium.common.exceptions import ElementClickInterceptedException from selenium.common.exceptions import InvalidSessionIdException from selenium.common.exceptions import SessionNotCreatedException from selenium.webdriver.chrome.service import Service as ChromeService from selenium.webdriver.common.options import ArgOptions from selenium.webdriver.common.service import utils as service_utils from selenium.webdriver.edge.service import Service as EdgeService from selenium.webdriver.firefox.service import Service as FirefoxService from selenium.webdriver.safari.service import Service as SafariService from seleniumbase import config as sb_config from seleniumbase import decorators from seleniumbase import drivers # webdriver storage folder for SeleniumBase from seleniumbase.drivers import cft_drivers # chrome-for-testing from seleniumbase.drivers import chs_drivers # chrome-headless-shell from seleniumbase.drivers import opera_drivers # still uses chromedriver from seleniumbase.drivers import brave_drivers # still uses chromedriver from seleniumbase.drivers import comet_drivers # still uses chromedriver from seleniumbase.drivers import atlas_drivers # still uses chromedriver from seleniumbase.drivers import chromium_drivers # still uses chromedriver from seleniumbase import extensions # browser extensions storage folder from seleniumbase.config import settings from seleniumbase.core import detect_b_ver from seleniumbase.core import download_helper from seleniumbase.core import proxy_helper from seleniumbase.core import sb_driver from seleniumbase.core import sb_cdp from seleniumbase.fixtures import constants from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_actions from seleniumbase.fixtures import shared_utils urllib3.disable_warnings() DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__)) DRIVER_DIR_CFT = os.path.dirname(os.path.realpath(cft_drivers.__file__)) DRIVER_DIR_CHS = os.path.dirname(os.path.realpath(chs_drivers.__file__)) DRIVER_DIR_OPERA = os.path.dirname(os.path.realpath(opera_drivers.__file__)) DRIVER_DIR_BRAVE = os.path.dirname(os.path.realpath(brave_drivers.__file__)) DRIVER_DIR_COMET = os.path.dirname(os.path.realpath(comet_drivers.__file__)) DRIVER_DIR_ATLAS = os.path.dirname(os.path.realpath(atlas_drivers.__file__)) DRIVER_DIR_CHROMIUM = os.path.dirname( os.path.realpath(chromium_drivers.__file__) ) # Make sure that the SeleniumBase DRIVER_DIR is at the top of the System PATH # (Changes to the System PATH with os.environ only last during the test run) if not os.environ["PATH"].startswith(DRIVER_DIR): # Remove existing SeleniumBase DRIVER_DIR from System PATH if present os.environ["PATH"] = os.environ["PATH"].replace(DRIVER_DIR, "") # If two path separators are next to each other, replace with just one os.environ["PATH"] = os.environ["PATH"].replace( os.pathsep + os.pathsep, os.pathsep ) # Put the SeleniumBase DRIVER_DIR at the beginning of the System PATH os.environ["PATH"] = DRIVER_DIR + os.pathsep + os.environ["PATH"] EXTENSIONS_DIR = os.path.dirname(os.path.realpath(extensions.__file__)) DISABLE_CSP_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "disable_csp.zip") AD_BLOCK_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "ad_block.zip") RECORDER_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "recorder.zip") SBASE_EXT_ZIP_PATH = os.path.join(EXTENSIONS_DIR, "sbase_ext.zip") DOWNLOADS_FOLDER = download_helper.get_downloads_folder() PROXY_ZIP_PATH = proxy_helper.PROXY_ZIP_PATH PROXY_ZIP_LOCK = proxy_helper.PROXY_ZIP_LOCK PROXY_DIR_PATH = proxy_helper.PROXY_DIR_PATH PROXY_DIR_LOCK = proxy_helper.PROXY_DIR_LOCK LOCAL_CHROMEDRIVER = None LOCAL_GECKODRIVER = None LOCAL_EDGEDRIVER = None LOCAL_IEDRIVER = None LOCAL_HEADLESS_IEDRIVER = None LOCAL_UC_DRIVER = None ARCH = platform.architecture()[0] IS_ARM_MAC = shared_utils.is_arm_mac() IS_MAC = shared_utils.is_mac() IS_LINUX = shared_utils.is_linux() IS_WINDOWS = shared_utils.is_windows() if IS_MAC or IS_LINUX: LOCAL_CHROMEDRIVER = DRIVER_DIR + "/chromedriver" LOCAL_GECKODRIVER = DRIVER_DIR + "/geckodriver" LOCAL_EDGEDRIVER = DRIVER_DIR + "/msedgedriver" LOCAL_UC_DRIVER = DRIVER_DIR + "/uc_driver" elif IS_WINDOWS: LOCAL_EDGEDRIVER = DRIVER_DIR + "/msedgedriver.exe" LOCAL_IEDRIVER = DRIVER_DIR + "/IEDriverServer.exe" LOCAL_HEADLESS_IEDRIVER = DRIVER_DIR + "/headless_ie_selenium.exe" LOCAL_CHROMEDRIVER = DRIVER_DIR + "/chromedriver.exe" LOCAL_GECKODRIVER = DRIVER_DIR + "/geckodriver.exe" LOCAL_UC_DRIVER = DRIVER_DIR + "/uc_driver.exe" else: # Cannot determine system pass # SeleniumBase will use web drivers from the System PATH by default def log_d(message): """If setting sb_config.settings.HIDE_DRIVER_DOWNLOADS to True, output from driver downloads are logged instead of printed.""" if getattr(settings, "HIDE_DRIVER_DOWNLOADS", None): logging.debug(message) else: print(message) def override_driver_dir(driver_dir): if ( driver_dir and isinstance(driver_dir, str) and os.path.exists(driver_dir) ): driver_dir = os.path.realpath(driver_dir) sb_config.settings.NEW_DRIVER_DIR = driver_dir if ( not os.environ["PATH"].startswith(driver_dir) and len(driver_dir) >= 4 ): os.environ["PATH"] = os.environ["PATH"].replace(driver_dir, "") os.environ["PATH"] = os.environ["PATH"].replace( os.pathsep + os.pathsep, os.pathsep ) os.environ["PATH"] = driver_dir + os.pathsep + os.environ["PATH"] elif ( not driver_dir or not isinstance(driver_dir, str) or not os.path.exists(driver_dir) ): bad_dir = "" if driver_dir and isinstance(driver_dir, str): bad_dir = os.path.realpath(driver_dir) log_d( "\n* Warning: Cannot set driver_dir to nonexistent directory:\n%s" "\n* Will use the default folder instead:\n%s" % (bad_dir, DRIVER_DIR) ) def make_driver_executable_if_not(driver_path): # Verify driver has executable permissions. If not, add them. permissions = oct(os.stat(driver_path)[0])[-3:] if "4" in permissions or "6" in permissions: # We want at least a '5' or '7' to make sure it's executable shared_utils.make_executable(driver_path) def extend_driver( driver, proxy_auth=False, use_uc=True, recorder_ext=False ): # Extend the driver with new methods driver.default_find_element = driver.find_element driver.default_find_elements = driver.find_elements driver.default_add_cookie = driver.add_cookie driver.default_get_cookie = driver.get_cookie driver.default_delete_cookie = driver.delete_cookie driver.default_back = driver.back driver.default_forward = driver.forward driver.default_refresh = driver.refresh DM = sb_driver.DriverMethods(driver) driver.find_element = DM.find_element driver.find_elements = DM.find_elements driver.add_cookie = DM.add_cookie driver.get_cookie = DM.get_cookie driver.delete_cookie = DM.delete_cookie driver.back = DM.back driver.forward = DM.forward driver.refresh = DM.refresh driver.locator = DM.locator page = types.SimpleNamespace() page.open = DM.open_url page.click = DM.click page.click_link = DM.click_link page.click_if_visible = DM.click_if_visible page.click_active_element = DM.click_active_element page.send_keys = DM.send_keys page.press_keys = DM.press_keys page.type = DM.update_text page.submit = DM.submit page.assert_element = DM.assert_element_visible page.assert_element_present = DM.assert_element_present page.assert_element_not_visible = DM.assert_element_not_visible page.assert_text = DM.assert_text page.assert_exact_text = DM.assert_exact_text page.assert_non_empty_text = DM.assert_non_empty_text page.assert_text_not_visible = DM.assert_text_not_visible page.wait_for_element = DM.wait_for_element page.wait_for_text = DM.wait_for_text page.wait_for_exact_text = DM.wait_for_exact_text page.wait_for_non_empty_text = DM.wait_for_non_empty_text page.wait_for_text_not_visible = DM.wait_for_text_not_visible page.wait_for_and_accept_alert = DM.wait_for_and_accept_alert page.wait_for_and_dismiss_alert = DM.wait_for_and_dismiss_alert page.is_element_present = DM.is_element_present page.is_element_visible = DM.is_element_visible page.is_text_visible = DM.is_text_visible page.is_exact_text_visible = DM.is_exact_text_visible page.is_attribute_present = DM.is_attribute_present page.is_non_empty_text_visible = DM.is_non_empty_text_visible page.get_text = DM.get_text page.find_element = DM.find_element page.find_elements = DM.find_elements page.locator = DM.locator page.get_current_url = DM.get_current_url page.get_page_source = DM.get_page_source page.get_title = DM.get_title page.get_page_title = DM.get_title page.switch_to_default_window = DM.switch_to_default_window page.switch_to_newest_window = DM.switch_to_newest_window page.open_new_window = DM.open_new_window page.open_new_tab = DM.open_new_tab page.switch_to_window = DM.switch_to_window page.switch_to_tab = DM.switch_to_tab page.switch_to_frame = DM.switch_to_frame driver.page = page js = types.SimpleNamespace() js.js_click = DM.js_click js.get_active_element_css = DM.get_active_element_css js.get_locale_code = DM.get_locale_code js.get_origin = DM.get_origin js.get_user_agent = DM.get_user_agent js.highlight = DM.highlight driver.js = js driver.open = DM.open_url driver.click = DM.click driver.click_link = DM.click_link driver.click_if_visible = DM.click_if_visible driver.click_active_element = DM.click_active_element driver.send_keys = DM.send_keys driver.press_keys = DM.press_keys driver.type = DM.update_text driver.submit = DM.submit driver.assert_element = DM.assert_element_visible driver.assert_element_present = DM.assert_element_present driver.assert_element_not_visible = DM.assert_element_not_visible driver.assert_text = DM.assert_text driver.assert_exact_text = DM.assert_exact_text driver.assert_non_empty_text = DM.assert_non_empty_text driver.assert_text_not_visible = DM.assert_text_not_visible driver.wait_for_element = DM.wait_for_element driver.wait_for_element_visible = DM.wait_for_element_visible driver.wait_for_element_present = DM.wait_for_element_present driver.wait_for_element_absent = DM.wait_for_element_absent driver.wait_for_element_not_visible = DM.wait_for_element_not_visible driver.wait_for_selector = DM.wait_for_selector driver.wait_for_text = DM.wait_for_text driver.wait_for_exact_text = DM.wait_for_exact_text driver.wait_for_non_empty_text = DM.wait_for_non_empty_text driver.wait_for_text_not_visible = DM.wait_for_text_not_visible driver.wait_for_and_accept_alert = DM.wait_for_and_accept_alert driver.wait_for_and_dismiss_alert = DM.wait_for_and_dismiss_alert driver.is_element_present = DM.is_element_present driver.is_element_visible = DM.is_element_visible driver.is_text_visible = DM.is_text_visible driver.is_exact_text_visible = DM.is_exact_text_visible driver.is_attribute_present = DM.is_attribute_present driver.is_non_empty_text_visible = DM.is_non_empty_text_visible driver.is_valid_url = DM.is_valid_url driver.is_alert_present = DM.is_alert_present driver.is_online = DM.is_online driver.is_connected = DM.is_connected driver.is_uc_mode_active = DM.is_uc_mode_active driver.is_cdp_mode_active = DM.is_cdp_mode_active driver.js_click = DM.js_click driver.get_text = DM.get_text driver.get_active_element_css = DM.get_active_element_css driver.get_locale_code = DM.get_locale_code driver.get_screen_rect = DM.get_screen_rect driver.get_origin = DM.get_origin driver.get_user_agent = DM.get_user_agent driver.get_cookie_string = DM.get_cookie_string driver.highlight = DM.highlight driver.highlight_click = DM.highlight_click driver.highlight_if_visible = DM.highlight_if_visible driver.sleep = time.sleep driver.get_attribute = DM.get_attribute driver.get_parent = DM.get_parent driver.get_current_url = DM.get_current_url driver.get_page_source = DM.get_page_source driver.get_title = DM.get_title driver.get_page_title = DM.get_title driver.switch_to_default_window = DM.switch_to_default_window driver.switch_to_newest_window = DM.switch_to_newest_window driver.open_new_window = DM.open_new_window driver.open_new_tab = DM.open_new_tab driver.switch_to_window = DM.switch_to_window driver.switch_to_tab = DM.switch_to_tab driver.switch_to_frame = DM.switch_to_frame driver.reset_window_size = DM.reset_window_size if recorder_ext: from seleniumbase.js_code.recorder_js import recorder_js recorder_code = ( """window.onload = function() { %s };""" % recorder_js ) driver.execute_cdp_cmd( "Page.addScriptToEvaluateOnNewDocument", {"source": recorder_code} ) if hasattr(driver, "proxy"): driver.set_wire_proxy = DM.set_wire_proxy completed_loads = [] for ext_dir in sb_config._ext_dirs: if ext_dir not in completed_loads: completed_loads.append(ext_dir) if not use_uc and os.path.exists(os.path.realpath(ext_dir)): with suppress(Exception): driver.webextension.install(os.path.realpath(ext_dir)) driver._is_using_auth = False if proxy_auth: driver._is_using_auth = True if not use_uc and os.path.exists(proxy_helper.PROXY_DIR_PATH): with suppress(Exception): driver.webextension.install(proxy_helper.PROXY_DIR_PATH) # Proxy needs a moment to load in Manifest V3 if use_uc: time.sleep(0.14) else: time.sleep(0.28) return driver @decorators.rate_limited(4) def requests_get(url, proxy_string=None): import requests protocol = "http" proxies = None if proxy_string: if proxy_string.endswith(":443"): protocol = "https" elif "socks4" in proxy_string: protocol = "socks4" elif "socks5" in proxy_string: protocol = "socks5" proxies = {protocol: proxy_string} response = None try: response = requests.get(url, proxies=proxies, timeout=1.25) except Exception: # Prevent SSLCertVerificationError / CERTIFICATE_VERIFY_FAILED url = url.replace("https://", "http://") time.sleep(0.04) response = requests.get(url, proxies=proxies, timeout=2.75) return response def get_latest_chromedriver_version(): from seleniumbase.console_scripts import sb_install return sb_install.get_latest_stable_chromedriver_version() def chromedriver_on_path(): paths = os.environ["PATH"].split(os.pathsep) for path in paths: if ( not IS_WINDOWS and os.path.exists(os.path.join(path, "chromedriver")) ): return os.path.join(path, "chromedriver") elif ( IS_WINDOWS and os.path.exists(os.path.join(path, "chromedriver.exe")) ): return os.path.join(path, "chromedriver.exe") return None def get_uc_driver_version(full=False, local_uc_driver=None): if not local_uc_driver: local_uc_driver = LOCAL_UC_DRIVER uc_driver_version = None if os.path.exists(local_uc_driver): with suppress(Exception): output = subprocess.check_output( '"%s" --version' % local_uc_driver, shell=True ) if IS_WINDOWS: output = output.decode("latin1") else: output = output.decode("utf-8") full_version = output.split(" ")[1] output = output.split(" ")[1].split(".")[0] if int(output) >= 72: if full: uc_driver_version = full_version else: uc_driver_version = output return uc_driver_version def find_chromedriver_version_to_use(use_version, driver_version): # Note: https://chromedriver.chromium.org/downloads stops at 114. # Future drivers are part of the Chrome-for-Testing collection. if ( driver_version and str(driver_version).split(".")[0].isdigit() and int(str(driver_version).split(".")[0]) >= 72 ): use_version = str(driver_version) elif driver_version and not str(driver_version).split(".")[0].isdigit(): from seleniumbase.console_scripts import sb_install driver_version = driver_version.lower() if driver_version == "stable" or driver_version == "latest": use_version = sb_install.get_latest_stable_chromedriver_version() elif driver_version == "beta": use_version = sb_install.get_latest_beta_chromedriver_version() elif driver_version == "dev": use_version = sb_install.get_latest_dev_chromedriver_version() elif driver_version == "canary": use_version = sb_install.get_latest_canary_chromedriver_version() elif driver_version == "previous" or driver_version == "latest-1": use_version = sb_install.get_latest_stable_chromedriver_version() use_version = str(int(use_version.split(".")[0]) - 1) elif driver_version == "mlatest": if use_version.split(".")[0].isdigit(): major = use_version.split(".")[0] if int(major) >= 115: use_version = ( sb_install.get_cft_latest_version_from_milestone(major) ) return use_version def find_edgedriver_version_to_use(use_version, driver_version): if ( driver_version and str(driver_version).split(".")[0].isdigit() and int(str(driver_version).split(".")[0]) >= 80 ): use_version = str(driver_version) return use_version def has_captcha(text): if ( "403 Forbidden" in text or "Permission Denied" in text or 'id="challenge-error-text"' in text or "/challenge-platform/h/b/" in text or "Just a moment..." in text or 'action="/?__cf_chl_f_tk' in text or 'id="challenge-widget-' in text or 'src="chromedriver.js"' in text or "com/recaptcha/api.js" in text or 'class="g-recaptcha"' in text or 'content="Pixelscan"' in text or 'id="challenge-form"' in text or "window._cf_chl_opt" in text or "/turnstile/" in text ): return True return False def __is_cdp_swap_needed(driver): """If the driver is disconnected, use a CDP method when available.""" return shared_utils.is_cdp_swap_needed(driver) def uc_execute_cdp_cmd(driver, *args, **kwargs): if not driver.is_connected(): driver.connect() return driver.default_execute_cdp_cmd(*args, **kwargs) def updated_get(driver, url): if url and ":" not in url and "." in url: url = "https:" + url driver.default_get(url) def uc_special_open_if_cf( driver, url, proxy_string=None, mobile_emulator=None, device_width=None, device_height=None, device_pixel_ratio=None, ): if url and ":" not in url and "." in url: url = "https:" + url if url.startswith("http:") or url.startswith("https:"): special = False with suppress(Exception): req_get = requests_get(url, proxy_string) status_str = str(req_get.status_code) if ( status_str.startswith("3") or status_str.startswith("4") or status_str.startswith("5") or has_captcha(req_get.text) ): special = True if status_str == "403" or status_str == "429": time.sleep(0.06) # Forbidden / Blocked! (Wait first!) if special and not hasattr(driver, "cdp_base"): time.sleep(0.05) with driver: driver.execute_script('window.open("%s","_blank");' % url) driver.close() if mobile_emulator: page_actions.switch_to_window( driver, driver.window_handles[-1], 2 ) uc_metrics = {} if ( isinstance(device_width, int) and isinstance(device_height, int) and isinstance(device_pixel_ratio, (int, float)) ): uc_metrics["width"] = device_width uc_metrics["height"] = device_height uc_metrics["pixelRatio"] = device_pixel_ratio else: uc_metrics["width"] = constants.Mobile.WIDTH uc_metrics["height"] = constants.Mobile.HEIGHT uc_metrics["pixelRatio"] = constants.Mobile.RATIO set_device_metrics_override = dict( { "width": uc_metrics["width"], "height": uc_metrics["height"], "deviceScaleFactor": uc_metrics["pixelRatio"], "mobile": True } ) with suppress(Exception): driver.execute_cdp_cmd( 'Emulation.setDeviceMetricsOverride', set_device_metrics_override ) if not mobile_emulator: page_actions.switch_to_window( driver, driver.window_handles[-1], 2 ) else: driver.default_get(url) # The original one else: driver.default_get(url) # The original one return None def uc_open(driver, url): url = shared_utils.fix_url_as_needed(url) if __is_cdp_swap_needed(driver): driver.cdp.get(url) time.sleep(0.3) return if (url.startswith("http:") or url.startswith("https:")): with driver: script = 'window.location.href = "%s";' % url js_utils.call_me_later(driver, script, 5) else: driver.default_get(url) # The original one return None def uc_open_with_tab(driver, url): url = shared_utils.fix_url_as_needed(url) if __is_cdp_swap_needed(driver): driver.cdp.get(url) time.sleep(0.3) return if (url.startswith("http:") or url.startswith("https:")): if not hasattr(driver, "cdp_base"): with driver: driver.execute_script('window.open("%s","_blank");' % url) driver.close() else: driver.cdp.open(url) page_actions.switch_to_window(driver, driver.window_handles[-1], 2) else: driver.default_get(url) # The original one return None def uc_open_with_reconnect(driver, url, reconnect_time=None): """Open a url, disconnect chromedriver, wait, and reconnect.""" if ( hasattr(sb_config, "_cdp_browser") and sb_config._cdp_browser in ["comet", "opera", "atlas"] ): if not __is_cdp_swap_needed(driver): if not driver.current_url.startswith( ("about", "data", "chrome") ): driver.get("about:blank") uc_activate_cdp_mode(driver, url) else: driver.cdp.open(url) return url = shared_utils.fix_url_as_needed(url) if __is_cdp_swap_needed(driver): driver.cdp.get(url) time.sleep(0.3) return if not reconnect_time: reconnect_time = constants.UC.RECONNECT_TIME if (url.startswith("http:") or url.startswith("https:")): script = 'window.open("%s","_blank");' % url if not hasattr(driver, "cdp_base"): driver.execute_script(script) time.sleep(0.05) driver.close() else: driver.cdp.open(url) if reconnect_time == "disconnect": driver.disconnect() time.sleep(0.008) else: driver.reconnect(reconnect_time) time.sleep(0.004) try: page_actions.switch_to_window( driver, driver.window_handles[-1], 2 ) except InvalidSessionIdException: time.sleep(0.05) page_actions.switch_to_window( driver, driver.window_handles[-1], 2 ) else: driver.default_get(url) # The original one return None def uc_open_with_cdp_mode(driver, url=None, **kwargs): """Activate CDP Mode with the URL and kwargs.""" import asyncio from seleniumbase.undetected.cdp_driver import cdp_util current_url = None try: current_url = driver.current_url except Exception: driver.connect() current_url = driver.current_url url_protocol = current_url.split(":")[0] driver.disconnect() cdp_details = driver._get_cdp_details() cdp_host = cdp_details[1].split("://")[1].split(":")[0] cdp_port = int(cdp_details[1].split("://")[1].split(":")[1].split("/")[0]) url = shared_utils.fix_url_as_needed(url) url_protocol = url.split(":")[0] safe_url = True if url_protocol not in ["about", "data", "chrome"]: safe_url = False if ( getattr(driver, "_is_using_cdp", None) and getattr(driver, "cdp", None) and hasattr(driver.cdp, "loop") ): # CDP Mode was already initialized driver.cdp.open(url, **kwargs) if not safe_url: time.sleep(constants.UC.CDP_MODE_OPEN_WAIT) if IS_WINDOWS: time.sleep(constants.UC.EXTRA_WINDOWS_WAIT) else: time.sleep(0.012) return headless = False headed = None xvfb = None xvfb_metrics = None binary_location = None if hasattr(sb_config, "headless"): headless = sb_config.headless if hasattr(sb_config, "headed"): headed = sb_config.headed if hasattr(sb_config, "xvfb"): xvfb = sb_config.xvfb if hasattr(sb_config, "xvfb_metrics"): xvfb_metrics = sb_config.xvfb_metrics if hasattr(sb_config, "binary_location"): binary_location = sb_config.binary_location loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) driver.cdp_base = loop.run_until_complete( cdp_util.start( host=cdp_host, port=cdp_port, headless=headless, headed=headed, xvfb=xvfb, xvfb_metrics=xvfb_metrics, browser_executable_path=binary_location, mobile=getattr(sb_config, "_cdp_mobile_mode", None), ) ) loop.run_until_complete(driver.cdp_base.wait(0)) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) if ( "chrome-extension://" in str(driver.cdp_base.main_tab) and len(driver.cdp_base.tabs) >= 2 ): with suppress(Exception): loop.run_until_complete(driver.cdp_base.main_tab.close()) for tab in driver.cdp_base.tabs[-1::-1]: if "chrome-extension://" not in str(tab): with gui_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.PYAUTOGUILOCK ) loop.run_until_complete(tab.activate()) break page_tab = None if "chrome-extension://" not in str(driver.cdp_base.tabs[-1]): page_tab = driver.cdp_base.tabs[-1] else: for tab in driver.cdp_base.tabs: if "chrome-extension://" not in str(tab): page_tab = tab break if page_tab: loop.run_until_complete(page_tab.aopen()) with gui_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.PYAUTOGUILOCK ) loop.run_until_complete(page_tab.activate()) loop.run_until_complete(driver.cdp_base.update_targets()) page = loop.run_until_complete( driver.cdp_base.get(url, **kwargs) ) with gui_lock: with suppress(Exception): shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK) loop.run_until_complete(page.activate()) loop.run_until_complete(page.wait()) if not safe_url: time.sleep(constants.UC.CDP_MODE_OPEN_WAIT) if IS_WINDOWS: time.sleep(constants.UC.EXTRA_WINDOWS_WAIT) else: time.sleep(0.012) cdp = types.SimpleNamespace() CDPM = sb_cdp.CDPMethods(loop, page, driver) cdp.get = CDPM.get cdp.open = CDPM.open cdp.reload = CDPM.reload cdp.refresh = CDPM.refresh cdp.add_handler = CDPM.add_handler cdp.get_event_loop = CDPM.get_event_loop cdp.get_rd_host = CDPM.get_rd_host cdp.get_rd_port = CDPM.get_rd_port cdp.get_rd_url = CDPM.get_rd_url cdp.get_endpoint_url = CDPM.get_endpoint_url cdp.get_websocket_url = CDPM.get_websocket_url cdp.get_port = CDPM.get_port cdp.find_element = CDPM.find_element cdp.find = CDPM.find_element cdp.locator = CDPM.find_element cdp.find_element_by_text = CDPM.find_element_by_text cdp.find_all = CDPM.find_all cdp.find_elements_by_text = CDPM.find_elements_by_text cdp.select = CDPM.select cdp.select_all = CDPM.select_all cdp.find_elements = CDPM.find_elements cdp.find_visible_elements = CDPM.find_visible_elements cdp.click_nth_element = CDPM.click_nth_element cdp.click_nth_visible_element = CDPM.click_nth_visible_element cdp.click_link = CDPM.click_link cdp.go_back = CDPM.go_back cdp.go_forward = CDPM.go_forward cdp.get_navigation_history = CDPM.get_navigation_history cdp.tile_windows = CDPM.tile_windows cdp.grant_permissions = CDPM.grant_permissions cdp.grant_all_permissions = CDPM.grant_all_permissions cdp.reset_permissions = CDPM.reset_permissions cdp.get_all_cookies = CDPM.get_all_cookies cdp.set_all_cookies = CDPM.set_all_cookies cdp.save_cookies = CDPM.save_cookies cdp.load_cookies = CDPM.load_cookies cdp.clear_cookies = CDPM.clear_cookies cdp.sleep = CDPM.sleep cdp.bring_active_window_to_front = CDPM.bring_active_window_to_front cdp.bring_to_front = CDPM.bring_active_window_to_front cdp.get_active_element = CDPM.get_active_element cdp.get_active_element_css = CDPM.get_active_element_css cdp.click = CDPM.click cdp.click_active_element = CDPM.click_active_element cdp.click_if_visible = CDPM.click_if_visible cdp.click_visible_elements = CDPM.click_visible_elements cdp.click_with_offset = CDPM.click_with_offset cdp.mouse_click = CDPM.mouse_click cdp.get_parent = CDPM.get_parent cdp.remove_element = CDPM.remove_element cdp.remove_from_dom = CDPM.remove_from_dom cdp.remove_elements = CDPM.remove_elements cdp.send_keys = CDPM.send_keys cdp.press_keys = CDPM.press_keys cdp.type = CDPM.type cdp.clear_input = CDPM.clear_input cdp.clear = CDPM.clear_input cdp.set_value = CDPM.set_value cdp.submit = CDPM.submit cdp.evaluate = CDPM.evaluate cdp.execute_script = CDPM.execute_script cdp.js_dumps = CDPM.js_dumps cdp.maximize = CDPM.maximize cdp.minimize = CDPM.minimize cdp.medimize = CDPM.medimize cdp.set_window_rect = CDPM.set_window_rect cdp.reset_window_size = CDPM.reset_window_size cdp.activate_messenger = CDPM.activate_messenger cdp.set_messenger_theme = CDPM.set_messenger_theme cdp.post_message = CDPM.post_message cdp.set_locale = CDPM.set_locale cdp.set_local_storage_item = CDPM.set_local_storage_item cdp.set_session_storage_item = CDPM.set_session_storage_item cdp.set_attributes = CDPM.set_attributes cdp.is_attribute_present = CDPM.is_attribute_present cdp.is_online = CDPM.is_online cdp.solve_captcha = CDPM.solve_captcha cdp.click_captcha = CDPM.click_captcha cdp.gui_press_key = CDPM.gui_press_key cdp.gui_press_keys = CDPM.gui_press_keys cdp.gui_write = CDPM.gui_write cdp.gui_click_x_y = CDPM.gui_click_x_y cdp.gui_click_element = CDPM.gui_click_element cdp.gui_click_with_offset = CDPM.gui_click_with_offset cdp.gui_click_captcha = CDPM.gui_click_captcha cdp.gui_drag_drop_points = CDPM.gui_drag_drop_points cdp.gui_drag_and_drop = CDPM.gui_drag_and_drop cdp.gui_click_and_hold = CDPM.gui_click_and_hold cdp.gui_hover_x_y = CDPM.gui_hover_x_y cdp.gui_hover_element = CDPM.gui_hover_element cdp.gui_hover_and_click = CDPM.gui_hover_and_click cdp.hover_element = CDPM.hover_element cdp.hover_and_click = CDPM.hover_and_click cdp.internalize_links = CDPM.internalize_links cdp.open_new_window = CDPM.open_new_window cdp.switch_to_window = CDPM.switch_to_window cdp.switch_to_newest_window = CDPM.switch_to_newest_window cdp.open_new_tab = CDPM.open_new_tab cdp.switch_to_tab = CDPM.switch_to_tab cdp.switch_to_newest_tab = CDPM.switch_to_newest_tab cdp.close_active_tab = CDPM.close_active_tab cdp.get_active_tab = CDPM.get_active_tab cdp.get_tabs = CDPM.get_tabs cdp.get_window = CDPM.get_window cdp.get_element_attributes = CDPM.get_element_attributes cdp.get_element_attribute = CDPM.get_element_attribute cdp.get_attribute = CDPM.get_attribute cdp.get_element_html = CDPM.get_element_html cdp.get_element_rect = CDPM.get_element_rect cdp.get_element_size = CDPM.get_element_size cdp.get_element_position = CDPM.get_element_position cdp.get_gui_element_rect = CDPM.get_gui_element_rect cdp.get_gui_element_center = CDPM.get_gui_element_center cdp.get_html = CDPM.get_html cdp.get_page_source = CDPM.get_page_source cdp.get_user_agent = CDPM.get_user_agent cdp.get_cookie_string = CDPM.get_cookie_string cdp.get_locale_code = CDPM.get_locale_code cdp.get_local_storage_item = CDPM.get_local_storage_item cdp.get_session_storage_item = CDPM.get_session_storage_item cdp.get_text = CDPM.get_text cdp.get_title = CDPM.get_title cdp.get_page_title = CDPM.get_title cdp.get_current_url = CDPM.get_current_url cdp.get_origin = CDPM.get_origin cdp.get_nested_element = CDPM.get_nested_element cdp.get_document = CDPM.get_document cdp.get_flattened_document = CDPM.get_flattened_document cdp.get_screen_rect = CDPM.get_screen_rect cdp.get_window_rect = CDPM.get_window_rect cdp.get_window_size = CDPM.get_window_size cdp.get_mfa_code = CDPM.get_mfa_code cdp.nested_click = CDPM.nested_click cdp.select_option_by_text = CDPM.select_option_by_text cdp.select_option_by_index = CDPM.select_option_by_index cdp.select_option_by_value = CDPM.select_option_by_value cdp.enter_mfa_code = CDPM.enter_mfa_code cdp.flash = CDPM.flash cdp.highlight = CDPM.highlight cdp.focus = CDPM.focus cdp.highlight_overlay = CDPM.highlight_overlay cdp.get_window_position = CDPM.get_window_position cdp.check_if_unchecked = CDPM.check_if_unchecked cdp.uncheck_if_checked = CDPM.uncheck_if_checked cdp.select_if_unselected = CDPM.select_if_unselected cdp.unselect_if_selected = CDPM.unselect_if_selected cdp.is_checked = CDPM.is_checked cdp.is_selected = CDPM.is_selected cdp.is_element_present = CDPM.is_element_present cdp.is_element_visible = CDPM.is_element_visible cdp.is_text_visible = CDPM.is_text_visible cdp.is_exact_text_visible = CDPM.is_exact_text_visible cdp.wait_for_text = CDPM.wait_for_text cdp.wait_for_text_not_visible = CDPM.wait_for_text_not_visible cdp.wait_for_element_visible = CDPM.wait_for_element_visible cdp.wait_for_element = CDPM.wait_for_element cdp.wait_for_element_not_visible = CDPM.wait_for_element_not_visible cdp.wait_for_element_absent = CDPM.wait_for_element_absent cdp.wait_for_any_of_elements_visible = ( CDPM.wait_for_any_of_elements_visible ) cdp.wait_for_any_of_elements_present = ( CDPM.wait_for_any_of_elements_present ) cdp.assert_any_of_elements_visible = CDPM.assert_any_of_elements_visible cdp.assert_any_of_elements_present = CDPM.assert_any_of_elements_present cdp.assert_element = CDPM.assert_element cdp.assert_element_visible = CDPM.assert_element_visible cdp.assert_element_present = CDPM.assert_element_present cdp.assert_element_absent = CDPM.assert_element_absent cdp.assert_element_not_visible = CDPM.assert_element_not_visible cdp.assert_element_attribute = CDPM.assert_element_attribute cdp.assert_title = CDPM.assert_title cdp.assert_title_contains = CDPM.assert_title_contains cdp.assert_url = CDPM.assert_url cdp.assert_url_contains = CDPM.assert_url_contains cdp.assert_text = CDPM.assert_text cdp.assert_exact_text = CDPM.assert_exact_text cdp.assert_text_not_visible = CDPM.assert_text_not_visible cdp.assert_true = CDPM.assert_true cdp.assert_false = CDPM.assert_false cdp.assert_equal = CDPM.assert_equal cdp.assert_not_equal = CDPM.assert_not_equal cdp.assert_in = CDPM.assert_in cdp.assert_not_in = CDPM.assert_not_in cdp.scroll_into_view = CDPM.scroll_into_view cdp.scroll_to_y = CDPM.scroll_to_y cdp.scroll_by_y = CDPM.scroll_by_y cdp.scroll_to_top = CDPM.scroll_to_top cdp.scroll_to_bottom = CDPM.scroll_to_bottom cdp.scroll_up = CDPM.scroll_up cdp.scroll_down = CDPM.scroll_down cdp.save_page_source = CDPM.save_page_source cdp.save_as_html = CDPM.save_as_html cdp.save_screenshot = CDPM.save_screenshot cdp.print_to_pdf = CDPM.print_to_pdf cdp.save_as_pdf = CDPM.save_as_pdf cdp.page = page # async world cdp.driver = driver.cdp_base # async world cdp.tab = cdp.page # shortcut (original) cdp.browser = driver.cdp_base # shortcut (original) cdp.util = cdp_util # shortcut (original) core_items = types.SimpleNamespace() core_items.browser = cdp.browser core_items.tab = cdp.tab core_items.util = cdp.util cdp._swap_driver = CDPM._swap_driver cdp.core = core_items cdp.loop = cdp.get_event_loop() driver.cdp = cdp driver.solve_captcha = CDPM.solve_captcha driver.click_captcha = CDPM.click_captcha driver.find_element_by_text = CDPM.find_element_by_text driver._is_using_cdp = True if ( getattr(sb_config, "_cdp_proxy", None) and "@" in sb_config._cdp_proxy ): time.sleep(0.077) loop.run_until_complete(page.wait(0.25)) time.sleep(0.022) def uc_activate_cdp_mode(driver, url=None, **kwargs): uc_open_with_cdp_mode(driver, url=url, **kwargs) def uc_open_with_disconnect(driver, url, timeout=None): """Open a url and disconnect chromedriver. Then waits for the duration of the timeout. Note: You can't perform Selenium actions again until after you've called driver.connect().""" url = shared_utils.fix_url_as_needed(url) if __is_cdp_swap_needed(driver): driver.cdp.get(url) time.sleep(0.3) return if not driver.is_connected(): driver.connect() if (url.startswith("http:") or url.startswith("https:")): script = 'window.open("%s","_blank");' % url driver.execute_script(script) time.sleep(0.05) driver.close() driver.disconnect() min_timeout = 0.008 if timeout and not str(timeout).replace(".", "", 1).isdigit(): timeout = min_timeout if not timeout or timeout < min_timeout: timeout = min_timeout time.sleep(timeout) else: driver.default_get(url) # The original one return None def uc_click( driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT, reconnect_time=None, ): if __is_cdp_swap_needed(driver): driver.cdp.click(selector) return with suppress(Exception): rct = float(by) # Add shortcut: driver.uc_click(selector, RCT) if not reconnect_time: reconnect_time = rct by = "css selector" element = driver.wait_for_selector(selector, by=by, timeout=timeout) tag_name = element.tag_name if not tag_name == "span" and not tag_name == "input": # Must be "visible" element = driver.wait_for_element(selector, by=by, timeout=timeout) try: element.uc_click( driver, selector, by=by, reconnect_time=reconnect_time, tag_name=tag_name, ) except ElementClickInterceptedException: time.sleep(0.16) driver.js_click(selector, by=by, timeout=timeout) if not reconnect_time: driver.reconnect(0.1) else: driver.reconnect(reconnect_time) def verify_pyautogui_has_a_headed_browser(driver): """PyAutoGUI requires a headed browser so that it can focus on the correct element when performing actions.""" if getattr(driver, "_is_hidden", None): raise Exception( "PyAutoGUI can't be used in headless mode!" ) def __install_pyautogui_if_missing(): try: import pyautogui with suppress(Exception): use_pyautogui_ver = constants.PyAutoGUI.VER u_pv = shared_utils.make_version_tuple(use_pyautogui_ver) pv = shared_utils.make_version_tuple(pyautogui.__version__) if pv < u_pv: del pyautogui # To get newer ver shared_utils.pip_install("pyautogui", version="Latest") import pyautogui except Exception: print("\nPyAutoGUI required! Installing now...") shared_utils.pip_install("pyautogui", version="Latest") try: import pyautogui except Exception: if ( IS_LINUX and hasattr(sb_config, "xvfb") and hasattr(sb_config, "headed") and hasattr(sb_config, "headless") and hasattr(sb_config, "headless2") and (not sb_config.headed or sb_config.xvfb) and not (sb_config.headless or sb_config.headless2) ): from sbvirtualdisplay import Display xvfb_width = 1366 xvfb_height = 768 if ( getattr(sb_config, "_xvfb_width", None) and isinstance(sb_config._xvfb_width, int) and getattr(sb_config, "_xvfb_height", None) and isinstance(sb_config._xvfb_height, int) ): xvfb_width = sb_config._xvfb_width xvfb_height = sb_config._xvfb_height if xvfb_width < 1024: xvfb_width = 1024 sb_config._xvfb_width = xvfb_width if xvfb_height < 768: xvfb_height = 768 sb_config._xvfb_height = xvfb_height with suppress(Exception): _xvfb_display = Display( visible=True, size=(xvfb_width, xvfb_height), backend="xvfb", use_xauth=True, ) if "--debug-display" in sys.argv: print( "Starting VDisplay from browser_launcher: (%s, %s)" % (xvfb_width, xvfb_height) ) _xvfb_display.start() sb_config._virtual_display = _xvfb_display sb_config.headless_active = True if ( getattr(sb_config, "reuse_session", None) and hasattr(sb_config, "_vd_list") and isinstance(sb_config._vd_list, list) ): sb_config._vd_list.append(_xvfb_display) def install_pyautogui_if_missing(driver): verify_pyautogui_has_a_headed_browser(driver) pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) try: with pip_find_lock: pass except Exception: # Since missing permissions, skip the locks __install_pyautogui_if_missing() return with pip_find_lock: # Prevent issues with multiple processes with suppress(Exception): shared_utils.make_writable(constants.PipInstall.FINDLOCK) __install_pyautogui_if_missing() def get_configured_pyautogui(pyautogui_copy): if ( IS_LINUX and hasattr(pyautogui_copy, "_pyautogui_x11") and "DISPLAY" in os.environ.keys() ): if ( getattr(sb_config, "_pyautogui_x11_display", None) and hasattr(pyautogui_copy._pyautogui_x11, "_display") and ( sb_config._pyautogui_x11_display == pyautogui_copy._pyautogui_x11._display ) ): pass else: import Xlib.display pyautogui_copy._pyautogui_x11._display = ( Xlib.display.Display(os.environ['DISPLAY']) ) sb_config._pyautogui_x11_display = ( pyautogui_copy._pyautogui_x11._display ) return pyautogui_copy def uc_gui_press_key(driver, key): install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: pyautogui.press(key) def uc_gui_press_keys(driver, keys): install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: for key in keys: pyautogui.press(key) def uc_gui_write(driver, text): install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: pyautogui.write(text) def get_gui_element_position(driver, selector): if __is_cdp_swap_needed(driver): element_rect = driver.cdp.get_gui_element_rect(selector) return (element_rect["x"], element_rect["y"]) element = driver.wait_for_element_present(selector, timeout=3) element_rect = element.rect window_rect = driver.get_window_rect() window_bottom_y = window_rect["y"] + window_rect["height"] viewport_height = driver.execute_script("return window.innerHeight;") viewport_x = window_rect["x"] + element_rect["x"] viewport_y = window_bottom_y - viewport_height + element_rect["y"] y_scroll_offset = driver.execute_script("return window.pageYOffset;") viewport_y = viewport_y - y_scroll_offset return (viewport_x, viewport_y) def _uc_gui_click_x_y(driver, x, y, timeframe=0.25, uc_lock=False): install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() if x < 0 or y < 0 or x > screen_width or y > screen_height: raise Exception( "PyAutoGUI cannot click on point (%s, %s)" " outside screen. (Width: %s, Height: %s)" % (x, y, screen_width, screen_height) ) if uc_lock: gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad) if timeframe >= 0.25: time.sleep(0.056) # Wait if moving at human-speed if "--debug" in sys.argv: print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y)) pyautogui.click(x=x, y=y) else: # Called from a method where the gui_lock is already active pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad) if timeframe >= 0.25: time.sleep(0.056) # Wait if moving at human-speed if "--debug" in sys.argv: print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y)) pyautogui.click(x=x, y=y) def uc_gui_click_x_y(driver, x, y, timeframe=0.25): gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) connected = True width_ratio = 1.0 if IS_WINDOWS: connected = driver.is_connected() if ( not connected and not getattr(sb_config, "_saved_width_ratio", None) and not __is_cdp_swap_needed(driver) ): driver.reconnect(0.1) if IS_WINDOWS and not __is_cdp_swap_needed(driver): window_rect = driver.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] scr_width = pyautogui.size().width driver.maximize_window() win_width = driver.get_window_size()["width"] width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio driver.minimize_window() driver.set_window_rect(win_x, win_y, width, height) elif IS_WINDOWS and __is_cdp_swap_needed(driver): window_rect = driver.cdp.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] scr_width = pyautogui.size().width driver.cdp.maximize() win_width = driver.cdp.get_window_size()["width"] width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio driver.cdp.minimize() driver.cdp.set_window_rect(win_x, win_y, width, height) if IS_WINDOWS: x = x * (width_ratio + 0.03) y = y * (width_ratio - 0.03) _uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False) return with suppress(Exception): page_actions.switch_to_window( driver, driver.current_window_handle, 2, uc_lock=False ) _uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False) def _on_a_cf_turnstile_page(driver): source = driver.get_page_source() if ( ( 'data-callback="onCaptchaSuccess"' in source and 'title="reCAPTCHA"' not in source and 'id="recaptcha-token"' not in source ) or "/challenge-platform/h/b/" in source or 'id="challenge-widget-' in source or "challenges.cloudf" in source or "cf-turnstile-" in source ): return True return False def _on_a_g_recaptcha_page(driver): source = driver.get_page_source() if ( 'id="recaptcha-token"' in source or 'title="reCAPTCHA"' in source ): return True return False def _uc_gui_click_captcha( driver, frame="iframe", retry=False, blind=False, ctype=None, ): cdp_mode_on_at_start = __is_cdp_swap_needed(driver) if cdp_mode_on_at_start: return driver.cdp.gui_click_captcha() _on_a_captcha_page = None if ctype == "cf_t": if not _on_a_cf_turnstile_page(driver): return False else: _on_a_captcha_page = _on_a_cf_turnstile_page elif ctype == "g_rc": if not _on_a_g_recaptcha_page(driver): return False else: _on_a_captcha_page = _on_a_g_recaptcha_page else: if _on_a_g_recaptcha_page(driver): ctype = "g_rc" _on_a_captcha_page = _on_a_g_recaptcha_page elif _on_a_cf_turnstile_page(driver): ctype = "cf_t" _on_a_captcha_page = _on_a_cf_turnstile_page else: return False install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) i_x = None i_y = None x = None y = None visible_iframe = True gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes needs_switch = False width_ratio = 1.0 is_in_frame = js_utils.is_in_frame(driver) if is_in_frame and driver.is_element_present("#challenge-stage"): driver.switch_to.parent_frame() needs_switch = True is_in_frame = js_utils.is_in_frame(driver) if not is_in_frame: # Make sure the window is on top if __is_cdp_swap_needed(driver): driver.cdp.bring_active_window_to_front() else: page_actions.switch_to_window( driver, driver.current_window_handle, 2, uc_lock=False ) if IS_WINDOWS and not __is_cdp_swap_needed(driver): window_rect = driver.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] scr_width = pyautogui.size().width driver.maximize_window() win_width = driver.get_window_size()["width"] width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio driver.minimize_window() driver.set_window_rect(win_x, win_y, width, height) elif IS_WINDOWS and __is_cdp_swap_needed(driver): window_rect = driver.cdp.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] scr_width = pyautogui.size().width driver.cdp.maximize() win_width = driver.cdp.get_window_size()["width"] width_ratio = round(float(scr_width) / float(win_width), 2) + 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio driver.cdp.minimize() driver.cdp.set_window_rect(win_x, win_y, width, height) if ctype == "cf_t": if ( driver.is_element_present(".cf-turnstile-wrapper iframe") or driver.is_element_present( '[data-callback="onCaptchaSuccess"] iframe' ) ): pass else: visible_iframe = False if ( frame != "iframe" and driver.is_element_present( "%s .cf-turnstile-wrapper" % frame ) ): frame = "%s .cf-turnstile-wrapper" % frame elif ( frame != "iframe" and driver.is_element_present( '%s [name*="cf-turnstile"]' % frame ) and driver.is_element_present("%s div" % frame) ): frame = "%s div" % frame elif driver.is_element_present("#challenge-form div > div"): frame = "#challenge-form div > div" elif ( driver.is_element_present( '[style="display: grid;"] div div' ) ): frame = '[style="display: grid;"] div div' elif ( driver.is_element_present('[name*="cf-turnstile-"]') and driver.is_element_present( ".spacer + div div:not([class])" ) ): frame = '.spacer + div div:not([class])' elif ( driver.is_element_present(".spacer div:not([class])") ): frame = ".spacer div:not([class])" elif ( driver.is_element_present( '[data-testid*="challenge-"] div' ) ): frame = '[data-testid*="challenge-"] div' elif driver.is_element_present( "div#turnstile-widget div:not([class])" ): frame = "div#turnstile-widget div:not([class])" elif driver.is_element_present( "ngx-turnstile div:not([class])" ): frame = "ngx-turnstile div:not([class])" elif driver.is_element_present( 'form div:not([class]):has(input[name*="cf-turn"])' ): frame = 'form div:not([class]):has(input[name*="cf-turn"])' elif ( driver.is_element_present('[src*="/turnstile/"]') and driver.is_element_present("form div:not(:has(*))") ): frame = "form div:not(:has(*))" elif ( driver.is_element_present('[src*="/turnstile/"]') and driver.is_element_present( "body > div#check > div:not([class])" ) ): frame = "body > div#check > div:not([class])" elif driver.is_element_present(".cf-turnstile-wrapper"): frame = ".cf-turnstile-wrapper" elif driver.is_element_present('[class="cf-turnstile"]'): frame = '[class="cf-turnstile"]' elif driver.is_element_present( '[id*="turnstile"] div:not([class])' ): frame = '[id*="turnstile"] div:not([class])' elif driver.is_element_present( '[class*="turnstile"] div:not([class])' ): frame = '[class*="turnstile"] div:not([class])' elif driver.is_element_present( '[data-callback="onCaptchaSuccess"]' ): frame = '[data-callback="onCaptchaSuccess"]' elif driver.is_element_present( "div:not([class]):not([id]):not([aria-label]) > " "div:not([class]):not([id]):not([aria-label])" ): frame = ( "div:not([class]):not([id]):not([aria-label]) > " "div:not([class]):not([id]):not([aria-label])" ) else: return False if ( driver.is_element_present("form") and ( driver.is_element_present('form[class*="center"]') or driver.is_element_present('form[class*="right"]') or driver.is_element_present('form div[class*="center"]') or driver.is_element_present('form div[class*="right"]') ) ): script = ( """var $elements = document.querySelectorAll( 'form[class], form div[class]'); var index = 0, length = $elements.length; for(; index < length; index++){ the_class = $elements[index].getAttribute('class'); new_class = the_class.replaceAll('center', 'left'); new_class = new_class.replaceAll('right', 'left'); $elements[index].setAttribute('class', new_class);}""" ) if __is_cdp_swap_needed(driver): driver.cdp.evaluate(script) else: driver.execute_script(script) elif ( driver.is_element_present("form") and ( driver.is_element_present('form div[style*="center"]') or driver.is_element_present('form div[style*="right"]') ) ): script = ( """var $elements = document.querySelectorAll( 'form[style], form div[style]'); var index = 0, length = $elements.length; for(; index < length; index++){ the_style = $elements[index].getAttribute('style'); new_style = the_style.replaceAll('center', 'left'); new_style = new_style.replaceAll('right', 'left'); $elements[index].setAttribute('style', new_style);}""" ) if __is_cdp_swap_needed(driver): driver.cdp.evaluate(script) else: driver.execute_script(script) elif ( driver.is_element_present( 'form [id*="turnstile"] div:not([class])' ) or driver.is_element_present( 'form [class*="turnstile"] div:not([class])' ) ): script = ( """var $elements = document.querySelectorAll( 'form [id*="turnstile"]'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].setAttribute('align', 'left');} var $elements = document.querySelectorAll( 'form [class*="turnstile"]'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].setAttribute('align', 'left');}""" ) if __is_cdp_swap_needed(driver): driver.cdp.evaluate(script) else: driver.execute_script(script) elif ( driver.is_element_present( '[style*="text-align: center;"] div:not([class])' ) ): script = ( """var $elements = document.querySelectorAll( '[style*="text-align: center;"]'); var index = 0, length = $elements.length; for(; index < length; index++){ the_style = $elements[index].getAttribute('style'); new_style = the_style.replaceAll('center', 'left'); $elements[index].setAttribute('style', new_style);}""" ) if __is_cdp_swap_needed(driver): driver.cdp.evaluate(script) else: driver.execute_script(script) if not is_in_frame or needs_switch: # Currently not in frame (or nested frame outside CF one) try: i_x, i_y = get_gui_element_position(driver, frame) if visible_iframe: driver.switch_to_frame(frame) except Exception: if visible_iframe: if driver.is_element_present("iframe"): i_x, i_y = get_gui_element_position(driver, "iframe") if driver.is_connected(): driver.switch_to_frame("iframe") else: return False if not i_x or not i_y: return False try: if ctype == "g_rc" and not driver.is_connected(): x = (i_x + 29) * width_ratio y = (i_y + 35) * width_ratio elif visible_iframe: selector = "span" if ctype == "g_rc": selector = "span.recaptcha-checkbox" if not driver.is_connected(): selector = "iframe" element = driver.wait_for_element_present( selector, timeout=2.5 ) x = i_x + element.rect["x"] + (element.rect["width"] / 2.0) x += 0.5 y = i_y + element.rect["y"] + (element.rect["height"] / 2.0) y += 0.5 else: x = (i_x + 28) * width_ratio if not IS_WINDOWS: y = (i_y + 32) * width_ratio else: y = (i_y + 22) * width_ratio if driver.is_connected(): driver.switch_to.default_content() except Exception: if driver.is_connected(): try: driver.switch_to.default_content() except Exception: return False if x and y: sb_config._saved_cf_x_y = (x, y) if not __is_cdp_swap_needed(driver): if driver.is_element_present(".footer .clearfix .ray-id"): driver.uc_open_with_disconnect( driver.get_current_url(), 3.8 ) else: driver.disconnect() with suppress(Exception): _uc_gui_click_x_y(driver, x, y, timeframe=0.32) if __is_cdp_swap_needed(driver): time.sleep(float(constants.UC.RECONNECT_TIME) / 2.0) return True reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6 if IS_LINUX: reconnect_time = constants.UC.RECONNECT_TIME + 0.2 if not x or not y: reconnect_time = 1 # Make it quick (it already failed) driver.reconnect(reconnect_time) caught = False if ( driver.is_element_present(".footer .clearfix .ray-id") and not driver.is_element_visible("#challenge-success-text") ): blind = True caught = True if blind: retry = True if retry and x and y and (caught or _on_a_captcha_page(driver)): with gui_lock: # Prevent issues with multiple processes # Make sure the window is on top if __is_cdp_swap_needed(driver): driver.cdp.bring_active_window_to_front() else: page_actions.switch_to_window( driver, driver.current_window_handle, 2, uc_lock=False ) if driver.is_element_present("iframe"): try: driver.switch_to_frame(frame) except Exception: try: driver.switch_to_frame("iframe") except Exception: return False checkbox_success = None if ctype == "cf_t": checkbox_success = "#success-icon" elif ctype == "g_rc": checkbox_success = "span.recaptcha-checkbox-checked" else: return False # If line is reached, ctype wasn't set if driver.is_element_visible("#success-icon"): driver.switch_to.parent_frame(checkbox_success) return True if blind: driver.uc_open_with_disconnect(driver.get_current_url(), 3.8) if __is_cdp_swap_needed(driver) and _on_a_captcha_page(driver): _uc_gui_click_x_y(driver, x, y, timeframe=0.32) else: time.sleep(0.1) else: driver.uc_open_with_reconnect(driver.get_current_url(), 3.8) if _on_a_captcha_page(driver): driver.disconnect() _uc_gui_click_x_y(driver, x, y, timeframe=0.32) if not cdp_mode_on_at_start: driver.reconnect(reconnect_time) return True def uc_gui_click_captcha(driver, frame="iframe", retry=False, blind=False): _uc_gui_click_captcha( driver, frame=frame, retry=retry, blind=blind, ctype=None, ) def uc_gui_click_rc(driver, frame="iframe", retry=False, blind=False): _uc_gui_click_captcha( driver, frame=frame, retry=retry, blind=blind, ctype="g_rc", ) def uc_gui_click_cf(driver, frame="iframe", retry=False, blind=False): _uc_gui_click_captcha( driver, frame=frame, retry=retry, blind=blind, ctype="cf_t", ) def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None): if ctype == "cf_t": if not _on_a_cf_turnstile_page(driver): return elif ctype == "g_rc": if not _on_a_g_recaptcha_page(driver): return else: if _on_a_g_recaptcha_page(driver): ctype = "g_rc" elif _on_a_cf_turnstile_page(driver): ctype = "cf_t" else: return if not driver.is_connected() and not __is_cdp_swap_needed(driver): driver.connect() time.sleep(2) install_pyautogui_if_missing(driver) import pyautogui pyautogui = get_configured_pyautogui(pyautogui) visible_iframe = True gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes needs_switch = False if not __is_cdp_swap_needed(driver): is_in_frame = js_utils.is_in_frame(driver) else: is_in_frame = False selector = "#challenge-stage" if ctype == "g_rc": selector = "#recaptcha-token" if is_in_frame and driver.is_element_present(selector): driver.switch_to.parent_frame() needs_switch = True is_in_frame = js_utils.is_in_frame(driver) if not is_in_frame and not __is_cdp_swap_needed(driver): # Make sure the window is on top page_actions.switch_to_window( driver, driver.current_window_handle, 2, uc_lock=False ) if IS_WINDOWS and hasattr(pyautogui, "getActiveWindowTitle"): py_a_g_title = pyautogui.getActiveWindowTitle() or "" window_title = driver.get_title() if not py_a_g_title.startswith(window_title): window_rect = driver.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] driver.minimize_window() driver.set_window_rect(win_x, win_y, width, height) time.sleep(0.33) tab_up_first = False if ctype == "cf_t": if ( driver.is_element_present(".cf-turnstile-wrapper iframe") or driver.is_element_present( '[data-callback="onCaptchaSuccess"] iframe' ) ): pass else: visible_iframe = False if driver.is_element_present(".cf-turnstile-wrapper"): frame = ".cf-turnstile-wrapper" elif driver.is_element_present( '[data-callback="onCaptchaSuccess"]' ): frame = '[data-callback="onCaptchaSuccess"]' elif ( driver.is_element_present('[name*="cf-turnstile-"]') and driver.is_element_present(".spacer div:not([class])") ): frame = ".spacer div:not([class])" elif ( driver.is_element_present('script[src*="challenges.c"]') and driver.is_element_present( '[data-testid*="challenge-"] div' ) ): frame = '[data-testid*="challenge-"] div' elif driver.is_element_present( 'form div:not([class]):has(input[name*="cf-turn"])' ): frame = 'form div:not([class]):has(input[name*="cf-turn"])' tab_up_first = True elif ( driver.is_element_present('[src*="/turnstile/"]') and driver.is_element_present("form div:not(:has(*))") ): frame = "form div:not(:has(*))" tab_up_first = True elif ( driver.is_element_present('[src*="/turnstile/"]') and driver.is_element_present( "body > div#check > div:not([class])" ) ): frame = "body > div#check > div:not([class])" elif driver.is_element_present( "div#turnstile-widget div:not([class])" ): frame = "div#turnstile-widget div:not([class])" elif driver.is_element_present(".cf-turnstile-wrapper"): frame = ".cf-turnstile-wrapper" elif driver.is_element_present('[class="cf-turnstile"]'): frame = '[class="cf-turnstile"]' elif driver.is_element_present( "div:not([class]) > div:not([class])" ): frame = "div:not([class]) > div:not([class])" else: return else: if ( driver.is_element_present('iframe[title="reCAPTCHA"]') and frame == "iframe" ): frame = 'iframe[title="reCAPTCHA"]' if not __is_cdp_swap_needed(driver): if not is_in_frame or needs_switch: # Currently not in frame (or nested frame outside CF one) try: if visible_iframe or ctype == "g_rc": driver.switch_to_frame(frame) except Exception: if visible_iframe or ctype == "g_rc": if driver.is_element_present("iframe"): driver.switch_to_frame("iframe") else: return try: selector = "div.cf-turnstile" if ctype == "g_rc": selector = "span#recaptcha-anchor" found_checkbox = False if tab_up_first: for i in range(10): pyautogui.hotkey("shift", "tab") time.sleep(0.027) if ctype == "g_rc": if js_utils.get_active_element_css(driver) == "body": break tab_count = 0 for i in range(34): pyautogui.press("\t") tab_count += 1 time.sleep(0.027) active_element_css = js_utils.get_active_element_css(driver) if ( active_element_css.startswith(selector) or active_element_css.endswith(" div") or (ctype == "g_rc" and "frame[name" in active_element_css) ): found_checkbox = True sb_config._saved_cf_tab_count = tab_count break time.sleep(0.02) if not found_checkbox: return except Exception: try: driver.switch_to.default_content() except Exception: return if ( ( driver.is_element_present(".footer .clearfix .ray-id") or driver.is_element_present("script[data-cf-beacon]") ) and getattr(sb_config, "_saved_cf_tab_count", None) and not __is_cdp_swap_needed(driver) ): driver.uc_open_with_disconnect(driver.current_url, 3.8) with suppress(Exception): if "--debug" in sys.argv: if sb_config._saved_cf_tab_count == 1: print(' <DEBUG> pyautogui.press("\\t")') else: print( ' <DEBUG> pyautogui.press("\\t") * %s' % sb_config._saved_cf_tab_count ) for i in range(sb_config._saved_cf_tab_count): pyautogui.press("\t") time.sleep(0.027) if "--debug" in sys.argv: print(' <DEBUG> pyautogui.press(" ")') pyautogui.press(" ") else: driver.disconnect() pyautogui.press(" ") reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6 if IS_LINUX: reconnect_time = constants.UC.RECONNECT_TIME + 0.2 driver.reconnect(reconnect_time) def _uc_gui_handle_captcha(driver, frame="iframe", ctype=None): _uc_gui_handle_captcha_(driver, frame=frame, ctype=ctype) if ( driver.is_element_present(".footer .clearfix .ray-id") and not driver.is_element_visible("#challenge-success-text") ): driver.uc_open_with_reconnect(driver.current_url, 3.8) _uc_gui_handle_captcha_(driver, frame=frame, ctype=ctype) def uc_gui_handle_captcha(driver, frame="iframe"): _uc_gui_handle_captcha(driver, frame=frame, ctype=None) def uc_gui_handle_cf(driver, frame="iframe"): _uc_gui_handle_captcha(driver, frame=frame, ctype="cf_t") def uc_gui_handle_rc(driver, frame="iframe"): _uc_gui_handle_captcha(driver, frame=frame, ctype="g_rc") def uc_switch_to_frame(driver, frame="iframe", reconnect_time=None): from selenium.webdriver.remote.webelement import WebElement if isinstance(frame, WebElement): if not reconnect_time: driver.reconnect(0.1) else: driver.reconnect(reconnect_time) driver.switch_to.frame(frame) else: iframe = driver.locator(frame) if not reconnect_time: driver.reconnect(0.1) else: driver.reconnect(reconnect_time) driver.switch_to.frame(iframe) def edgedriver_on_path(): return os.path.exists(LOCAL_EDGEDRIVER) def geckodriver_on_path(): paths = os.environ["PATH"].split(os.pathsep) for path in paths: if (not IS_WINDOWS) and os.path.exists(path + "/geckodriver"): return True elif IS_WINDOWS and os.path.exists(path + "/geckodriver.exe"): return True return False def iedriver_on_path(): paths = os.environ["PATH"].split(os.pathsep) for path in paths: if os.path.exists(path + "/IEDriverServer.exe"): return True return False def headless_iedriver_on_path(): return os.path.exists(LOCAL_HEADLESS_IEDRIVER) def get_valid_binary_names_for_browser(browser): if browser == constants.Browser.GOOGLE_CHROME: if IS_LINUX: return constants.ValidBinaries.valid_chrome_binaries_on_linux elif IS_MAC: return constants.ValidBinaries.valid_chrome_binaries_on_macos elif IS_WINDOWS: return constants.ValidBinaries.valid_chrome_binaries_on_windows else: raise Exception("Could not determine OS, or unsupported!") elif browser == constants.Browser.EDGE: if IS_LINUX: return constants.ValidBinaries.valid_edge_binaries_on_linux elif IS_MAC: return constants.ValidBinaries.valid_edge_binaries_on_macos elif IS_WINDOWS: return constants.ValidBinaries.valid_edge_binaries_on_windows else: raise Exception("Could not determine OS, or unsupported!") else: raise Exception("Invalid combination for OS browser binaries!") def _special_binary_exists(location, name): filename = str(location).split("/")[-1].split("\\")[-1] return ( location and str(name).lower() in filename.lower() and os.path.exists(location) ) def _repair_chromedriver(chrome_options, headless_options, mcv=None): if mcv: subprocess.check_call( "sbase get chromedriver %s" % mcv, shell=True ) return driver = None subprocess.check_call( "sbase get chromedriver 72.0.3626.69", shell=True ) try: service = ChromeService(executable_path=LOCAL_CHROMEDRIVER) driver = webdriver.Chrome( service=service, options=headless_options, ) except Exception: subprocess.check_call( "sbase get chromedriver latest-1", shell=True ) return chrome_version = None if "version" in driver.capabilities: chrome_version = driver.capabilities["version"] else: chrome_version = driver.capabilities["browserVersion"] major_chrome_ver = chrome_version.split(".")[0] chrome_dict = driver.capabilities["chrome"] driver.quit() chromedriver_ver = chrome_dict["chromedriverVersion"] chromedriver_ver = chromedriver_ver.split(" ")[0] major_chromedriver_ver = chromedriver_ver.split(".")[0] if ( major_chromedriver_ver != major_chrome_ver and int(major_chrome_ver) >= 73 ): subprocess.check_call( "sbase get chromedriver %s" % major_chrome_ver, shell=True ) return def _repair_edgedriver(edge_version): log_d( "\nWarning: msedgedriver version doesn't match Edge version!" "\nAttempting to install a matching version of msedgedriver:" ) subprocess.check_call( "sbase get edgedriver %s" % edge_version, shell=True ) return def _mark_driver_repaired(): abs_path = os.path.abspath(".") driver_repaired_lock = constants.MultiBrowser.DRIVER_REPAIRED file_path = os.path.join(abs_path, driver_repaired_lock) if not os.path.exists(DOWNLOADS_FOLDER): os.makedirs(DOWNLOADS_FOLDER) out_file = open(file_path, mode="w+", encoding="utf-8") out_file.writelines("") out_file.close() def _was_driver_repaired(): abs_path = os.path.abspath(".") driver_repaired_lock = constants.MultiBrowser.DRIVER_REPAIRED file_path = os.path.join(abs_path, driver_repaired_lock) return os.path.exists(file_path) def _set_proxy_filenames(): DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER for num in range(1000): PROXY_ZIP_PATH = os.path.join(DOWNLOADS_DIR, "proxy_%s.zip" % num) PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir_%s" % num) if os.path.exists(PROXY_ZIP_PATH) or os.path.exists(PROXY_DIR_PATH): continue proxy_helper.PROXY_ZIP_PATH = PROXY_ZIP_PATH proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH return # Exceeded upper bound. Use Defaults: PROXY_ZIP_PATH = os.path.join(DOWNLOADS_DIR, "proxy.zip") PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir") proxy_helper.PROXY_ZIP_PATH = PROXY_ZIP_PATH proxy_helper.PROXY_DIR_PATH = PROXY_DIR_PATH def _add_chrome_proxy_extension( chrome_options, proxy_string, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list=None, zip_it=True, multi_proxy=False, ): """Implementation of https://stackoverflow.com/a/35293284/7058266 for https://stackoverflow.com/q/12848327/7058266 (Run Selenium on a proxy server that requires authentication.)""" zip_it = False args = " ".join(sys.argv) bypass_list = proxy_bypass_list if ( not ("-n" in sys.argv or " -n=" in args or args == "-c") and not multi_proxy ): # Single-threaded if zip_it: proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK) with proxy_zip_lock: proxy_helper.create_proxy_ext( proxy_string, proxy_user, proxy_pass, proxy_scheme, bypass_list, ) proxy_zip = proxy_helper.PROXY_ZIP_PATH chrome_options.add_extension(proxy_zip) else: proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK) with proxy_dir_lock: proxy_helper.create_proxy_ext( proxy_string, proxy_user, proxy_pass, proxy_scheme, bypass_list, zip_it=False, ) proxy_dir_path = proxy_helper.PROXY_DIR_PATH chrome_options = add_chrome_ext_dir( chrome_options, proxy_dir_path ) else: # Multi-threaded if zip_it: proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK) with proxy_zip_lock: with suppress(Exception): shared_utils.make_writable(PROXY_ZIP_LOCK) if multi_proxy: _set_proxy_filenames() if not os.path.exists(proxy_helper.PROXY_ZIP_PATH): proxy_helper.create_proxy_ext( proxy_string, proxy_user, proxy_pass, proxy_scheme, bypass_list, ) proxy_zip = proxy_helper.PROXY_ZIP_PATH chrome_options.add_extension(proxy_zip) else: proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK) with proxy_dir_lock: with suppress(Exception): shared_utils.make_writable(PROXY_DIR_LOCK) if multi_proxy: _set_proxy_filenames() if not os.path.exists(proxy_helper.PROXY_DIR_PATH): proxy_helper.create_proxy_ext( proxy_string, proxy_user, proxy_pass, proxy_scheme, bypass_list, zip_it=False, ) chrome_options = add_chrome_ext_dir( chrome_options, proxy_helper.PROXY_DIR_PATH ) return chrome_options def is_using_uc(undetectable, browser_name): if undetectable and browser_name == constants.Browser.GOOGLE_CHROME: return True return False def _unzip_to_new_folder(zip_file, folder): proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK) with proxy_dir_lock: with suppress(Exception): shared_utils.make_writable(PROXY_DIR_LOCK) if not os.path.exists(folder): import zipfile zip_ref = zipfile.ZipFile(zip_file, "r") os.makedirs(folder) zip_ref.extractall(folder) zip_ref.close() def add_chrome_ext_dir(chrome_options, dir_path): option_exists = False for arg in chrome_options.arguments: if arg.startswith("--load-extension="): option_exists = True chrome_options.arguments.remove(arg) chrome_options.add_argument( "%s,%s" % (arg, os.path.realpath(dir_path)) ) if not option_exists: chrome_options.add_argument( "--load-extension=%s" % os.path.realpath(dir_path) ) return chrome_options def _add_chrome_disable_csp_extension(chrome_options): """Disable Chrome's Content-Security-Policy with a browser extension. See https://github.com/PhilGrayson/chrome-csp-disable for details.""" chrome_options.add_extension(DISABLE_CSP_ZIP_PATH) return chrome_options def _add_chrome_ad_block_extension(chrome_options): """Block Ads on Chromium Browsers with a browser extension. See https://github.com/slingamn/simpleblock for details.""" chrome_options.add_extension(AD_BLOCK_ZIP_PATH) return chrome_options def _add_chrome_recorder_extension(chrome_options): """The SeleniumBase Recorder Chrome/Edge extension. https://seleniumbase.io/help_docs/recorder_mode/""" chrome_options.add_extension(RECORDER_ZIP_PATH) return chrome_options def _set_chrome_options( browser_name, downloads_path, headless, locale_code, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, servername, mobile_emulator, device_width, device_height, device_pixel_ratio, ): chrome_options = webdriver.ChromeOptions() if is_using_uc(undetectable, browser_name): from seleniumbase import undetected chrome_options = undetected.ChromeOptions() elif browser_name == constants.Browser.EDGE: chrome_options = webdriver.edge.options.Options() prefs = {} prefs["download.default_directory"] = downloads_path prefs["download.directory_upgrade"] = True prefs["download.prompt_for_download"] = False prefs["download_bubble.partial_view_enabled"] = False prefs["credentials_enable_service"] = False prefs["autofill.credit_card_enabled"] = False prefs["local_discovery.notifications_enabled"] = False prefs["safebrowsing.enabled"] = False # Prevent PW "data breach" pop-ups prefs["safebrowsing.disable_download_protection"] = True prefs["omnibox-max-zero-suggest-matches"] = 0 prefs["omnibox-use-existing-autocomplete-client"] = 0 prefs["omnibox-trending-zero-prefix-suggestions-on-ntp"] = 0 prefs["omnibox-local-history-zero-suggest-beyond-ntp"] = 0 prefs["omnibox-on-focus-suggestions-contextual-web"] = 0 prefs["omnibox-on-focus-suggestions-srp"] = 0 prefs["omnibox-zero-suggest-prefetching"] = 0 prefs["omnibox-zero-suggest-prefetching-on-srp"] = 0 prefs["omnibox-zero-suggest-prefetching-on-web"] = 0 prefs["omnibox-zero-suggest-in-memory-caching"] = 0 prefs["content_settings.exceptions.automatic_downloads.*.setting"] = 1 prefs["default_content_setting_values.notifications"] = 0 prefs["default_content_settings.popups"] = 0 prefs["managed_default_content_settings.popups"] = 0 prefs["profile.password_manager_enabled"] = False prefs["profile.password_manager_leak_detection"] = False prefs["profile.default_content_setting_values.notifications"] = 2 prefs["profile.default_content_settings.popups"] = 0 prefs["profile.managed_default_content_settings.popups"] = 0 prefs["profile.default_content_setting_values.automatic_downloads"] = 1 if locale_code: prefs["intl.accept_languages"] = locale_code sb_config._cdp_locale = locale_code else: sb_config._cdp_locale = None if block_images: prefs["profile.managed_default_content_settings.images"] = 2 if disable_cookies: prefs["profile.default_content_setting_values.cookies"] = 2 if disable_js: prefs["profile.managed_default_content_settings.javascript"] = 2 if do_not_track: prefs["enable_do_not_track"] = True if external_pdf: prefs["plugins.always_open_pdf_externally"] = True pdf_settings = { "recentDestinations": [ {"id": "Save as PDF", "origin": "local", "account": ""} ], "selectedDestinationId": "Save as PDF", "version": 2, } app_state = "printing.print_preview_sticky_settings.appState" prefs[app_state] = json.dumps(pdf_settings) if proxy_string or proxy_pac_url: # Implementation of https://stackoverflow.com/q/65705775/7058266 prefs["webrtc.ip_handling_policy"] = "disable_non_proxied_udp" prefs["webrtc.multiple_routes_enabled"] = False prefs["webrtc.nonproxied_udp_enabled"] = False chrome_options.add_experimental_option("prefs", prefs) if enable_sync: chrome_options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging", "disable-sync"], ) chrome_options.add_argument("--enable-sync") else: chrome_options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging", "enable-blink-features"], ) if log_cdp_events: chrome_options.set_capability( "goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"} ) if host_resolver_rules: chrome_options.add_argument( "--host-resolver-rules=%s" % host_resolver_rules ) if mobile_emulator and not is_using_uc(undetectable, browser_name): emulator_settings = {} device_metrics = {} if ( isinstance(device_width, int) and isinstance(device_height, int) and isinstance(device_pixel_ratio, (int, float)) ): device_metrics["width"] = device_width device_metrics["height"] = device_height device_metrics["pixelRatio"] = device_pixel_ratio else: device_metrics["width"] = constants.Mobile.WIDTH device_metrics["height"] = constants.Mobile.HEIGHT device_metrics["pixelRatio"] = constants.Mobile.RATIO emulator_settings["deviceMetrics"] = device_metrics if user_agent: emulator_settings["userAgent"] = user_agent chrome_options.add_experimental_option( "mobileEmulation", emulator_settings ) # Handle Window Position if (headless or headless2) and IS_WINDOWS: # https://stackoverflow.com/a/78999088/7058266 chrome_options.add_argument("--window-position=-2400,-2400") else: if ( hasattr(settings, "WINDOW_START_X") and isinstance(settings.WINDOW_START_X, int) and hasattr(settings, "WINDOW_START_Y") and isinstance(settings.WINDOW_START_Y, int) ): chrome_options.add_argument( "--window-position=%s,%s" % ( settings.WINDOW_START_X, settings.WINDOW_START_Y ) ) # Handle Window Size if headless or headless2: if ( hasattr(settings, "HEADLESS_START_WIDTH") and isinstance(settings.HEADLESS_START_WIDTH, int) and hasattr(settings, "HEADLESS_START_HEIGHT") and isinstance(settings.HEADLESS_START_HEIGHT, int) ): chrome_options.add_argument( "--window-size=%s,%s" % ( settings.HEADLESS_START_WIDTH, settings.HEADLESS_START_HEIGHT, ) ) else: if ( hasattr(settings, "CHROME_START_WIDTH") and isinstance(settings.CHROME_START_WIDTH, int) and hasattr(settings, "CHROME_START_HEIGHT") and isinstance(settings.CHROME_START_HEIGHT, int) ): chrome_options.add_argument( "--window-size=%s,%s" % ( settings.CHROME_START_WIDTH, settings.CHROME_START_HEIGHT, ) ) if ( not proxy_auth and not disable_csp and not ad_block_on and not recorder_ext and (not extension_zip and not extension_dir) ): if ( binary_location and isinstance(binary_location, str) and ( binary_location.lower().endswith("comet") or binary_location.lower().endswith("comet.exe") or binary_location.lower().endswith("atlas") or binary_location.lower().endswith("atlas.exe") ) ): # AI browsers don't like Incognito / Guest Mode incognito = False guest_mode = False if incognito: # Use Chrome's Incognito Mode # Incognito Mode prevents Chrome extensions from loading, # so if using extensions or a feature that uses extensions, # then Chrome's Incognito mode will be disabled instead. chrome_options.add_argument("--incognito") elif guest_mode: # Use Chrome's Guest Mode # Guest mode prevents Chrome extensions from loading, # so if using extensions or a feature that uses extensions, # then Chrome's Guest Mode will be disabled instead. chrome_options.add_argument("--guest") else: pass if dark_mode: chrome_options.add_argument("--enable-features=WebContentsForceDark") if user_data_dir and not is_using_uc(undetectable, browser_name): abs_path = os.path.abspath(user_data_dir) chrome_options.add_argument("--user-data-dir=%s" % abs_path) if extension_zip: # Can be a comma-separated list of .ZIP or .CRX files extension_zip_list = extension_zip.split(",") for extension_zip_item in extension_zip_list: abs_path = os.path.realpath(extension_zip_item) if os.path.exists(abs_path): try: abs_path_dir = os.path.join( DOWNLOADS_FOLDER, abs_path.split(".")[0] ) _unzip_to_new_folder(abs_path, abs_path_dir) chrome_options = add_chrome_ext_dir( chrome_options, abs_path_dir ) sb_config._ext_dirs.append(abs_path_dir) except Exception: with suppress(Exception): chrome_options.add_extension(abs_path) if extension_dir: # load-extension input can be a comma-separated list abs_path = ( ",".join(os.path.realpath(p) for p in extension_dir.split(",")) ) chrome_options = add_chrome_ext_dir(chrome_options, abs_path) for p in extension_dir.split(","): sb_config._ext_dirs.append(os.path.realpath(p)) if ( page_load_strategy and page_load_strategy.lower() in ["eager", "none"] ): # Only change it if not "normal", which is the default. chrome_options.page_load_strategy = page_load_strategy.lower() elif ( not page_load_strategy and getattr(settings, "PAGE_LOAD_STRATEGY", None) and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"] ): # Only change it if not "normal", which is the default. chrome_options.page_load_strategy = settings.PAGE_LOAD_STRATEGY.lower() if headless2: if servername and servername != "localhost": # The Grid Node will need Chrome 109 or newer chrome_options.add_argument("--headless=new") else: pass # Processed After Version Check elif headless: if not undetectable: if headless1: chrome_options.add_argument("--headless=old") else: chrome_options.add_argument("--headless") if undetectable and servername and servername != "localhost": # The Grid Node will need Chrome 109 or newer chrome_options.add_argument("--headless=new") else: pass # Processed After Version Check if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless: # Headless Chrome does not support extensions, which are required # for disabling the Content Security Policy on Chrome. disable_csp_zip = DISABLE_CSP_ZIP_PATH disable_csp_dir = os.path.join(DOWNLOADS_FOLDER, "disable_csp") _unzip_to_new_folder(disable_csp_zip, disable_csp_dir) chrome_options = add_chrome_ext_dir( chrome_options, disable_csp_dir ) sb_config._ext_dirs.append(disable_csp_dir) if ad_block_on and not headless: # Headless Chrome does not support extensions. ad_block_zip = AD_BLOCK_ZIP_PATH ad_block_dir = os.path.join(DOWNLOADS_FOLDER, "ad_block") _unzip_to_new_folder(ad_block_zip, ad_block_dir) chrome_options = add_chrome_ext_dir(chrome_options, ad_block_dir) sb_config._ext_dirs.append(ad_block_dir) if recorder_ext and not headless: recorder_zip = RECORDER_ZIP_PATH recorder_dir = os.path.join(DOWNLOADS_FOLDER, "recorder") _unzip_to_new_folder(recorder_zip, recorder_dir) chrome_options = add_chrome_ext_dir(chrome_options, recorder_dir) sb_config._ext_dirs.append(recorder_dir) if chromium_arg and "sbase" in chromium_arg: sbase_ext_zip = SBASE_EXT_ZIP_PATH sbase_ext_dir = os.path.join(DOWNLOADS_FOLDER, "sbase_ext") _unzip_to_new_folder(sbase_ext_zip, sbase_ext_dir) chrome_options = add_chrome_ext_dir(chrome_options, sbase_ext_dir) sb_config._ext_dirs.append(sbase_ext_dir) if proxy_string: if proxy_auth: zip_it = True if is_using_uc(undetectable, browser_name): zip_it = False # undetected-chromedriver needs a folder ext chrome_options = _add_chrome_proxy_extension( chrome_options, proxy_string, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, zip_it, multi_proxy, ) chrome_options.add_argument("--proxy-server=%s" % proxy_string) if proxy_bypass_list: chrome_options.add_argument( "--proxy-bypass-list=%s" % proxy_bypass_list ) elif proxy_pac_url: if proxy_auth: zip_it = True # undetected-chromedriver needs a folder ext if is_using_uc(undetectable, browser_name): zip_it = False chrome_options = _add_chrome_proxy_extension( chrome_options, None, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, zip_it, multi_proxy, ) chrome_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url) if ( not is_using_uc(undetectable, browser_name) or not enable_ws or proxy_string ): chrome_options.add_argument("--ignore-certificate-errors") chrome_options.add_argument("--ignore-ssl-errors=yes") if not enable_ws: chrome_options.add_argument("--disable-web-security") if ( IS_LINUX or (IS_MAC and not is_using_uc(undetectable, browser_name)) ): chrome_options.add_argument("--no-sandbox") if remote_debug: # To access the Debugger, go to: chrome://inspect/#devices # while a Chromium driver is running. # Info: https://chromedevtools.github.io/devtools-protocol/ args = " ".join(sys.argv) debug_port = 9222 if ("-n" in sys.argv or " -n=" in args or args == "-c"): debug_port = service_utils.free_port() chrome_options.add_argument("--remote-debugging-port=%s" % debug_port) if swiftshader: chrome_options.add_argument("--use-gl=angle") chrome_options.add_argument("--use-angle=swiftshader-webgl") elif ( not is_using_uc(undetectable, browser_name) and not enable_3d_apis ): chrome_options.add_argument("--disable-gpu") if ( (not IS_LINUX and is_using_uc(undetectable, browser_name)) or ( IS_MAC and binary_location and "chrome-headless-shell" in binary_location ) ): chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-application-cache") if IS_LINUX: chrome_options.add_argument("--disable-dev-shm-usage") if is_using_uc(undetectable, browser_name): chrome_options.add_argument("--disable-application-cache") chrome_options.add_argument("--disable-setuid-sandbox") if not binary_location: if os.path.exists("/bin/google-chrome"): binary_location = "/bin/google-chrome" elif os.path.exists("/usr/bin/google-chrome-stable"): binary_location = "/usr/bin/google-chrome-stable" elif os.path.exists("/usr/bin/google-chrome"): binary_location = "/usr/bin/google-chrome" elif os.path.exists("/usr/bin/google-chrome-stable"): binary_location = "/usr/bin/google-chrome-stable" else: br_app = "google-chrome" binary_loc = detect_b_ver.get_binary_location(br_app, True) if os.path.exists(binary_loc): binary_location = binary_loc extra_disabled_features = [] if chromium_arg: # Can be a comma-separated list of Chromium args or a list chromium_arg_list = None if isinstance(chromium_arg, (list, tuple)): chromium_arg_list = chromium_arg else: chromium_arg_list = chromium_arg.split(",") for chromium_arg_item in chromium_arg_list: chromium_arg_item = chromium_arg_item.strip() if not chromium_arg_item.startswith("--"): if chromium_arg_item.startswith("-"): chromium_arg_item = "-" + chromium_arg_item else: chromium_arg_item = "--" + chromium_arg_item if "remote-debugging-port=" in chromium_arg_item: with suppress(Exception): # Extra processing for UC Mode chrome_options._remote_debugging_port = int( chromium_arg_item.split("remote-debugging-port=")[1] ) if "set-binary" in chromium_arg_item and not binary_location: br_app = "google-chrome" binary_loc = detect_b_ver.get_binary_location( br_app, is_using_uc(undetectable, browser_name) ) if os.path.exists(binary_loc): binary_location = binary_loc elif "disable-features=" in chromium_arg_item: d_f = chromium_arg_item.split("disable-features=")[-1] extra_disabled_features.append(d_f) elif len(chromium_arg_item) >= 3: chrome_options.add_argument(chromium_arg_item) if disable_features: extra_disabled_features.extend(disable_features.split(",")) if devtools and not headless: chrome_options.add_argument("--auto-open-devtools-for-tabs") if user_agent: chrome_options.add_argument("--user-agent=%s" % user_agent) chrome_options.add_argument("--safebrowsing-disable-download-protection") chrome_options.add_argument("--disable-search-engine-choice-screen") chrome_options.add_argument("--disable-browser-side-navigation") chrome_options.add_argument("--disable-save-password-bubble") chrome_options.add_argument("--disable-single-click-autofill") chrome_options.add_argument("--allow-file-access-from-files") chrome_options.add_argument("--disable-prompt-on-repost") chrome_options.add_argument("--dns-prefetch-disable") chrome_options.add_argument("--disable-translate") if binary_location: chrome_options.binary_location = binary_location if not enable_3d_apis and not is_using_uc(undetectable, browser_name): chrome_options.add_argument("--disable-3d-apis") if headless or headless2 or is_using_uc(undetectable, browser_name): chrome_options.add_argument("--disable-renderer-backgrounding") chrome_options.add_argument("--disable-backgrounding-occluded-windows") chrome_options.add_argument("--disable-client-side-phishing-detection") chrome_options.add_argument("--disable-device-discovery-notifications") chrome_options.add_argument("--disable-oopr-debug-crash-dump") chrome_options.add_argument("--disable-top-sites") chrome_options.add_argument("--ash-no-nudges") chrome_options.add_argument("--no-crash-upload") chrome_options.add_argument("--deny-permission-prompts") chrome_options.add_argument( '--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"' ) chrome_options.add_argument("--disable-ipc-flooding-protection") chrome_options.add_argument("--disable-password-generation") chrome_options.add_argument("--disable-domain-reliability") chrome_options.add_argument("--disable-breakpad") included_disabled_features = [] included_disabled_features.append("OptimizationHints") included_disabled_features.append("OptimizationHintsFetching") included_disabled_features.append("Translate") included_disabled_features.append("ComponentUpdater") included_disabled_features.append("OptimizationTargetPrediction") included_disabled_features.append("OptimizationGuideModelDownloading") included_disabled_features.append("DownloadBubble") included_disabled_features.append("DownloadBubbleV2") included_disabled_features.append("InsecureDownloadWarnings") included_disabled_features.append("InterestFeedContentSuggestions") included_disabled_features.append("PrivacySandboxSettings4") included_disabled_features.append("SidePanelPinning") included_disabled_features.append("UserAgentClientHint") included_disabled_features.append("DisableLoadExtensionCommandLineSwitch") included_disabled_features.append("Bluetooth") included_disabled_features.append("WebBluetooth") included_disabled_features.append("UnifiedWebBluetooth") included_disabled_features.append("WebAuthentication") included_disabled_features.append("PasskeyAuth") for item in extra_disabled_features: if item not in included_disabled_features: included_disabled_features.append(item) d_f_string = ",".join(included_disabled_features) chrome_options.add_argument("--disable-features=%s" % d_f_string) chrome_options.add_argument("--enable-unsafe-extension-debugging") if proxy_string: chrome_options.add_argument("--test-type") if proxy_auth or sb_config._ext_dirs: if not is_using_uc(undetectable, browser_name): chrome_options.add_argument("--remote-debugging-pipe") chrome_options.enable_webextensions = True chrome_options.enable_bidi = True if ( is_using_uc(undetectable, browser_name) and ( not headless or IS_LINUX # switches to Xvfb (non-headless) ) ): chrome_options.add_argument("--no-pings") chrome_options.add_argument("--homepage=chrome://version/") chrome_options.add_argument("--animation-duration-scale=0") chrome_options.add_argument("--wm-window-animations-disabled") chrome_options.add_argument("--enable-privacy-sandbox-ads-apis") chrome_options.add_argument("--disable-background-timer-throttling") # Prevent new tabs opened by Selenium from being blocked: chrome_options.add_argument("--disable-popup-blocking") # Skip remaining options that trigger anti-bot services return chrome_options if not proxy_string: chrome_options.add_argument("--test-type") chrome_options.add_argument("--log-level=3") chrome_options.add_argument("--no-first-run") chrome_options.add_argument("--allow-insecure-localhost") chrome_options.add_argument("--allow-running-insecure-content") chrome_options.add_argument("--disable-infobars") chrome_options.add_argument("--disable-notifications") chrome_options.add_argument( "--disable-autofill-keyboard-accessory-view[8]" ) chrome_options.add_argument("--homepage=about:blank") chrome_options.add_argument("--dom-automation") chrome_options.add_argument("--disable-hang-monitor") return chrome_options def _set_firefox_options( downloads_path, headless, locale_code, proxy_string, proxy_bypass_list, proxy_pac_url, user_agent, disable_cookies, disable_js, disable_csp, firefox_arg, firefox_pref, external_pdf, ): blank_p = "about:blank" options = webdriver.FirefoxOptions() options.accept_untrusted_certs = True options.set_preference("reader.parse-on-load.enabled", False) options.set_preference("browser.startup.homepage", blank_p) options.set_preference("startup.homepage_welcome_url", blank_p) options.set_preference("startup.homepage_welcome_url.additional", blank_p) options.set_preference("browser.newtab.url", blank_p) options.set_preference("trailhead.firstrun.branches", "nofirstrun-empty") options.set_preference("browser.aboutwelcome.enabled", False) options.set_preference("app.update.auto", False) options.set_preference("app.update.enabled", False) options.set_preference("browser.formfill.enable", False) options.set_preference("browser.privatebrowsing.autostart", True) options.set_preference("dom.webnotifications.enabled", False) options.set_preference("dom.disable_beforeunload", True) options.set_preference("browser.contentblocking.database.enabled", True) options.set_preference("extensions.systemAddon.update.enabled", False) options.set_preference("extensions.update.autoUpdateDefault", False) options.set_preference("extensions.update.enabled", False) options.set_preference("datareporting.healthreport.service.enabled", False) options.set_preference("datareporting.healthreport.uploadEnabled", False) options.set_preference("datareporting.policy.dataSubmissionEnabled", False) options.set_preference("browser.search.update", False) options.set_preference("privacy.trackingprotection.enabled", False) options.set_preference("toolkit.telemetry.enabled", False) options.set_preference("toolkit.telemetry.unified", False) options.set_preference("toolkit.telemetry.archive.enabled", False) if proxy_string: socks_proxy = False socks_ver = 0 chunks = proxy_string.split(":") if len(chunks) == 3 and ( chunks[0] == "socks4" or chunks[0] == "socks5" ): socks_proxy = True socks_ver = int(chunks[0][5]) if chunks[1].startswith("//") and len(chunks[1]) > 2: chunks[1] = chunks[1][2:] proxy_server = chunks[1] proxy_port = chunks[2] else: proxy_server = proxy_string.split(":")[0] proxy_port = proxy_string.split(":")[1] options.set_preference("network.proxy.type", 1) if socks_proxy: options.set_preference("network.proxy.socks", proxy_server) options.set_preference("network.proxy.socks_port", int(proxy_port)) options.set_preference("network.proxy.socks_version", socks_ver) else: options.set_preference("network.proxy.http", proxy_server) options.set_preference("network.proxy.http_port", int(proxy_port)) options.set_preference("network.proxy.ssl", proxy_server) options.set_preference("network.proxy.ssl_port", int(proxy_port)) if proxy_bypass_list: options.set_preference("no_proxies_on", proxy_bypass_list) elif proxy_pac_url: options.set_preference("network.proxy.type", 2) options.set_preference("network.proxy.autoconfig_url", proxy_pac_url) if user_agent: options.set_preference("general.useragent.override", user_agent) options.set_preference( "security.mixed_content.block_active_content", False ) options.set_preference("security.warn_submit_insecure", False) if disable_cookies: options.set_preference("network.cookie.cookieBehavior", 2) if disable_js: options.set_preference("javascript.enabled", False) if settings.DISABLE_CSP_ON_FIREFOX or disable_csp: options.set_preference("security.csp.enable", False) options.set_preference( "browser.download.manager.showAlertOnComplete", False ) if headless and not IS_LINUX: options.add_argument("--headless") elif headless and IS_LINUX: # This assumes Xvfb is running, which prevents many Linux issues. # If not, we'll fix this later during the error-handling process. # To override this feature: ``pytest --firefox-arg="-headless"``. pass if locale_code: options.set_preference("intl.accept_languages", locale_code) options.set_preference("browser.shell.checkDefaultBrowser", False) options.set_preference("browser.startup.page", 0) options.set_preference("browser.download.panel.shown", False) options.set_preference("browser.download.animateNotifications", False) options.set_preference("browser.download.dir", downloads_path) options.set_preference("browser.download.folderList", 2) options.set_preference("browser.helperApps.alwaysAsk.force", False) options.set_preference("browser.download.manager.showWhenStarting", False) options.set_preference( "browser.helperApps.neverAsk.saveToDisk", ( "application/pdf,application/zip,application/octet-stream," "text/csv,text/xml,application/xml,text/plain,application/json," "text/octet-stream,application/x-gzip,application/x-tar," "application/java-archive,text/x-java-source,java," "application/javascript,video/jpeg,audio/x-aac,image/svg+xml," "application/x-font-woff,application/x-7z-compressed," "application/mp4,video/mp4,audio/mp4,video/x-msvideo," "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ), ) if external_pdf: options.set_preference("pdfjs.disabled", True) else: options.set_preference("pdfjs.disabled", False) if firefox_arg: # Can be a comma-separated list of Firefox args firefox_arg_list = firefox_arg.split(",") for firefox_arg_item in firefox_arg_list: firefox_arg_item = firefox_arg_item.strip() if not firefox_arg_item.startswith("-"): if firefox_arg_item.count(os.sep) == 0: firefox_arg_item = "--" + firefox_arg_item if len(firefox_arg_item) >= 3: options.add_argument(firefox_arg_item) if firefox_pref: # Can be a comma-separated list of Firefox preference:value pairs firefox_pref_list = firefox_pref.split(",") for firefox_pref_item in firefox_pref_list: f_pref = None f_pref_value = None needs_conversion = False if firefox_pref_item.count(":") == 0: f_pref = firefox_pref_item f_pref_value = True elif firefox_pref_item.count(":") == 1: f_pref = firefox_pref_item.split(":")[0] f_pref_value = firefox_pref_item.split(":")[1] needs_conversion = True elif firefox_pref_item.count("://") == 1: f_pref = firefox_pref_item.split(":")[0] f_pref_value = ":".join(firefox_pref_item.split(":")[1:]) else: # More than one ":" in the set without a URL. raise Exception( 'Incorrect formatting for Firefox "pref:value" set!' ) if needs_conversion: if f_pref_value.lower() == "true" or len(f_pref_value) == 0: f_pref_value = True elif f_pref_value.lower() == "false": f_pref_value = False elif f_pref_value.isdigit(): f_pref_value = int(f_pref_value) elif f_pref_value.replace(".", "", 1).isdigit(): f_pref_value = float(f_pref_value) else: pass # keep as string if len(f_pref) >= 1: options.set_preference(f_pref, f_pref_value) return options def get_driver( browser_name=None, headless=False, locale_code=None, use_grid=False, protocol="http", servername="localhost", port=4444, proxy_string=None, proxy_bypass_list=None, proxy_pac_url=None, multi_proxy=None, user_agent=None, cap_file=None, cap_string=None, recorder_ext=False, disable_cookies=False, disable_js=False, disable_csp=False, enable_ws=False, enable_sync=False, use_auto_ext=False, undetectable=False, uc_cdp_events=False, uc_subprocess=False, log_cdp_events=False, no_sandbox=False, disable_gpu=False, headless1=False, headless2=False, incognito=False, guest_mode=False, dark_mode=False, devtools=False, remote_debug=False, enable_3d_apis=False, swiftshader=False, ad_block_on=False, host_resolver_rules=None, block_images=False, do_not_track=False, chromium_arg=None, firefox_arg=None, firefox_pref=None, user_data_dir=None, extension_zip=None, extension_dir=None, disable_features=None, binary_location=None, driver_version=None, page_load_strategy=None, use_wire=False, external_pdf=False, test_id=None, mobile_emulator=False, device_width=None, device_height=None, device_pixel_ratio=None, browser=None, # A duplicate of browser_name to avoid confusion ): sb_config._ext_dirs = [] driver_dir = DRIVER_DIR if binary_location == "_chromium_": driver_dir = DRIVER_DIR_CHROMIUM elif binary_location == "cft": driver_dir = DRIVER_DIR_CFT elif binary_location == "chs": driver_dir = DRIVER_DIR_CHS elif _special_binary_exists(binary_location, "opera"): driver_dir = DRIVER_DIR_OPERA sb_config._cdp_browser = "opera" elif _special_binary_exists(binary_location, "brave"): driver_dir = DRIVER_DIR_BRAVE sb_config._cdp_browser = "brave" elif _special_binary_exists(binary_location, "comet"): driver_dir = DRIVER_DIR_COMET sb_config._cdp_browser = "comet" elif _special_binary_exists(binary_location, "atlas"): driver_dir = DRIVER_DIR_ATLAS sb_config._cdp_browser = "atlas" if ( hasattr(sb_config, "settings") and getattr(sb_config.settings, "NEW_DRIVER_DIR", None) and os.path.exists(sb_config.settings.NEW_DRIVER_DIR) ): driver_dir = sb_config.settings.NEW_DRIVER_DIR if not browser_name: if browser: browser_name = browser else: browser_name = "chrome" # The default if not specified if browser_name in constants.ChromiumSubs.chromium_subs: browser_name = "chrome" browser_name = browser_name.lower() if is_using_uc(undetectable, browser_name): if ad_block_on: sb_config.ad_block_on = True else: sb_config.ad_block_on = False if disable_csp: sb_config.disable_csp = True else: sb_config.disable_csp = False if mobile_emulator: # For stealthy mobile mode, see the CDP Mode examples # to learn how to properly configure it. user_agent = None # Undo the override mobile_emulator = False # Instead, set from CDP Mode sb_config._cdp_mobile_mode = True else: sb_config._cdp_mobile_mode = False if headless2 and browser_name == constants.Browser.FIREFOX: headless2 = False # Only for Chromium headless = True if binary_location and isinstance(binary_location, str): binary_location = binary_location.strip() if ( is_using_uc(undetectable, browser_name) and binary_location and isinstance(binary_location, str) and binary_location.lower() == "chs" ): raise Exception("UC Mode can't be used with Chrome-Headless-Shell!") if ( binary_location and isinstance(binary_location, str) and ( browser_name == constants.Browser.GOOGLE_CHROME or browser_name == constants.Browser.EDGE ) ): if ( binary_location.lower() == "_chromium_" and browser_name == constants.Browser.GOOGLE_CHROME ): binary_folder = None if IS_MAC: binary_folder = "chrome-mac" elif IS_LINUX: binary_folder = "chrome-linux" elif IS_WINDOWS: binary_folder = "chrome-win" if binary_folder: binary_location = os.path.join(driver_dir, binary_folder) if not os.path.exists(binary_location): from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ( "-n" in sys.argv or " -n=" in args or args == "-c" ): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args log_d("\nWarning: Chromium binary not found...") try: sb_install.main(override="chromium") except Exception as e: log_d("\nWarning: Chrome download failed: %s" % e) sys.argv = sys_args # Put back the original sys args else: chrome_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with chrome_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) if not os.path.exists(binary_location): sys_args = sys.argv # Save a copy of sys args log_d( "\nWarning: Chromium binary not found..." ) sb_install.main(override="chromium") sys.argv = sys_args # Put back original args else: binary_location = None if ( binary_location.lower() == "cft" and browser_name == constants.Browser.GOOGLE_CHROME ): binary_folder = None if IS_MAC: if IS_ARM_MAC: binary_folder = "chrome-mac-arm64" else: binary_folder = "chrome-mac-x64" elif IS_LINUX: binary_folder = "chrome-linux64" elif IS_WINDOWS: if "64" in ARCH: binary_folder = "chrome-win64" else: binary_folder = "chrome-win32" if binary_folder: binary_location = os.path.join(driver_dir, binary_folder) if not os.path.exists(binary_location): from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ( "-n" in sys.argv or " -n=" in args or args == "-c" ): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args log_d( "\nWarning: Chrome for Testing binary not found..." ) try: sb_install.main(override="cft") except Exception as e: log_d("\nWarning: Chrome download failed: %s" % e) sys.argv = sys_args # Put back the original sys args else: chrome_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with chrome_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) if not os.path.exists(binary_location): sys_args = sys.argv # Save a copy of sys args log_d( "\nWarning: " "Chrome for Testing binary not found..." ) sb_install.main(override="cft") sys.argv = sys_args # Put back original args else: binary_location = None if ( binary_location.lower() == "chs" and browser_name == constants.Browser.GOOGLE_CHROME ): binary_folder = None if IS_MAC: if IS_ARM_MAC: binary_folder = "chrome-headless-shell-mac-arm64" else: binary_folder = "chrome-headless-shell-mac-x64" elif IS_LINUX: binary_folder = "chrome-headless-shell-linux64" elif IS_WINDOWS: if "64" in ARCH: binary_folder = "chrome-headless-shell-win64" else: binary_folder = "chrome-headless-shell-win32" if binary_folder: binary_location = os.path.join(driver_dir, binary_folder) if not os.path.exists(binary_location): from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ( "-n" in sys.argv or " -n=" in args or args == "-c" ): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args log_d( "\nWarning: " "Chrome-Headless-Shell binary not found..." ) try: sb_install.main(override="chs") except Exception as e: log_d( "\nWarning: " "Chrome-Headless-Shell download failed: %s" % e ) sys.argv = sys_args # Put back the original sys args else: chrome_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with chrome_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) if not os.path.exists(binary_location): sys_args = sys.argv # Save a copy of sys args log_d( "\nWarning: " "Chrome-Headless-Shell binary not found..." ) sb_install.main(override="chs") sys.argv = sys_args # Put back original args else: binary_location = None if not os.path.exists(binary_location): log_d( "\nWarning: The Chromium binary specified (%s) was NOT found!" "\n(Will use default settings...)\n" % binary_location ) binary_location = None elif binary_location.endswith("/") or binary_location.endswith("\\"): log_d( "\nWarning: The Chromium binary path must be a full path " "that includes the browser filename at the end of it!" "\n(Will use default settings...)\n" % binary_location ) # Example of a valid binary location path - MacOS: # "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" binary_location = None else: binary_name = binary_location.split("/")[-1].split("\\")[-1] valid_names = get_valid_binary_names_for_browser(browser_name) if binary_name == "Google Chrome for Testing.app": binary_name = "Google Chrome for Testing" binary_location += "/Contents/MacOS/Google Chrome for Testing" elif binary_name in ["chrome-mac-arm64", "chrome-mac-x64"]: binary_name = "Google Chrome for Testing" binary_location += "/Google Chrome for Testing.app" binary_location += "/Contents/MacOS/Google Chrome for Testing" elif binary_name == "Chromium.app": binary_name = "Chromium" binary_location += "/Contents/MacOS/Chromium" elif binary_name in ["chrome-mac"]: binary_name = "Chromium" binary_location += "/Chromium.app" binary_location += "/Contents/MacOS/Chromium" elif binary_name == "chrome-linux64": binary_name = "chrome" binary_location += "/chrome" elif binary_name == "chrome-linux": binary_name = "chrome" binary_location += "/chrome" elif binary_name in ["chrome-win32", "chrome-win64"]: binary_name = "chrome.exe" binary_location += "\\chrome.exe" elif binary_name in ["chrome-win"]: binary_name = "chrome.exe" binary_location += "\\chrome.exe" elif binary_name in [ "chrome-headless-shell-mac-arm64", "chrome-headless-shell-mac-x64", ]: binary_name = "chrome-headless-shell" binary_location += "/chrome-headless-shell" elif binary_name == "chrome-headless-shell-linux64": binary_name = "chrome-headless-shell" binary_location += "/chrome-headless-shell" elif binary_name in [ "chrome-headless-shell-win32", "chrome-headless-shell-win64", ]: binary_name = "chrome-headless-shell.exe" binary_location += "\\chrome-headless-shell.exe" if binary_name not in valid_names: log_d( "\nWarning: The Chromium binary specified is NOT valid!" '\nExpecting "%s" to be found in %s for the browser / OS!' "\n(Will use default settings...)\n" "" % (binary_name, valid_names) ) binary_location = None elif binary_location.lower() == "chs": headless = True if (uc_cdp_events or uc_subprocess) and not undetectable: undetectable = True if mobile_emulator and not user_agent: # Use a Pixel user agent by default if not specified user_agent = constants.Mobile.AGENT if page_load_strategy and page_load_strategy.lower() == "none": settings.PAGE_LOAD_STRATEGY = "none" proxy_auth = False proxy_user = None proxy_pass = None proxy_scheme = "http" if proxy_string: # (The line below is for the Chrome 142 proxy auth fix) sb_config._cdp_proxy = proxy_string username_and_password = None if "@" in proxy_string: # Format => username:password@hostname:port try: username_and_password = proxy_string.split("@")[0] proxy_string = proxy_string.split("@")[1] proxy_user = username_and_password.split(":")[0] proxy_pass = username_and_password.split(":")[1] except Exception: raise Exception( "The format for using a proxy server with authentication " 'is: "username:password@hostname:port". If using a proxy ' 'server without auth, the format is: "hostname:port".' ) if browser_name != constants.Browser.GOOGLE_CHROME and ( browser_name != constants.Browser.EDGE ): raise Exception( "Chrome or Edge is required when using a proxy server " "that has authentication! (If using a proxy server " "without auth, Chrome, Edge, or Firefox may be used.)" ) proxy_string, proxy_scheme = proxy_helper.validate_proxy_string( proxy_string, keep_scheme=True ) if proxy_user and not proxy_pass: proxy_pass = "" if proxy_string and proxy_user: proxy_auth = True elif proxy_pac_url: username_and_password = None if "@" in proxy_pac_url: # Format => username:password@PAC_URL.pac try: username_and_password = proxy_pac_url.split("@")[0] proxy_pac_url = proxy_pac_url.split("@")[1] proxy_user = username_and_password.split(":")[0] proxy_pass = username_and_password.split(":")[1] except Exception: raise Exception( "The format for using a PAC URL with authentication " 'is: "username:password@PAC_URL.pac". If using a PAC ' 'URL without auth, the format is: "PAC_URL.pac".' ) if browser_name != constants.Browser.GOOGLE_CHROME and ( browser_name != constants.Browser.EDGE ): raise Exception( "Chrome or Edge is required when using a PAC URL " "that has authentication! (If using a PAC URL " "without auth, Chrome, Edge, or Firefox may be used.)" ) if not proxy_pac_url.lower().endswith(".pac"): raise Exception('The proxy PAC URL must end with ".pac"!') if proxy_user and not proxy_pass: proxy_pass = "" if proxy_pac_url and proxy_user: proxy_auth = True if ( is_using_uc(undetectable, browser_name) and not IS_LINUX and headless ): headless = False headless2 = True if ( headless and ( proxy_auth or disable_cookies or disable_js or ad_block_on or disable_csp or recorder_ext or extension_zip or extension_dir ) and ( browser_name == constants.Browser.GOOGLE_CHROME or browser_name == constants.Browser.EDGE ) ): # Headless Chrome/Edge doesn't support extensions, which are # required when using a proxy server that has authentication, # or when using other SeleniumBase extensions (eg: Recorder). # Instead, base_case.py will use the SBVirtualDisplay when not # using Chrome's built-in headless mode. See link for details: # https://bugs.chromium.org/p/chromium/issues/detail?id=706008 headless = False if not IS_LINUX: # Use the new headless mode on Chrome if not using Linux: # bugs.chromium.org/p/chromium/issues/detail?id=706008#c36 # Although Linux is technically supported, there are a lot # of old versions of Chrome on Linux server machines, and # this mode requires a recent version of Chrome to work. # Specify "--headless2" as a pytest arg to use on Linux. headless2 = True if ( browser_name == constants.Browser.GOOGLE_CHROME and user_data_dir and len(user_data_dir) < 3 ): raise Exception( "Name length of Chrome's User Data Directory must be >= 3." ) if use_grid: return get_remote_driver( browser_name, headless, locale_code, protocol, servername, port, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, cap_file, cap_string, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, firefox_arg, firefox_pref, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, test_id, mobile_emulator, device_width, device_height, device_pixel_ratio, ) else: return get_local_driver( browser_name, headless, locale_code, servername, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, firefox_arg, firefox_pref, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, mobile_emulator, device_width, device_height, device_pixel_ratio, ) def get_remote_driver( browser_name, headless, locale_code, protocol, servername, port, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, cap_file, cap_string, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, firefox_arg, firefox_pref, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, test_id, mobile_emulator, device_width, device_height, device_pixel_ratio, ): if use_wire: pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) with pip_find_lock: # Prevent issues with multiple processes with suppress(Exception): shared_utils.make_writable(constants.PipInstall.FINDLOCK) try: from seleniumwire import webdriver import blinker with suppress(Exception): use_blinker_ver = constants.SeleniumWire.BLINKER_VER if blinker.__version__ != use_blinker_ver: shared_utils.pip_install( "blinker", version=use_blinker_ver ) del blinker except Exception: shared_utils.pip_install( "blinker", version=constants.SeleniumWire.BLINKER_VER ) shared_utils.pip_install( "selenium-wire", version=constants.SeleniumWire.VER ) from seleniumwire import webdriver warnings.simplefilter("ignore", category=DeprecationWarning) else: from selenium import webdriver # Construct the address for connecting to a Selenium Grid if servername.startswith("https://"): protocol = "https" servername = servername.split("https://")[1] elif "://" in servername: servername = servername.split("://")[1] server_with_port = "" if ":" not in servername: col_port = ":" + str(port) first_slash = servername.find("/") if first_slash != -1: server_with_port = ( servername[:first_slash] + col_port + servername[first_slash:] ) else: server_with_port = servername + col_port else: server_with_port = servername address = "%s://%s" % (protocol, server_with_port) if not address.endswith("/wd/hub"): if address.endswith("/"): address += "wd/hub" else: address += "/wd/hub" downloads_path = DOWNLOADS_FOLDER desired_caps = {} extra_caps = {} if cap_file: from seleniumbase.core import capabilities_parser desired_caps = capabilities_parser.get_desired_capabilities(cap_file) if cap_string: try: extra_caps = json.loads(str(cap_string)) except Exception as e: p1 = "Invalid input format for --cap-string:\n %s" % e p2 = "The --cap-string input was: %s" % cap_string p3 = ( "Enclose cap-string in SINGLE quotes; " "keys and values in DOUBLE quotes." ) p4 = ( """Here's an example of correct cap-string usage:\n """ """--cap-string='{"browserName":"chrome","name":"test1"}'""" ) raise Exception("%s\n%s\n%s\n%s" % (p1, p2, p3, p4)) for cap_key in extra_caps.keys(): desired_caps[cap_key] = extra_caps[cap_key] if cap_file or cap_string: if "name" in desired_caps.keys(): if desired_caps["name"] == "*": desired_caps["name"] = test_id if browser_name == constants.Browser.GOOGLE_CHROME: chrome_options = _set_chrome_options( browser_name, downloads_path, headless, locale_code, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, servername, mobile_emulator, device_width, device_height, device_pixel_ratio, ) capabilities = webdriver.ChromeOptions().to_capabilities() # Set custom desired capabilities selenoid = False selenoid_options = None screen_resolution = None browser_version = None platform_name = None extension_capabilities = {} for key in desired_caps.keys(): capabilities[key] = desired_caps[key] if key == "selenoid:options": selenoid = True selenoid_options = desired_caps[key] elif key == "screenResolution": screen_resolution = desired_caps[key] elif key == "version" or key == "browserVersion": browser_version = desired_caps[key] elif key == "platform" or key == "platformName": platform_name = desired_caps[key] elif re.match("[a-zA-Z0-9]*:[a-zA-Z0-9]*", key): extension_capabilities[key] = desired_caps[key] cap_str = str(desired_caps).lower() if "browserstack" in cap_str or "bstack" in cap_str: chrome_options.set_capability("bstack:options", desired_caps) chrome_options.set_capability("cloud:options", capabilities) if selenoid: snops = selenoid_options chrome_options.set_capability("selenoid:options", snops) if screen_resolution: scres = screen_resolution chrome_options.set_capability("screenResolution", scres) if browser_version: br_vers = browser_version chrome_options.set_capability("browserVersion", br_vers) if platform_name: plat_name = platform_name chrome_options.set_capability("platformName", plat_name) if extension_capabilities: for key in extension_capabilities: ext_caps = extension_capabilities chrome_options.set_capability(key, ext_caps[key]) driver = webdriver.Remote( command_executor=address, options=chrome_options, ) return extend_driver(driver) elif browser_name == constants.Browser.FIREFOX: firefox_options = _set_firefox_options( downloads_path, headless, locale_code, proxy_string, proxy_bypass_list, proxy_pac_url, user_agent, disable_cookies, disable_js, disable_csp, firefox_arg, firefox_pref, external_pdf, ) capabilities = webdriver.FirefoxOptions().to_capabilities() capabilities["marionette"] = True if IS_LINUX and headless: capabilities["moz:firefoxOptions"] = {"args": ["-headless"]} # Set custom desired capabilities selenoid = False selenoid_options = None screen_resolution = None browser_version = None platform_name = None extension_capabilities = {} for key in desired_caps.keys(): capabilities[key] = desired_caps[key] if key == "selenoid:options": selenoid = True selenoid_options = desired_caps[key] elif key == "screenResolution": screen_resolution = desired_caps[key] elif key == "version" or key == "browserVersion": browser_version = desired_caps[key] elif key == "platform" or key == "platformName": platform_name = desired_caps[key] elif re.match("[a-zA-Z0-9]*:[a-zA-Z0-9]*", key): extension_capabilities[key] = desired_caps[key] cap_str = str(desired_caps).lower() if "browserstack" in cap_str or "bstack" in cap_str: firefox_options.set_capability("bstack:options", desired_caps) firefox_options.set_capability("cloud:options", capabilities) if selenoid: snops = selenoid_options firefox_options.set_capability("selenoid:options", snops) if screen_resolution: scres = screen_resolution firefox_options.set_capability("screenResolution", scres) if browser_version: br_vers = browser_version firefox_options.set_capability("browserVersion", br_vers) if platform_name: plat_name = platform_name firefox_options.set_capability("platformName", plat_name) if extension_capabilities: for key in extension_capabilities: ext_caps = extension_capabilities firefox_options.set_capability(key, ext_caps[key]) driver = webdriver.Remote( command_executor=address, options=firefox_options, ) return extend_driver(driver) elif browser_name == constants.Browser.INTERNET_EXPLORER: capabilities = webdriver.DesiredCapabilities.INTERNETEXPLORER remote_options = ArgOptions() remote_options.set_capability("cloud:options", desired_caps) driver = webdriver.Remote( command_executor=address, options=remote_options, ) return extend_driver(driver) elif browser_name == constants.Browser.EDGE: edge_options = _set_chrome_options( browser_name, downloads_path, headless, locale_code, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, servername, mobile_emulator, device_width, device_height, device_pixel_ratio, ) capabilities = webdriver.EdgeOptions().to_capabilities() # Set custom desired capabilities selenoid = False selenoid_options = None screen_resolution = None browser_version = None platform_name = None extension_capabilities = {} for key in desired_caps.keys(): capabilities[key] = desired_caps[key] if key == "selenoid:options": selenoid = True selenoid_options = desired_caps[key] elif key == "screenResolution": screen_resolution = desired_caps[key] elif key == "version" or key == "browserVersion": browser_version = desired_caps[key] elif key == "platform" or key == "platformName": platform_name = desired_caps[key] elif re.match("[a-zA-Z0-9]*:[a-zA-Z0-9]*", key): extension_capabilities[key] = desired_caps[key] edge_options.set_capability("cloud:options", capabilities) if selenoid: snops = selenoid_options edge_options.set_capability("selenoid:options", snops) if screen_resolution: scres = screen_resolution edge_options.set_capability("screenResolution", scres) if browser_version: br_vers = browser_version edge_options.set_capability("browserVersion", br_vers) if platform_name: plat_name = platform_name edge_options.set_capability("platformName", plat_name) if extension_capabilities: for key in extension_capabilities: ext_caps = extension_capabilities edge_options.set_capability(key, ext_caps[key]) driver = webdriver.Remote( command_executor=address, options=edge_options, ) return extend_driver(driver) elif browser_name == constants.Browser.SAFARI: capabilities = webdriver.DesiredCapabilities.SAFARI remote_options = ArgOptions() remote_options.set_capability("cloud:options", desired_caps) driver = webdriver.Remote( command_executor=address, options=remote_options, ) return extend_driver(driver) elif browser_name == constants.Browser.REMOTE: remote_options = ArgOptions() for cap_name, cap_value in desired_caps.items(): remote_options.set_capability(cap_name, cap_value) cap_str = str(desired_caps).lower() if "browserstack" in cap_str or "bstack" in cap_str: remote_options.set_capability("bstack:options", desired_caps) driver = webdriver.Remote( command_executor=address, options=remote_options, ) return extend_driver(driver) def get_local_driver( browser_name, headless, locale_code, servername, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, firefox_arg, firefox_pref, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, mobile_emulator, device_width, device_height, device_pixel_ratio, ): """Spins up a new web browser and returns the driver. Can also be used to spin up additional browsers for the same test.""" downloads_path = DOWNLOADS_FOLDER driver_dir = DRIVER_DIR special_chrome = False if binary_location: if ( binary_location == "_chromium_" or "chromium_drivers" in binary_location ): special_chrome = True driver_dir = DRIVER_DIR_CHROMIUM elif binary_location == "cft" or "cft_drivers" in binary_location: special_chrome = True driver_dir = DRIVER_DIR_CFT elif binary_location == "chs" or "chs_drivers" in binary_location: special_chrome = True driver_dir = DRIVER_DIR_CHS elif _special_binary_exists(binary_location, "opera"): special_chrome = True driver_dir = DRIVER_DIR_OPERA elif _special_binary_exists(binary_location, "brave"): special_chrome = True driver_dir = DRIVER_DIR_BRAVE elif _special_binary_exists(binary_location, "comet"): special_chrome = True driver_dir = DRIVER_DIR_COMET elif _special_binary_exists(binary_location, "atlas"): special_chrome = True driver_dir = DRIVER_DIR_ATLAS if ( hasattr(sb_config, "settings") and getattr(sb_config.settings, "NEW_DRIVER_DIR", None) and os.path.exists(sb_config.settings.NEW_DRIVER_DIR) ): driver_dir = sb_config.settings.NEW_DRIVER_DIR elif special_chrome: override_driver_dir(driver_dir) if IS_MAC or IS_LINUX: local_chromedriver = driver_dir + "/chromedriver" local_geckodriver = driver_dir + "/geckodriver" local_edgedriver = driver_dir + "/msedgedriver" local_uc_driver = driver_dir + "/uc_driver" elif IS_WINDOWS: local_edgedriver = driver_dir + "/msedgedriver.exe" local_iedriver = driver_dir + "/IEDriverServer.exe" local_headless_iedriver = driver_dir + "/headless_ie_selenium.exe" local_chromedriver = driver_dir + "/chromedriver.exe" local_geckodriver = driver_dir + "/geckodriver.exe" local_uc_driver = driver_dir + "/uc_driver.exe" b_path = binary_location use_uc = is_using_uc(undetectable, browser_name) if use_wire: pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) with pip_find_lock: # Prevent issues with multiple processes with suppress(Exception): shared_utils.make_writable(constants.PipInstall.FINDLOCK) try: from seleniumwire import webdriver import blinker with suppress(Exception): use_blinker_ver = constants.SeleniumWire.BLINKER_VER if blinker.__version__ != use_blinker_ver: shared_utils.pip_install( "blinker", version=use_blinker_ver ) del blinker except Exception: shared_utils.pip_install( "blinker", version=constants.SeleniumWire.BLINKER_VER ) shared_utils.pip_install( "selenium-wire", version=constants.SeleniumWire.VER ) from seleniumwire import webdriver warnings.simplefilter("ignore", category=DeprecationWarning) else: from selenium import webdriver if browser_name == constants.Browser.FIREFOX: firefox_options = _set_firefox_options( downloads_path, headless, locale_code, proxy_string, proxy_bypass_list, proxy_pac_url, user_agent, disable_cookies, disable_js, disable_csp, firefox_arg, firefox_pref, external_pdf, ) if local_geckodriver and os.path.exists(local_geckodriver): try: make_driver_executable_if_not(local_geckodriver) except Exception as e: logging.debug( "\nWarning: Could not make geckodriver" " executable: %s" % e ) elif not geckodriver_on_path(): from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ("-n" in sys.argv or " -n=" in args or args == "-c"): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args log_d("\nWarning: geckodriver not found. Getting it now:") try: sb_install.main(override="geckodriver") except Exception as e: log_d("\nWarning: Could not install geckodriver: %s" % e) sys.argv = sys_args # Put back the original sys args else: geckodriver_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with geckodriver_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) if not geckodriver_on_path(): sys_args = sys.argv # Save a copy of sys args log_d( "\nWarning: geckodriver not found. " "Getting it now:" ) sb_install.main(override="geckodriver") sys.argv = sys_args # Put back original sys args # Launch Firefox if os.path.exists(local_geckodriver): service = FirefoxService( executable_path=local_geckodriver, log_output=os.devnull, ) try: driver = webdriver.Firefox( service=service, options=firefox_options, ) return extend_driver(driver) except BaseException as e: if ( "geckodriver unexpectedly exited" in str(e) or "Process unexpectedly closed" in str(e) or "Failed to read marionette port" in str(e) or "A connection attempt failed" in str(e) or "Expected browser binary" in str(e) or hasattr(e, "msg") and ( "geckodriver unexpectedly exited" in e.msg or "Process unexpectedly closed" in e.msg or "Failed to read marionette port" in e.msg or "A connection attempt failed" in e.msg or "Expected browser binary" in e.msg ) ): time.sleep(0.1) if ( IS_LINUX and headless and ( "unexpected" in str(e) or ( hasattr(e, "msg") and "unexpected" in e.msg ) ) ): firefox_options.add_argument("-headless") driver = webdriver.Firefox( service=service, options=firefox_options, ) return extend_driver(driver) else: raise # Not an obvious fix. else: service = FirefoxService(log_output=os.devnull) try: driver = webdriver.Firefox( service=service, options=firefox_options, ) return extend_driver(driver) except BaseException as e: if ( "geckodriver unexpectedly exited" in str(e) or "Process unexpectedly closed" in str(e) or "Failed to read marionette port" in str(e) or "A connection attempt failed" in str(e) or "Expected browser binary" in str(e) or hasattr(e, "msg") and ( "geckodriver unexpectedly exited" in e.msg or "Process unexpectedly closed" in e.msg or "Failed to read marionette port" in e.msg or "A connection attempt failed" in e.msg or "Expected browser binary" in e.msg ) ): time.sleep(0.1) if ( IS_LINUX and headless and ( "unexpected" in str(e) or ( hasattr(e, "msg") and "unexpected" in e.msg ) ) ): firefox_options.add_argument("-headless") driver = webdriver.Firefox( service=service, options=firefox_options, ) return extend_driver(driver) else: raise # Not an obvious fix. elif browser_name == constants.Browser.INTERNET_EXPLORER: if not IS_WINDOWS: raise Exception( "IE Browser is for Windows-based systems only!" ) from selenium.webdriver.ie.options import Options from selenium.webdriver.ie.service import Service ie_options = Options() ie_options.add_argument("--guest") ie_options.attach_to_edge_chrome = True ie_options.ignore_protected_mode_settings = True ie_options.ignore_zoom_level = True ie_options.require_window_focus = False ie_options.native_events = True ie_options.full_page_screenshot = True ie_options.persistent_hover = True if local_iedriver and os.path.exists(local_iedriver): try: make_driver_executable_if_not(local_iedriver) except Exception as e: logging.debug( "\nWarning: Could not make IEDriver executable: %s" % e ) elif not iedriver_on_path(): from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ("-n" in sys.argv or " -n=" in args or args == "-c"): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args log_d("\nWarning: IEDriver not found. Getting it now:") sb_install.main(override="iedriver") sys.argv = sys_args # Put back the original sys args if local_headless_iedriver and os.path.exists(local_headless_iedriver): try: make_driver_executable_if_not(local_headless_iedriver) except Exception as e: logging.debug( "\nWarning: Could not make HeadlessIEDriver executable: %s" % e ) elif not headless_iedriver_on_path(): from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ("-n" in sys.argv or " -n=" in args or args == "-c"): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args log_d("\nWarning: HeadlessIEDriver not found. Getting it now:") sb_install.main(override="iedriver") sys.argv = sys_args # Put back the original sys args d_b_c = "--disable-build-check" logger = logging.getLogger("selenium") logger.setLevel("INFO") if not headless: warnings.simplefilter("ignore", category=DeprecationWarning) service = Service(service_args=[d_b_c], log_output=os.devnull) driver = webdriver.Ie(service=service, options=ie_options) return extend_driver(driver) else: warnings.simplefilter("ignore", category=DeprecationWarning) service = Service( executable_path=local_iedriver, service_args=[d_b_c], log_output=os.devnull, ) driver = webdriver.Ie(service=service, options=ie_options) return extend_driver(driver) elif browser_name == constants.Browser.EDGE: prefs = { "download.default_directory": downloads_path, "download.directory_upgrade": True, "download.prompt_for_download": False, "credentials_enable_service": False, "autofill.credit_card_enabled": False, "local_discovery.notifications_enabled": False, "safebrowsing.disable_download_protection": True, "safebrowsing.enabled": False, # Prevent PW "data breach" pop-ups "omnibox-max-zero-suggest-matches": 0, "omnibox-use-existing-autocomplete-client": 0, "omnibox-trending-zero-prefix-suggestions-on-ntp": 0, "omnibox-local-history-zero-suggest-beyond-ntp": 0, "omnibox-on-focus-suggestions-contextual-web": 0, "omnibox-on-focus-suggestions-srp": 0, "omnibox-zero-suggest-prefetching": 0, "omnibox-zero-suggest-prefetching-on-srp": 0, "omnibox-zero-suggest-prefetching-on-web": 0, "omnibox-zero-suggest-in-memory-caching": 0, "content_settings.exceptions.automatic_downloads.*.setting": 1, "default_content_setting_values.notifications": 0, "default_content_settings.popups": 0, "managed_default_content_settings.popups": 0, "profile.password_manager_enabled": False, "profile.password_manager_leak_detection": False, "profile.default_content_setting_values.notifications": 2, "profile.default_content_settings.popups": 0, "profile.managed_default_content_settings.popups": 0, "profile.default_content_setting_values.automatic_downloads": 1, } use_version = "latest" major_edge_version = None saved_mev = None use_br_version_for_edge = False use_exact_version_for_edge = False try: if binary_location: try: major_edge_version = ( detect_b_ver.get_browser_version_from_binary( binary_location ) ) saved_mev = major_edge_version major_edge_version = saved_mev.split(".")[0] if len(major_edge_version) < 2: major_edge_version = None except Exception: major_edge_version = None if not major_edge_version: br_app = "edge" major_edge_version = ( detect_b_ver.get_browser_version_from_os(br_app) ) saved_mev = major_edge_version major_edge_version = major_edge_version.split(".")[0] if int(major_edge_version) < 80: major_edge_version = None elif int(major_edge_version) >= 115: if ( driver_version == "browser" and saved_mev and len(saved_mev.split(".")) == 4 ): driver_version = saved_mev use_br_version_for_edge = True except Exception: major_edge_version = None if driver_version and "." in driver_version: use_exact_version_for_edge = True if use_br_version_for_edge: major_edge_version = saved_mev if major_edge_version: use_version = major_edge_version edge_driver_version = None edgedriver_upgrade_needed = False if os.path.exists(local_edgedriver): with suppress(Exception): output = subprocess.check_output( '"%s" --version' % local_edgedriver, shell=True ) if IS_WINDOWS: output = output.decode("latin1") else: output = output.decode("utf-8") if output.split(" ")[0] == "MSEdgeDriver": # MSEdgeDriver VERSION output = output.split(" ")[1] if use_exact_version_for_edge: edge_driver_version = output.split(" ")[0] output = output.split(".")[0] elif output.split(" ")[0] == "Microsoft": output = output.split(" ")[3] if use_exact_version_for_edge: edge_driver_version = output.split(" ")[0] output = output.split(".")[0] else: output = 0 if int(output) >= 2: if not use_exact_version_for_edge: edge_driver_version = output if driver_version == "keep": driver_version = edge_driver_version use_version = find_edgedriver_version_to_use( use_version, driver_version ) local_edgedriver_exists = False if local_edgedriver and os.path.exists(local_edgedriver): local_edgedriver_exists = True if ( use_version != "latest" and edge_driver_version and use_version != edge_driver_version ): edgedriver_upgrade_needed = True else: try: make_driver_executable_if_not(local_edgedriver) except Exception as e: logging.debug( "\nWarning: Could not make edgedriver" " executable: %s" % e ) if not local_edgedriver_exists or edgedriver_upgrade_needed: from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ("-n" in sys.argv or " -n=" in args or args == "-c"): # (Not multithreaded) msg = "Microsoft Edge Driver not found." if edgedriver_upgrade_needed: msg = "Microsoft Edge Driver update needed." sys_args = sys.argv # Save a copy of current sys args log_d("\n%s Getting it now:" % msg) sb_install.main(override="edgedriver %s" % use_version) sys.argv = sys_args # Put back the original sys args else: edgedriver_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with edgedriver_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) msg = "Microsoft Edge Driver not found." if edgedriver_upgrade_needed: msg = "Microsoft Edge Driver update needed." sys_args = sys.argv # Save a copy of current sys args log_d("\n%s Getting it now:" % msg) sb_install.main(override="edgedriver %s" % use_version) sys.argv = sys_args # Put back the original sys args # For Microsoft Edge (Chromium) version 80 or higher Edge = webdriver.Edge EdgeOptions = webdriver.EdgeOptions if local_edgedriver and os.path.exists(local_edgedriver): try: make_driver_executable_if_not(local_edgedriver) except Exception as e: logging.debug( "\nWarning: Could not make edgedriver" " executable: %s" % e ) edge_options = EdgeOptions() edge_options.use_chromium = True if locale_code: prefs["intl.accept_languages"] = locale_code if block_images: prefs["profile.managed_default_content_settings.images"] = 2 if disable_cookies: prefs["profile.default_content_setting_values.cookies"] = 2 if disable_js: prefs["profile.managed_default_content_settings.javascript"] = 2 if do_not_track: prefs["enable_do_not_track"] = True if external_pdf: prefs["plugins.always_open_pdf_externally"] = True pdce = "user_experience_metrics.personalization_data_consent_enabled" prefs[pdce] = True # Remove "Personalize your web experience" prompt edge_options.add_experimental_option("prefs", prefs) edge_options.add_argument( "--disable-blink-features=AutomationControlled" ) edge_options.add_experimental_option( "excludeSwitches", ["enable-automation", "enable-logging"] ) if log_cdp_events: edge_options.set_capability( "ms:loggingPrefs", {"performance": "ALL", "browser": "ALL"} ) if host_resolver_rules: edge_options.add_argument( "--host-resolver-rules=%s" % host_resolver_rules ) if not enable_sync: edge_options.add_argument("--disable-sync") if ( not recorder_ext and not disable_csp and not proxy_auth ): edge_options.add_argument("--guest") if dark_mode: edge_options.add_argument("--enable-features=WebContentsForceDark") if headless1: # developer.chrome.com/blog/removing-headless-old-from-chrome with suppress(Exception): if int(str(use_version).split(".")[0]) >= 132: headless1 = False headless2 = True if headless2: try: if use_version == "latest" or int(use_version) >= 109: edge_options.add_argument("--headless=new") else: edge_options.add_argument("--headless=chrome") except Exception: edge_options.add_argument("--headless=new") elif headless and undetectable: # (For later: UC Mode doesn't support Edge now) with suppress(Exception): if int(use_version) >= 109: edge_options.add_argument("--headless=new") elif ( int(use_version) >= 96 and int(use_version) <= 108 ): edge_options.add_argument("--headless=chrome") else: pass # Will need Xvfb on Linux elif headless: if ( "--headless" not in edge_options.arguments and "--headless=old" not in edge_options.arguments ): if headless1: edge_options.add_argument("--headless=old") else: edge_options.add_argument("--headless") if mobile_emulator and not use_uc: emulator_settings = {} device_metrics = {} if ( isinstance(device_width, int) and isinstance(device_height, int) and isinstance(device_pixel_ratio, (int, float)) ): device_metrics["width"] = device_width device_metrics["height"] = device_height device_metrics["pixelRatio"] = device_pixel_ratio else: device_metrics["width"] = constants.Mobile.WIDTH device_metrics["height"] = constants.Mobile.HEIGHT device_metrics["pixelRatio"] = constants.Mobile.RATIO emulator_settings["deviceMetrics"] = device_metrics if user_agent: emulator_settings["userAgent"] = user_agent edge_options.add_experimental_option( "mobileEmulation", emulator_settings ) # Handle Window Position if (headless or headless2) and IS_WINDOWS: # https://stackoverflow.com/a/78999088/7058266 edge_options.add_argument("--window-position=-2400,-2400") else: if ( hasattr(settings, "WINDOW_START_X") and isinstance(settings.WINDOW_START_X, int) and hasattr(settings, "WINDOW_START_Y") and isinstance(settings.WINDOW_START_Y, int) ): edge_options.add_argument( "--window-position=%s,%s" % ( settings.WINDOW_START_X, settings.WINDOW_START_Y ) ) # Handle Window Size if headless or headless2: if ( hasattr(settings, "HEADLESS_START_WIDTH") and isinstance(settings.HEADLESS_START_WIDTH, int) and hasattr(settings, "HEADLESS_START_HEIGHT") and isinstance(settings.HEADLESS_START_HEIGHT, int) ): edge_options.add_argument( "--window-size=%s,%s" % ( settings.HEADLESS_START_WIDTH, settings.HEADLESS_START_HEIGHT, ) ) else: if ( hasattr(settings, "CHROME_START_WIDTH") and isinstance(settings.CHROME_START_WIDTH, int) and hasattr(settings, "CHROME_START_HEIGHT") and isinstance(settings.CHROME_START_HEIGHT, int) ): edge_options.add_argument( "--window-size=%s,%s" % ( settings.CHROME_START_WIDTH, settings.CHROME_START_HEIGHT, ) ) if user_data_dir and not use_uc: abs_path = os.path.abspath(user_data_dir) edge_options.add_argument("--user-data-dir=%s" % abs_path) if extension_zip: # Can be a comma-separated list of .ZIP or .CRX files extension_zip_list = extension_zip.split(",") for extension_zip_item in extension_zip_list: abs_path = os.path.realpath(extension_zip_item) edge_options.add_extension(abs_path) if extension_dir: # load-extension input can be a comma-separated list abs_path = ( ",".join(os.path.realpath(p) for p in extension_dir.split(",")) ) edge_options = add_chrome_ext_dir(edge_options, abs_path) edge_options.add_argument("--disable-infobars") edge_options.add_argument("--disable-notifications") edge_options.add_argument("--disable-save-password-bubble") edge_options.add_argument("--disable-single-click-autofill") edge_options.add_argument( "--disable-autofill-keyboard-accessory-view[8]" ) edge_options.add_argument("--safebrowsing-disable-download-protection") edge_options.add_argument("--disable-search-engine-choice-screen") edge_options.add_argument("--disable-browser-side-navigation") edge_options.add_argument("--disable-translate") if not enable_ws: edge_options.add_argument("--disable-web-security") edge_options.add_argument("--homepage=about:blank") edge_options.add_argument("--dns-prefetch-disable") edge_options.add_argument("--dom-automation") edge_options.add_argument("--disable-hang-monitor") edge_options.add_argument("--disable-prompt-on-repost") if not enable_3d_apis: edge_options.add_argument("--disable-3d-apis") if headless or headless2 or use_uc: edge_options.add_argument("--disable-renderer-backgrounding") edge_options.add_argument("--disable-backgrounding-occluded-windows") edge_options.add_argument("--disable-client-side-phishing-detection") edge_options.add_argument("--disable-oopr-debug-crash-dump") edge_options.add_argument("--disable-top-sites") edge_options.add_argument("--ash-no-nudges") edge_options.add_argument("--no-crash-upload") edge_options.add_argument("--deny-permission-prompts") if ( page_load_strategy and page_load_strategy.lower() in ["eager", "none"] ): # Only change it if not "normal", which is the default. edge_options.page_load_strategy = page_load_strategy.lower() elif ( not page_load_strategy and getattr(settings, "PAGE_LOAD_STRATEGY", None) and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"] ): # Only change it if not "normal", which is the default. edge_options.page_load_strategy = ( settings.PAGE_LOAD_STRATEGY.lower() ) if (settings.DISABLE_CSP_ON_CHROME or disable_csp) and not headless: # Headless Edge doesn't support extensions, which are required # for disabling the Content Security Policy on Edge edge_options = _add_chrome_disable_csp_extension(edge_options) if ad_block_on and not headless: edge_options = _add_chrome_ad_block_extension(edge_options) if recorder_ext and not headless: edge_options = _add_chrome_recorder_extension(edge_options) if proxy_string: if proxy_auth: edge_options = _add_chrome_proxy_extension( edge_options, proxy_string, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, zip_it=True, multi_proxy=multi_proxy, ) edge_options.add_argument("--proxy-server=%s" % proxy_string) if proxy_bypass_list: edge_options.add_argument( "--proxy-bypass-list=%s" % proxy_bypass_list ) elif proxy_pac_url: if proxy_auth: edge_options = _add_chrome_proxy_extension( edge_options, None, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, zip_it=True, multi_proxy=multi_proxy, ) edge_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url) edge_options.add_argument("--test-type") edge_options.add_argument("--log-level=3") edge_options.add_argument("--no-first-run") edge_options.add_argument("--ignore-certificate-errors") edge_options.add_argument("--ignore-ssl-errors=yes") if devtools and not headless: edge_options.add_argument("--auto-open-devtools-for-tabs") edge_options.add_argument("--allow-file-access-from-files") edge_options.add_argument("--allow-insecure-localhost") edge_options.add_argument("--allow-running-insecure-content") if user_agent: edge_options.add_argument("--user-agent=%s" % user_agent) if IS_LINUX or (IS_MAC and not use_uc): edge_options.add_argument("--no-sandbox") if remote_debug: # To access the Debugger, go to: edge://inspect/#devices # while a Chromium driver is running. # Info: https://chromedevtools.github.io/devtools-protocol/ args = " ".join(sys.argv) free_port = 9222 if ("-n" in sys.argv or " -n=" in args or args == "-c"): free_port = service_utils.free_port() edge_options.add_argument("--remote-debugging-port=%s" % free_port) if swiftshader: edge_options.add_argument("--use-gl=angle") edge_options.add_argument("--use-angle=swiftshader-webgl") elif not use_uc and not enable_3d_apis: edge_options.add_argument("--disable-gpu") if IS_LINUX: edge_options.add_argument("--disable-dev-shm-usage") extra_disabled_features = [] set_binary = False if chromium_arg: # Can be a comma-separated list of Chromium args chromium_arg_list = None if isinstance(chromium_arg, (list, tuple)): chromium_arg_list = chromium_arg else: chromium_arg_list = chromium_arg.split(",") for chromium_arg_item in chromium_arg_list: chromium_arg_item = chromium_arg_item.strip() if not chromium_arg_item.startswith("--"): if chromium_arg_item.startswith("-"): chromium_arg_item = "-" + chromium_arg_item else: chromium_arg_item = "--" + chromium_arg_item if "set-binary" in chromium_arg_item: set_binary = True elif "disable-features=" in chromium_arg_item: d_f = chromium_arg_item.split("disable-features=")[-1] extra_disabled_features.append(d_f) elif len(chromium_arg_item) >= 3: edge_options.add_argument(chromium_arg_item) if disable_features: extra_disabled_features.extend(disable_features.split(",")) edge_options.add_argument( '--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"' ) edge_options.add_argument("--disable-ipc-flooding-protection") edge_options.add_argument("--disable-password-generation") edge_options.add_argument("--disable-domain-reliability") edge_options.add_argument("--disable-breakpad") included_disabled_features = [] included_disabled_features.append("OptimizationHints") included_disabled_features.append("OptimizationHintsFetching") included_disabled_features.append("Translate") included_disabled_features.append("ComponentUpdater") included_disabled_features.append("OptimizationTargetPrediction") included_disabled_features.append("OptimizationGuideModelDownloading") included_disabled_features.append("InsecureDownloadWarnings") included_disabled_features.append("InterestFeedContentSuggestions") included_disabled_features.append("PrivacySandboxSettings4") included_disabled_features.append("SidePanelPinning") included_disabled_features.append("UserAgentClientHint") included_disabled_features.append( "DisableLoadExtensionCommandLineSwitch" ) included_disabled_features.append("Bluetooth") included_disabled_features.append("WebBluetooth") included_disabled_features.append("UnifiedWebBluetooth") included_disabled_features.append("WebAuthentication") included_disabled_features.append("PasskeyAuth") for item in extra_disabled_features: if item not in included_disabled_features: included_disabled_features.append(item) d_f_string = ",".join(included_disabled_features) edge_options.add_argument("--disable-features=%s" % d_f_string) if (set_binary or IS_LINUX) and not binary_location: br_app = "edge" binary_loc = detect_b_ver.get_binary_location(br_app) if os.path.exists(binary_loc): binary_location = binary_loc if binary_location: edge_options.binary_location = binary_location service = EdgeService( executable_path=local_edgedriver, log_output=os.devnull, service_args=["--disable-build-check"], ) try: driver = Edge(service=service, options=edge_options) except Exception as e: if not hasattr(e, "msg"): raise auto_upgrade_edgedriver = False edge_version = None if ( "This version of MSEdgeDriver only supports" in e.msg or "This version of Microsoft Edge WebDriver" in e.msg ): if "Current browser version is " in e.msg: auto_upgrade_edgedriver = True edge_version = e.msg.split( "Current browser version is " )[1].split(" ")[0] elif "only supports MSEdge version " in e.msg: auto_upgrade_edgedriver = True edge_version = e.msg.split( "only supports MSEdge version " )[1].split(" ")[0] elif "DevToolsActivePort file doesn't exist" in e.msg: # https://stackoverflow.com/a/56638103/7058266 args = " ".join(sys.argv) free_port = 9222 if ("-n" in sys.argv or " -n=" in args or args == "-c"): free_port = service_utils.free_port() edge_options.add_argument( "--remote-debugging-port=%s" % free_port ) driver = Edge(service=service, options=edge_options) return extend_driver(driver) if not auto_upgrade_edgedriver: raise # Not an obvious fix. else: pass # Try upgrading EdgeDriver to match Edge. args = " ".join(sys.argv) if "-n" in sys.argv or " -n=" in args or args == "-c": edgedriver_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with edgedriver_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with suppress(Exception): if not _was_driver_repaired(): _repair_edgedriver(edge_version) _mark_driver_repaired() else: with suppress(Exception): if not _was_driver_repaired(): _repair_edgedriver(edge_version) _mark_driver_repaired() driver = Edge(service=service, options=edge_options) return extend_driver(driver) elif browser_name == constants.Browser.SAFARI: args = " ".join(sys.argv) if ("-n" in sys.argv or " -n=" in args or args == "-c"): # Skip if multithreaded raise Exception("Can't run Safari tests in multithreaded mode!") warnings.simplefilter("ignore", category=DeprecationWarning) from selenium.webdriver.safari.options import Options as SafariOptions service = SafariService(quiet=False) options = SafariOptions() if ( page_load_strategy and page_load_strategy.lower() in ["eager", "none"] ): # Only change it if not "normal", which is the default. options.page_load_strategy = page_load_strategy.lower() elif ( not page_load_strategy and getattr(settings, "PAGE_LOAD_STRATEGY", None) and settings.PAGE_LOAD_STRATEGY.lower() in ["eager", "none"] ): # Only change it if not "normal", which is the default. options.page_load_strategy = settings.PAGE_LOAD_STRATEGY.lower() driver = webdriver.safari.webdriver.WebDriver( service=service, options=options ) return extend_driver(driver) elif browser_name == constants.Browser.GOOGLE_CHROME: set_chromium = None if _special_binary_exists(binary_location, "opera"): set_chromium = "opera" local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver" if IS_WINDOWS: local_chromedriver = DRIVER_DIR_OPERA + "/chromedriver.exe" if _special_binary_exists(binary_location, "brave"): set_chromium = "brave" local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver" if IS_WINDOWS: local_chromedriver = DRIVER_DIR_BRAVE + "/chromedriver.exe" if _special_binary_exists(binary_location, "comet"): set_chromium = "comet" local_chromedriver = DRIVER_DIR_COMET + "/chromedriver" if IS_WINDOWS: local_chromedriver = DRIVER_DIR_COMET + "/chromedriver.exe" if _special_binary_exists(binary_location, "atlas"): set_chromium = "atlas" local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver" if IS_WINDOWS: local_chromedriver = DRIVER_DIR_ATLAS + "/chromedriver.exe" try: chrome_options = _set_chrome_options( browser_name, downloads_path, headless, locale_code, proxy_string, proxy_auth, proxy_user, proxy_pass, proxy_scheme, proxy_bypass_list, proxy_pac_url, multi_proxy, user_agent, recorder_ext, disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, undetectable, uc_cdp_events, uc_subprocess, log_cdp_events, no_sandbox, disable_gpu, headless1, headless2, incognito, guest_mode, dark_mode, devtools, remote_debug, enable_3d_apis, swiftshader, ad_block_on, host_resolver_rules, block_images, do_not_track, chromium_arg, user_data_dir, extension_zip, extension_dir, disable_features, binary_location, driver_version, page_load_strategy, use_wire, external_pdf, servername, mobile_emulator, device_width, device_height, device_pixel_ratio, ) if binary_location and "chromium_drivers" in binary_location: chrome_options.add_argument("--use-mock-keychain") use_version = "latest" major_chrome_version = None saved_mcv = None full_ch_version = None full_ch_driver_version = None use_br_version_for_uc = False try: if chrome_options.binary_location: try: major_chrome_version = ( detect_b_ver.get_browser_version_from_binary( chrome_options.binary_location, ) ) saved_mcv = major_chrome_version major_chrome_version = saved_mcv.split(".")[0] if len(major_chrome_version) < 2: major_chrome_version = None except Exception: major_chrome_version = None if not major_chrome_version: br_app = "google-chrome" full_ch_version = ( detect_b_ver.get_browser_version_from_os(br_app) ) saved_mcv = full_ch_version major_chrome_version = full_ch_version.split(".")[0] if int(major_chrome_version) < 67: major_chrome_version = None elif ( int(major_chrome_version) >= 67 and int(major_chrome_version) <= 72 ): # chromedrivers 2.41 - 2.46 could be swapped with 72 major_chrome_version = "72" elif int(major_chrome_version) >= 115: if ( driver_version == "browser" and saved_mcv and len(saved_mcv.split(".")) == 4 ): driver_version = saved_mcv if use_uc: use_br_version_for_uc = True if ( (headless or headless2) and IS_WINDOWS and major_chrome_version and int(major_chrome_version) >= 117 and not use_uc and not (remote_debug or devtools or use_wire) and not (proxy_string or multi_proxy or proxy_pac_url) and (not chromium_arg or "debug" not in chromium_arg) and (not servername or servername == "localhost") ): # Hide the "DevTools listening on ..." message. # https://bugs.chromium.org # /p/chromedriver/issues/detail?id=4403#c35 # (Only when the remote debugging port is not needed.) chrome_options.add_argument("--remote-debugging-pipe") except Exception: major_chrome_version = None if major_chrome_version: use_version = major_chrome_version if ( set_chromium == "opera" and use_version.isnumeric() and int(use_version) < 130 ): use_version = "130" # Special case ch_driver_version = None path_chromedriver = chromedriver_on_path() if os.path.exists(local_chromedriver): with suppress(Exception): output = subprocess.check_output( '"%s" --version' % local_chromedriver, shell=True ) if IS_WINDOWS: output = output.decode("latin1") else: output = output.decode("utf-8") full_ch_driver_version = output.split(" ")[1] output = full_ch_driver_version.split(".")[0] if int(output) >= 2: ch_driver_version = output if driver_version == "keep": driver_version = ch_driver_version elif path_chromedriver and not set_chromium: try: make_driver_executable_if_not(path_chromedriver) except Exception as e: logging.debug( "\nWarning: Could not make chromedriver" " executable: %s" % e ) with suppress(Exception): output = subprocess.check_output( '"%s" --version' % path_chromedriver, shell=True ) if IS_WINDOWS: output = output.decode("latin1") else: output = output.decode("utf-8") full_ch_driver_version = output.split(" ")[1] output = full_ch_driver_version.split(".")[0] if int(output) >= 2: ch_driver_version = output if driver_version == "keep": use_version = ch_driver_version disable_build_check = True uc_driver_version = None if use_uc: if use_br_version_for_uc or driver_version == "mlatest": uc_driver_version = get_uc_driver_version( full=True, local_uc_driver=local_uc_driver ) full_ch_driver_version = uc_driver_version else: uc_driver_version = get_uc_driver_version( local_uc_driver=local_uc_driver ) if multi_proxy: sb_config.multi_proxy = True if uc_driver_version and driver_version == "keep": driver_version = uc_driver_version use_version = find_chromedriver_version_to_use( use_version, driver_version ) if headless1: # developer.chrome.com/blog/removing-headless-old-from-chrome with suppress(Exception): if int(str(use_version).split(".")[0]) >= 132: headless1 = False headless2 = True if headless2: try: if ( use_version == "latest" or int(str(use_version).split(".")[0]) >= 109 ): chrome_options.add_argument("--headless=new") else: chrome_options.add_argument("--headless=chrome") except Exception: chrome_options.add_argument("--headless=new") elif headless and undetectable: try: int_use_version = int(str(use_version).split(".")[0]) if int_use_version >= 109: chrome_options.add_argument("--headless=new") elif ( int_use_version >= 96 and int_use_version <= 108 ): chrome_options.add_argument("--headless=chrome") else: pass # Will need Xvfb on Linux except Exception: pass # Will need Xvfb on Linux elif headless: if ( "--headless" not in chrome_options.arguments and "--headless=old" not in chrome_options.arguments ): if headless1: chrome_options.add_argument("--headless=old") else: chrome_options.add_argument("--headless") if local_chromedriver and os.path.exists(local_chromedriver): try: make_driver_executable_if_not(local_chromedriver) except Exception as e: logging.debug( "\nWarning: Could not make chromedriver" " executable: %s" % e ) make_uc_driver_from_chromedriver = False local_ch_exists = ( local_chromedriver and os.path.exists(local_chromedriver) ) """If no local_chromedriver, but path_chromedriver, and the browser version nearly matches the driver version, then use the path_chromedriver instead of downloading a new driver. Eg. 116.0.* for both is close, but not 116.0.* and 116.1.*""" browser_driver_close_match = False if ( path_chromedriver and full_ch_version and full_ch_driver_version ): full_ch_v_p = full_ch_version.split(".")[0:2] full_ch_driver_v_p = full_ch_driver_version.split(".")[0:2] if ( full_ch_v_p == full_ch_driver_v_p or driver_version == "keep" ): browser_driver_close_match = True one_off_chromium = False if ( hasattr(sb_config, "binary_location") and sb_config.binary_location == "_chromium_" ): with suppress(Exception): one_off_chromium_ver = int(use_version.split(".")[0]) - 1 if one_off_chromium_ver == int(ch_driver_version): one_off_chromium = True # If not ARM MAC and need to use uc_driver (and it's missing), # and already have chromedriver with the correct version, # then copy chromedriver to uc_driver (and it'll get patched). if ( not IS_ARM_MAC and use_uc and ( ( (local_ch_exists or path_chromedriver) and use_version == ch_driver_version and ( not os.path.exists(local_uc_driver) or uc_driver_version != use_version ) ) or ( local_ch_exists and use_version == "latest" and not os.path.exists(local_uc_driver) ) ) ): make_uc_driver_from_chromedriver = True elif ( (use_uc and not os.path.exists(local_uc_driver)) or (not use_uc and not path_chromedriver) or ( not use_uc and use_version != "latest" # Browser version detected and (ch_driver_version or not local_ch_exists) and ( ( use_version.split(".")[0] != ch_driver_version and not one_off_chromium ) or ( not local_ch_exists and use_version.isnumeric() and int(use_version) >= 115 and not browser_driver_close_match and not one_off_chromium ) ) ) or ( use_uc and use_version != "latest" # Browser version detected and uc_driver_version != use_version and not one_off_chromium ) or ( full_ch_driver_version # Also used for the uc_driver and driver_version and len(str(driver_version).split(".")) == 4 and full_ch_driver_version != driver_version and not one_off_chromium ) ): # chromedriver download needed in the seleniumbase/drivers dir from seleniumbase.console_scripts import sb_install args = " ".join(sys.argv) if not ("-n" in sys.argv or " -n=" in args or args == "-c"): # (Not multithreaded) sys_args = sys.argv # Save a copy of current sys args msg = "chromedriver update needed. Getting it now:" if not path_chromedriver: msg = "chromedriver not found. Getting it now:" if use_uc and not os.path.exists(local_uc_driver): msg = "uc_driver not found. Getting it now:" if use_uc and os.path.exists(local_uc_driver): msg = "uc_driver update needed. Getting it now:" log_d("\nWarning: %s" % msg) force_uc = False intel_for_uc = False if use_uc: force_uc = True if IS_ARM_MAC and use_uc: intel_for_uc = True # Use Intel's driver for UC Mode try: sb_install.main( override="chromedriver %s" % use_version, intel_for_uc=intel_for_uc, force_uc=force_uc, ) except Exception: d_latest = get_latest_chromedriver_version() if ( d_latest and use_version != "latest" and int(use_version) > int(d_latest.split(".")[0]) ): disable_build_check = True d_latest_major = d_latest.split(".")[0] if ( not path_chromedriver or ( ch_driver_version and ( int(ch_driver_version) < int(d_latest_major) ) ) ): sb_install.main(override="chromedriver latest") sys.argv = sys_args # Put back the original sys args else: # (Multithreaded) chromedriver_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with chromedriver_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) msg = "chromedriver update needed. Getting it now:" if not path_chromedriver: msg = "chromedriver not found. Getting it now:" if use_uc and not os.path.exists(local_uc_driver): msg = "uc_driver not found. Getting it now:" if use_uc and os.path.exists(local_uc_driver): msg = "uc_driver update needed. Getting it now:" force_uc = False intel_for_uc = False if use_uc: force_uc = True if IS_ARM_MAC and use_uc: intel_for_uc = True # Use Intel driver for UC Mode if os.path.exists(local_chromedriver): with suppress(Exception): output = subprocess.check_output( '"%s" --version' % local_chromedriver, shell=True, ) if IS_WINDOWS: output = output.decode("latin1") else: output = output.decode("utf-8") full_ch_driver_version = output.split(" ")[1] output = full_ch_driver_version.split(".")[0] if int(output) >= 2: ch_driver_version = output if ( ( not use_uc and not os.path.exists(local_chromedriver) ) or (use_uc and not os.path.exists(local_uc_driver)) or ( not use_uc and ( use_version.split(".")[0] != ch_driver_version ) ) or ( use_uc and ( use_version.split(".")[0] != get_uc_driver_version( local_uc_driver=local_uc_driver ) ) ) ): log_d("\nWarning: %s" % msg) sys_args = sys.argv # Save a copy of sys args try: sb_install.main( override="chromedriver %s" % use_version, intel_for_uc=intel_for_uc, force_uc=force_uc, ) except Exception: d_latest = get_latest_chromedriver_version() if ( d_latest and use_version != "latest" and ( int(use_version) > int(d_latest.split(".")[0]) ) ): disable_build_check = True d_latest_major = d_latest.split(".")[0] if ( not path_chromedriver or ( ch_driver_version and ( int(ch_driver_version) < int(d_latest_major) ) ) ): sb_install.main( override="chromedriver latest" ) finally: sys.argv = sys_args # Put back original args service_args = [] if disable_build_check: service_args = ["--disable-build-check"] if use_uc: uc_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with uc_lock: # Avoid multithreaded issues with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) if make_uc_driver_from_chromedriver: if os.path.exists(local_chromedriver): with suppress(Exception): make_driver_executable_if_not( local_chromedriver ) shutil.copy2(local_chromedriver, local_uc_driver) elif os.path.exists(path_chromedriver): with suppress(Exception): make_driver_executable_if_not( path_chromedriver ) shutil.copy2(path_chromedriver, local_uc_driver) try: make_driver_executable_if_not(local_uc_driver) except Exception as e: logging.debug( "\nWarning: Could not make uc_driver" " executable: %s" % e ) if not headless or not IS_LINUX or use_uc: uc_activated = False try: if os.path.exists(local_chromedriver) or use_uc: if headless and not IS_LINUX: undetectable = False # No support for headless use_uc = is_using_uc(undetectable, browser_name) if use_uc: from seleniumbase import undetected from urllib.error import URLError if IS_LINUX: if "--headless" in ( chrome_options.arguments ): chrome_options.arguments.remove( "--headless" ) if "--headless=old" in ( chrome_options.arguments ): chrome_options.arguments.remove( "--headless=old" ) uc_chrome_version = None if ( use_version.isnumeric() and int(use_version) >= 72 ): uc_chrome_version = int(use_version) elif ( str(use_version).split(".")[0].isnumeric() and int(str(use_version).split(".")[0]) >= 72 ): uc_chrome_version = ( int(str(use_version).split(".")[0]) ) cdp_events = uc_cdp_events cert = "unable to get local issuer certificate" mac_certificate_error = False if ( use_version.isnumeric() and int(use_version) <= 74 ): chrome_options.add_experimental_option( "w3c", True ) if ( (not user_agent or "Headless" in user_agent) and uc_chrome_version and uc_chrome_version >= 117 and (headless or headless2) and hasattr(sb_config, "uc_agent_cache") ): user_agent = sb_config.uc_agent_cache chrome_options.add_argument( "--user-agent=%s" % user_agent ) with suppress(Exception): if ( ( not user_agent or "Headless" in user_agent ) and uc_chrome_version and uc_chrome_version >= 117 and (headless or headless2) and chromium_arg != "decoy" ): from seleniumbase.console_scripts import ( sb_install ) sb_config.uc_user_agent_cache = True headless_options = _set_chrome_options( browser_name, downloads_path, True, # headless locale_code, None, # proxy_string None, # proxy_auth None, # proxy_user None, # proxy_pass None, # proxy_scheme None, # proxy_bypass_list None, # proxy_pac_url None, # multi_proxy None, # user_agent None, # recorder_ext disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, False, # undetectable False, # uc_cdp_events False, # uc_subprocess False, # log_cdp_events no_sandbox, disable_gpu, False, # headless1 False, # headless2 incognito, guest_mode, dark_mode, None, # devtools remote_debug, enable_3d_apis, swiftshader, None, # ad_block_on None, # host_resolver_rules block_images, do_not_track, None, # chromium_arg None, # user_data_dir None, # extension_zip None, # extension_dir None, # disable_features binary_location, driver_version, page_load_strategy, use_wire, external_pdf, servername, mobile_emulator, device_width, device_height, device_pixel_ratio, ) if ( not path_chromedriver or ( ch_driver_version and use_version and ( int(ch_driver_version) < int(str( use_version).split(".")[0] ) ) ) ): sb_install.main( override="chromedriver %s" % use_version, intel_for_uc=False, force_uc=False, ) d_b_c = "--disable-build-check" if os.path.exists(local_chromedriver): service = ChromeService( executable_path=local_chromedriver, log_output=os.devnull, service_args=[d_b_c], ) driver = webdriver.Chrome( service=service, options=headless_options, ) else: service = ChromeService( log_output=os.devnull, service_args=[d_b_c], ) driver = webdriver.Chrome( service=service, options=headless_options, ) with suppress(Exception): user_agent = driver.execute_script( "return navigator.userAgent;" ) if ( major_chrome_version and full_ch_version and full_ch_version.count(".") == 3 and full_ch_version in user_agent ): mcv = major_chrome_version user_agent = user_agent.replace( "Chrome/%s" % full_ch_version, "Chrome/%s.0.0.0" % mcv ) user_agent = user_agent.replace( "Headless", "" ) chrome_options.add_argument( "--user-agent=%s" % user_agent ) sb_config.uc_agent_cache = user_agent driver.quit() uc_path = None if os.path.exists(local_uc_driver): uc_path = local_uc_driver uc_path = os.path.realpath(uc_path) try: driver = undetected.Chrome( options=chrome_options, user_data_dir=user_data_dir, driver_executable_path=uc_path, browser_executable_path=b_path, enable_cdp_events=cdp_events, headless=False, # Xvfb needed! version_main=uc_chrome_version, use_subprocess=True, # Always! ) uc_activated = True except URLError as e: if ( IS_MAC and hasattr(e, "args") and isinstance(e.args, (list, tuple)) and len(e.args) > 0 and cert in e.args[0] ): mac_certificate_error = True else: raise except SessionNotCreatedException: time.sleep(0.2) driver = undetected.Chrome( options=chrome_options, user_data_dir=user_data_dir, driver_executable_path=uc_path, browser_executable_path=b_path, enable_cdp_events=cdp_events, headless=False, # Xvfb needed! version_main=uc_chrome_version, use_subprocess=True, # Always! ) uc_activated = True if mac_certificate_error: cf_lock_path = ( constants.MultiBrowser.CERT_FIXING_LOCK ) cf_lock = fasteners.InterProcessLock( constants.MultiBrowser.CERT_FIXING_LOCK ) if not os.path.exists(cf_lock_path): # Avoid multithreaded issues with cf_lock: with suppress(Exception): shared_utils.make_writable( cf_lock_path ) # Install Python Certificates (MAC) os.system( r"bash /Applications/Python*/" r"Install\ " r"Certificates.command" ) driver = undetected.Chrome( options=chrome_options, user_data_dir=user_data_dir, driver_executable_path=uc_path, browser_executable_path=b_path, enable_cdp_events=cdp_events, headless=False, # Xvfb needed! version_main=uc_chrome_version, use_subprocess=True, # Always! ) uc_activated = True else: if ( use_version.isnumeric() and int(use_version) <= 74 ): chrome_options.add_experimental_option( "w3c", True ) service = ChromeService( executable_path=local_chromedriver, log_output=os.devnull, service_args=service_args, ) driver = webdriver.Chrome( service=service, options=chrome_options, ) else: service = ChromeService( log_output=os.devnull, service_args=service_args, ) driver = webdriver.Chrome( service=service, options=chrome_options, ) except Exception as e: if not hasattr(e, "msg"): raise auto_upgrade_chromedriver = False if "This version of ChromeDriver only supports" in e.msg: auto_upgrade_chromedriver = True elif "Chrome version must be between" in e.msg: auto_upgrade_chromedriver = True elif "Missing or invalid capabilities" in e.msg: chrome_options.add_experimental_option("w3c", True) service = ChromeService( log_output=os.devnull, service_args=service_args, ) with warnings.catch_warnings(): warnings.simplefilter( "ignore", category=DeprecationWarning ) driver = webdriver.Chrome( service=service, options=chrome_options ) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) if not auto_upgrade_chromedriver: raise # Not an obvious fix. else: pass # Try upgrading ChromeDriver to match Chrome. mcv = None # Major Chrome Version if "Current browser version is " in e.msg: line = e.msg.split("Current browser version is ")[1] browser_version = line.split(" ")[0] major_chrome_version = browser_version.split(".")[0] if ( major_chrome_version.isnumeric() and int(major_chrome_version) >= 86 ): mcv = major_chrome_version mcv = find_chromedriver_version_to_use( mcv, driver_version ) headless_options = _set_chrome_options( browser_name, downloads_path, True, # headless locale_code, None, # proxy_string None, # proxy_auth None, # proxy_user None, # proxy_pass None, # proxy_scheme None, # proxy_bypass_list None, # proxy_pac_url None, # multi_proxy None, # user_agent None, # recorder_ext disable_cookies, disable_js, disable_csp, enable_ws, enable_sync, use_auto_ext, False, # undetectable False, # uc_cdp_events False, # uc_subprocess False, # log_cdp_events no_sandbox, disable_gpu, False, # headless1 False, # headless2 incognito, guest_mode, dark_mode, None, # devtools remote_debug, enable_3d_apis, swiftshader, None, # ad_block_on None, # host_resolver_rules block_images, do_not_track, None, # chromium_arg None, # user_data_dir None, # extension_zip None, # extension_dir None, # disable_features binary_location, driver_version, page_load_strategy, use_wire, external_pdf, servername, mobile_emulator, device_width, device_height, device_pixel_ratio, ) args = " ".join(sys.argv) if "-n" in sys.argv or " -n=" in args or args == "-c": chromedriver_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with chromedriver_fixing_lock: with suppress(Exception): shared_utils.make_writable( constants.MultiBrowser.DRIVER_FIXING_LOCK ) if not _was_driver_repaired(): _repair_chromedriver( chrome_options, headless_options, mcv ) _mark_driver_repaired() else: if not _was_driver_repaired(): _repair_chromedriver( chrome_options, headless_options, mcv ) _mark_driver_repaired() if os.path.exists(local_chromedriver): service = ChromeService( executable_path=local_chromedriver, log_output=os.devnull, service_args=["--disable-build-check"], ) driver = webdriver.Chrome( service=service, options=chrome_options, ) else: service = ChromeService( log_output=os.devnull, service_args=["--disable-build-check"], ) driver = webdriver.Chrome( service=service, options=chrome_options, ) driver.default_get = driver.get # Save copy of original driver.cdp = None # Set a placeholder driver._is_using_uc = False driver._is_using_cdp = False driver._is_connected = True if uc_activated: driver.get = lambda url: uc_special_open_if_cf( driver, url, proxy_string, mobile_emulator, device_width, device_height, device_pixel_ratio, ) driver.uc_open = lambda url: uc_open(driver, url) driver.uc_open_with_tab = ( lambda url: uc_open_with_tab(driver, url) ) driver.uc_open_with_reconnect = ( lambda *args, **kwargs: uc_open_with_reconnect( driver, *args, **kwargs ) ) driver.uc_open_with_disconnect = ( lambda *args, **kwargs: uc_open_with_disconnect( driver, *args, **kwargs ) ) driver.uc_click = lambda *args, **kwargs: uc_click( driver, *args, **kwargs ) driver.uc_activate_cdp_mode = ( lambda *args, **kwargs: uc_activate_cdp_mode( driver, *args, **kwargs ) ) driver.activate_cdp_mode = driver.uc_activate_cdp_mode driver.uc_open_with_cdp_mode = ( lambda *args, **kwargs: uc_open_with_cdp_mode( driver, *args, **kwargs ) ) driver.uc_gui_press_key = ( lambda *args, **kwargs: uc_gui_press_key( driver, *args, **kwargs ) ) driver.uc_gui_press_keys = ( lambda *args, **kwargs: uc_gui_press_keys( driver, *args, **kwargs ) ) driver.uc_gui_write = ( lambda *args, **kwargs: uc_gui_write( driver, *args, **kwargs ) ) driver.uc_gui_click_x_y = ( lambda *args, **kwargs: uc_gui_click_x_y( driver, *args, **kwargs ) ) driver.uc_gui_click_captcha = ( lambda *args, **kwargs: uc_gui_click_captcha( driver, *args, **kwargs ) ) driver.uc_gui_click_cf = ( lambda *args, **kwargs: uc_gui_click_cf( driver, *args, **kwargs ) ) driver.uc_gui_click_rc = ( lambda *args, **kwargs: uc_gui_click_rc( driver, *args, **kwargs ) ) driver.uc_gui_handle_captcha = ( lambda *args, **kwargs: uc_gui_handle_captcha( driver, *args, **kwargs ) ) driver.uc_gui_handle_cf = ( lambda *args, **kwargs: uc_gui_handle_cf( driver, *args, **kwargs ) ) driver.uc_gui_handle_rc = ( lambda *args, **kwargs: uc_gui_handle_rc( driver, *args, **kwargs ) ) driver.uc_switch_to_frame = ( lambda *args, **kwargs: uc_switch_to_frame( driver, *args, **kwargs ) ) driver.default_execute_cdp_cmd = driver.execute_cdp_cmd driver.execute_cdp_cmd = ( lambda *args, **kwargs: uc_execute_cdp_cmd( driver, *args, **kwargs ) ) driver._is_hidden = (headless or headless2) driver._is_using_uc = True with suppress(Exception): if int(uc_driver_version) >= 133: for window_handle in driver.window_handles: driver.switch_to.window(window_handle) if driver.current_url.startswith( "chrome-extension://" ): driver.close() time.sleep(0.003) driver.switch_to.window(driver.window_handles[0]) time.sleep(0.003) # seleniumbase/SeleniumBase/discussions/4190 if getattr(sb_config, "skip_133_patch", None): # To skip the connect() patch for Chrome 133+: # from seleniumbase import config as sb_config # sb_config.skip_133_patch = True # (Do the above before launching the browser.) pass else: # This fixes an issue on Chrome 133+ # (Some people might not need it though.) driver.connect() time.sleep(0.003) if mobile_emulator: uc_metrics = {} if ( isinstance(device_width, int) and isinstance(device_height, int) and isinstance(device_pixel_ratio, (int, float)) ): uc_metrics["width"] = device_width uc_metrics["height"] = device_height uc_metrics["pixelRatio"] = device_pixel_ratio else: uc_metrics["width"] = constants.Mobile.WIDTH uc_metrics["height"] = constants.Mobile.HEIGHT uc_metrics["pixelRatio"] = constants.Mobile.RATIO set_device_metrics_override = dict( { "width": uc_metrics["width"], "height": uc_metrics["height"], "deviceScaleFactor": uc_metrics["pixelRatio"], "mobile": True } ) with suppress(Exception): driver.execute_cdp_cmd( 'Emulation.setDeviceMetricsOverride', set_device_metrics_override ) else: driver.get = lambda url: updated_get(driver, url) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) else: # Running headless on Linux (and not using --uc) try: driver = webdriver.Chrome(options=chrome_options) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) except Exception as e: if not hasattr(e, "msg"): raise auto_upgrade_chromedriver = False if "This version of ChromeDriver only supports" in e.msg: auto_upgrade_chromedriver = True elif "Chrome version must be between" in e.msg: auto_upgrade_chromedriver = True elif "Missing or invalid capabilities" in e.msg: chrome_options.add_experimental_option("w3c", True) service = ChromeService( log_output=os.devnull, service_args=["--disable-build-check"], ) with warnings.catch_warnings(): warnings.simplefilter( "ignore", category=DeprecationWarning ) driver = webdriver.Chrome( service=service, options=chrome_options ) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) mcv = None # Major Chrome Version if "Current browser version is " in e.msg: line = e.msg.split("Current browser version is ")[1] browser_version = line.split(" ")[0] major_chrome_version = browser_version.split(".")[0] if ( major_chrome_version.isnumeric() and int(major_chrome_version) >= 86 ): mcv = major_chrome_version if auto_upgrade_chromedriver: args = " ".join(sys.argv) if "-n" in sys.argv or " -n=" in args or args == "-c": chromedr_fixing_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) D_F_L = constants.MultiBrowser.DRIVER_FIXING_LOCK with chromedr_fixing_lock: with suppress(Exception): shared_utils.make_writable(D_F_L) if not _was_driver_repaired(): with suppress(Exception): _repair_chromedriver( chrome_options, chrome_options, mcv ) _mark_driver_repaired() else: if not _was_driver_repaired(): with suppress(Exception): _repair_chromedriver( chrome_options, chrome_options, mcv ) _mark_driver_repaired() with suppress(Exception): service = ChromeService( log_output=os.devnull, service_args=["--disable-build-check"], ) driver = webdriver.Chrome( service=service, options=chrome_options, ) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) # Use the virtual display on Linux during headless errors logging.debug( "\nWarning: Chrome failed to launch in" " headless mode. Attempting to use the" " SeleniumBase virtual display on Linux..." ) if "--headless" in chrome_options.arguments: chrome_options.arguments.remove("--headless") if "--headless=old" in chrome_options.arguments: chrome_options.arguments.remove("--headless=old") service = ChromeService( log_output=os.devnull, service_args=["--disable-build-check"] ) driver = webdriver.Chrome( service=service, options=chrome_options ) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) except Exception as original_exception: if use_uc: raise # Try again if Chrome didn't launch with suppress(Exception): service = ChromeService(service_args=["--disable-build-check"]) driver = webdriver.Chrome( service=service, options=chrome_options ) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) if user_data_dir: print("\nUnable to set user_data_dir while starting Chrome!\n") raise elif mobile_emulator: print("\nFailed to start Chrome's mobile device emulator!\n") raise elif extension_zip or extension_dir: print("\nUnable to load extension while starting Chrome!\n") raise elif headless or headless2 or IS_LINUX or proxy_string or use_wire: raise # Try running without any options (bare bones Chrome launch) if local_chromedriver and os.path.exists(local_chromedriver): try: make_driver_executable_if_not(local_chromedriver) except Exception as e: logging.debug( "\nWarning: Could not make chromedriver" " executable: %s" % e ) service = ChromeService( log_output=os.devnull, service_args=["--disable-build-check"] ) try: driver = webdriver.Chrome(service=service) return extend_driver( driver, proxy_auth, use_uc, recorder_ext ) except Exception: raise original_exception else: raise Exception( "%s is not a valid browser option for this system!" % browser_name ) ================================================ FILE: seleniumbase/core/capabilities_parser.py ================================================ import re import ast import json import yaml # Requires pyyaml def _analyze_ast(contents): try: return ast.literal_eval(contents) except SyntaxError: pass try: # Remove all comments contents = re.sub(re.compile(r"/\*.*?\*/", re.DOTALL), "", contents) contents = re.sub(re.compile(r"#.*?\n"), "", contents) # Remove anything before dict declaration like: "caps = { ..." match = re.match(r"^([^{]+)", contents) if match: contents = contents.replace(match.group(1), "") return ast.literal_eval(contents) except SyntaxError: pass return False def _analyze_manual(contents): capabilities = {} code_lines = contents.split("\n") for line in code_lines: if "desired_cap = {" in line: line = line.split("desired_cap = {")[1] # 'KEY' : 'VALUE' data = re.match(r"^\s*'([\S\s]+)'\s*:\s*'([\S\s]+)'\s*[,}]?\s*$", line) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # "KEY" : "VALUE" data = re.match(r'^\s*"([\S\s]+)"\s*:\s*"([\S\s]+)"\s*[,}]?\s*$', line) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # 'KEY' : "VALUE" data = re.match( r"""^\s*'([\S\s]+)'\s*:\s*"([\S\s]+)"\s*[,}]?\s*$""", line ) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # "KEY" : 'VALUE' data = re.match( r"""^\s*"([\S\s]+)"\s*:\s*'([\S\s]+)'\s*[,}]?\s*$""", line ) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # "KEY" : True data = re.match(r"""^\s*"([\S\s]+)"\s*:\s*True\s*[,}]?\s*$""", line) if data: key = data.group(1) value = True capabilities[key] = value continue # 'KEY' : True data = re.match(r"""^\s*'([\S\s]+)'\s*:\s*True\s*[,}]?\s*$""", line) if data: key = data.group(1) value = True capabilities[key] = value continue # "KEY" : False data = re.match(r"""^\s*"([\S\s]+)"\s*:\s*False\s*[,}]?\s*$""", line) if data: key = data.group(1) value = False capabilities[key] = value continue # 'KEY' : False data = re.match(r"""^\s*'([\S\s]+)'\s*:\s*False\s*[,}]?\s*$""", line) if data: key = data.group(1) value = False capabilities[key] = value continue # caps['KEY'] = 'VALUE' data = re.match(r"^\s*caps\['([\S\s]+)'\]\s*=\s*'([\S\s]+)'\s*$", line) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # caps["KEY"] = "VALUE" data = re.match(r'^\s*caps\["([\S\s]+)"\]\s*=\s*"([\S\s]+)"\s*$', line) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # caps['KEY'] = "VALUE" data = re.match( r"""^\s*caps\['([\S\s]+)'\]\s*=\s*"([\S\s]+)"\s*$""", line ) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # caps["KEY"] = 'VALUE' data = re.match( r"""^\s*caps\["([\S\s]+)"\]\s*=\s*'([\S\s]+)'\s*$""", line ) if data: key = data.group(1) value = data.group(2) capabilities[key] = value continue # caps["KEY"] = True data = re.match(r"""^\s*caps\["([\S\s]+)"\]\s*=\s*True\s*$""", line) if data: key = data.group(1) value = True capabilities[key] = value continue # caps['KEY'] = True data = re.match(r"""^\s*caps\['([\S\s]+)'\]\s*=\s*True\s*$""", line) if data: key = data.group(1) value = True capabilities[key] = value continue # caps["KEY"] = False data = re.match(r"""^\s*caps\["([\S\s]+)"\]\s*=\s*False\s*$""", line) if data: key = data.group(1) value = False capabilities[key] = value continue # caps['KEY'] = False data = re.match(r"""^\s*caps\['([\S\s]+)'\]\s*=\s*False\s*$""", line) if data: key = data.group(1) value = False capabilities[key] = value continue return capabilities def _read_file(file): f = open(file, "r") data = f.read() f.close() return data def _parse_py_file(cap_file): all_code = _read_file(cap_file) capabilities = _analyze_ast(all_code) if not capabilities: capabilities = _analyze_manual(all_code) return capabilities def _parse_json_file(cap_file): all_code = _read_file(cap_file) return json.loads(all_code) def _parse_yaml_file(cap_file): all_code = _read_file(cap_file) return yaml.safe_load(all_code) def get_desired_capabilities(cap_file): if cap_file.endswith(".py"): capabilities = _parse_py_file(cap_file) elif cap_file.endswith(".json"): capabilities = _parse_json_file(cap_file) elif (cap_file.endswith(".yml") or cap_file.endswith(".yaml")): capabilities = _parse_yaml_file(cap_file) else: raise Exception( '\n\n`%s` must end in ".py", ".json", ".yml", or ".yaml"!\n' % cap_file ) if len(capabilities.keys()) == 0: raise Exception("Unable to parse desired capabilities file!") return capabilities ================================================ FILE: seleniumbase/core/colored_traceback.py ================================================ import sys def add_hook(always=False, style="default", debug=False): import os if os.environ.get("NO_COLOR", ""): return # https://no-color.org isatty = getattr(sys.stderr, "isatty", lambda: False) if always or isatty(): colorizer = Colorizer(style, debug) sys.excepthook = colorizer.colorize_traceback class Colorizer(object): def __init__(self, style, debug=False): self.style = style self.debug = debug def colorize_traceback(self, type, value, tb): import traceback import pygments.lexers tb_text = "".join(traceback.format_exception(type, value, tb)) lexer_name = "py3tb" lexer = pygments.lexers.get_lexer_by_name(lexer_name) tb_colored = pygments.highlight(tb_text, lexer, self.formatter) self.stream.write(tb_colored) @property def formatter(self): from pygments.formatters import get_formatter_by_name import pygments.util colors = _get_term_color_support() if self.debug: sys.stderr.write("Detected support for %s colors\n" % colors) if colors == 256: fmt_options = {"style": self.style} elif self.style in ("light", "dark"): fmt_options = {"bg": self.style} else: fmt_options = {"bg": "dark"} fmt_alias = "terminal256" if colors == 256 else "terminal" try: return get_formatter_by_name(fmt_alias, **fmt_options) except pygments.util.ClassNotFound as ex: if self.debug: sys.stderr.write(str(ex) + "\n") return get_formatter_by_name(fmt_alias) @property def stream(self): try: import colorama except ImportError: return sys.stderr return colorama.AnsiToWin32(sys.stderr) def _get_term_color_support(): try: import curses except ImportError: return 16 curses.setupterm() return curses.tigetnum("colors") ================================================ FILE: seleniumbase/core/create_db_tables.sql ================================================ # Creates test_db tables for using SeleniumBase with MySQL # test_run_data table # ----------------------------------- CREATE TABLE `test_run_data` ( `guid` varchar(64) NOT NULL DEFAULT '', `test_address` varchar(255) DEFAULT NULL, `env` varchar(64) DEFAULT NULL, `start_time` varchar(64) DEFAULT NULL, `execution_guid` varchar(64) DEFAULT NULL, `runtime` int(11), `state` varchar(64) DEFAULT NULL, `browser` varchar(64) DEFAULT NULL, `message` text, `stack_trace` text, `retry_count` int(11) DEFAULT '0', `exception_map_guid` varchar(64) DEFAULT NULL, `log_url` text, PRIMARY KEY (`guid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # test_execution table # ----------------------------------- CREATE TABLE `test_execution` ( `guid` varchar(64) NOT NULL DEFAULT '', `total_execution_time` int(11), `username` varchar(255) DEFAULT NULL, `execution_start` bigint(20) DEFAULT '0', PRIMARY KEY (`guid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ================================================ FILE: seleniumbase/core/detect_b_ver.py ================================================ """Detect the browser version before launching tests. Eg. detect_b_ver.get_browser_version_from_os("google-chrome")""" import datetime import os import platform import re import subprocess import sys class File(object): def __init__(self, stream): self.content = stream.content self.__stream = stream self.__temp_name = "driver" @property def filename(self): try: filename = re.findall( "filename=(.+)", self.__stream.headers["content-disposition"] )[0] except KeyError: filename = "%s.zip" % self.__temp_name except IndexError: filename = "%s.exe" % self.__temp_name if '"' in filename: filename = filename.replace('"', "") return filename class OSType(object): LINUX = "linux" MAC = "mac" WIN = "win" class ChromeType(object): GOOGLE = "google-chrome" MSEDGE = "edge" OPERA = "opera" BRAVE = "brave" COMET = "comet" ATLAS = "atlas" PATTERN = { ChromeType.GOOGLE: r"\d+\.\d+\.\d+", ChromeType.MSEDGE: r"\d+\.\d+\.\d+", } def os_name(): if "linux" in sys.platform: return OSType.LINUX elif "darwin" in sys.platform: return OSType.MAC elif "win32" in sys.platform: return OSType.WIN else: raise Exception("Could not determine the OS type!") def os_architecture(): if platform.machine().endswith("64"): return 64 else: return 32 def os_type(): return "%s%s" % (os_name(), os_architecture()) def is_arch(os_sys_type): if '_m1' in os_sys_type: return True return platform.processor() != 'i386' def is_mac_os(os_sys_type): return OSType.MAC in os_sys_type def get_date_diff(date1, date2, date_format): a = datetime.datetime.strptime(date1, date_format) b = datetime.datetime.strptime( str(date2.strftime(date_format)), date_format) return (b - a).days def linux_browser_apps_to_cmd(*apps): """Create 'browser --version' command from browser app names.""" ignore_errors_cmd_part = " 2>/dev/null" if os.getenv( "WDM_LOG_LEVEL") == "0" else "" return " || ".join( "%s --version%s" % (i, ignore_errors_cmd_part) for i in apps ) def chrome_on_linux_path(chromium_ok=False, browser_type=None): if browser_type and browser_type != ChromeType.GOOGLE: return "" if os_name() != OSType.LINUX: return "" paths = [ "/bin/google-chrome", "/bin/google-chrome-stable", "/usr/bin/google-chrome", "/usr/bin/google-chrome-stable" ] for path in paths: try: if ( os.path.exists(path) and os.access(path, os.R_OK) and os.access(path, os.X_OK) ): return path except Exception: pass paths = os.environ["PATH"].split(os.pathsep) binaries = [] binaries.append("google-chrome") binaries.append("google-chrome-stable") binaries.append("google-chrome-beta") binaries.append("google-chrome-dev") binaries.append("google-chrome-unstable") binaries.append("chrome") binaries.append("chromium") binaries.append("chromium-browser") for binary in binaries: for path in paths: full_path = os.path.join(path, binary) try: if ( os.path.exists(full_path) and os.access(full_path, os.R_OK) and os.access(full_path, os.X_OK) ): return full_path except Exception: pass if chromium_ok: paths = [ "/bin/chromium", "/bin/chromium-browser", "/usr/bin/chromium", "/usr/bin/chromium-browser" ] for path in paths: try: if ( os.path.exists(path) and os.access(path, os.R_OK) and os.access(path, os.X_OK) ): return path except Exception: pass return "/usr/bin/google-chrome" def edge_on_linux_path(browser_type=None): if browser_type and browser_type != ChromeType.MSEDGE: return "" if os_name() != OSType.LINUX: return "" paths = os.environ["PATH"].split(os.pathsep) binaries = [] binaries.append("microsoft-edge") binaries.append("microsoft-edge-stable") binaries.append("microsoft-edge-beta") binaries.append("microsoft-edge-dev") for binary in binaries: for path in paths: full_path = os.path.join(path, binary) if os.path.exists(full_path) and os.access(full_path, os.X_OK): return full_path return "/usr/bin/microsoft-edge" def opera_on_linux_path(browser_type=None): if browser_type and browser_type != ChromeType.OPERA: return "" if os_name() != OSType.LINUX: return "" paths = os.environ["PATH"].split(os.pathsep) binaries = [] binaries.append("opera") binaries.append("opera-stable") for binary in binaries: for path in paths: full_path = os.path.join(path, binary) if os.path.exists(full_path) and os.access(full_path, os.X_OK): return full_path return "/usr/bin/opera-stable" def brave_on_linux_path(browser_type=None): if browser_type and browser_type != ChromeType.BRAVE: return "" if os_name() != OSType.LINUX: return "" paths = os.environ["PATH"].split(os.pathsep) binaries = [] binaries.append("brave-browser") binaries.append("brave") binaries.append("brave-browser-stable") for binary in binaries: for path in paths: full_path = os.path.join(path, binary) if os.path.exists(full_path) and os.access(full_path, os.X_OK): return full_path return "/usr/bin/brave-browser" def comet_on_linux_path(browser_type=None): if browser_type and browser_type != ChromeType.COMET: return "" if os_name() != OSType.LINUX: return "" return "" # Comet Browser isn't supported on Linux yet def atlas_on_linux_path(browser_type=None): if browser_type and browser_type != ChromeType.ATLAS: return "" if os_name() != OSType.LINUX: return "" return "" # Atlas Browser isn't supported on Linux yet def chrome_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.GOOGLE: return "" if os_name() != OSType.WIN: return "" candidates = [] for item in map( os.environ.get, ( "PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA", "PROGRAMW6432", ), ): for subitem in ( "Google/Chrome/Application", "Google/Chrome Beta/Application", "Google/Chrome Canary/Application", ): try: candidates.append(os.sep.join((item, subitem, "chrome.exe"))) except TypeError: pass for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return "" def edge_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.MSEDGE: return "" if os_name() != OSType.WIN: return "" candidates = [] for item in map( os.environ.get, ( "PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA", "PROGRAMW6432", ), ): for subitem in ( "Microsoft/Edge/Application", "Microsoft/Edge Beta/Application", "Microsoft/Edge Canary/Application", ): try: candidates.append(os.sep.join((item, subitem, "msedge.exe"))) except TypeError: pass for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return "" def opera_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.OPERA: return "" if os_name() != OSType.WIN: return "" candidates = [] for item in map( os.environ.get, ( "PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA", "PROGRAMW6432", ), ): for subitem in ( "Programs/Opera", "Opera", "Opera/Application", ): try: candidates.append(os.sep.join((item, subitem, "opera.exe"))) except TypeError: pass for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return "" def brave_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.BRAVE: return "" if os_name() != OSType.WIN: return "" candidates = [] for item in map( os.environ.get, ( "PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA", "PROGRAMW6432", ), ): for subitem in ( "BraveSoftware/Brave-Browser/Application", ): try: candidates.append(os.sep.join((item, subitem, "brave.exe"))) except TypeError: pass for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return "" def comet_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.COMET: return "" if os_name() != OSType.WIN: return "" candidates = [] for item in map( os.environ.get, ( "LOCALAPPDATA", "PROGRAMFILES", "PROGRAMFILES(X86)", "PROGRAMW6432", ), ): for subitem in ( "Perplexity/Comet/Application", "Comet/Application", "Programs/Comet", ): try: candidates.append(os.sep.join((item, subitem, "comet.exe"))) except TypeError: pass for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return "" def atlas_on_windows_path(browser_type=None): if browser_type and browser_type != ChromeType.ATLAS: return "" if os_name() != OSType.WIN: return "" candidates = [] for item in map( os.environ.get, ( "LOCALAPPDATA", "PROGRAMFILES", "PROGRAMFILES(X86)", "PROGRAMW6432", ), ): for subitem in ( "OpenAI/Atlas/Application", "Atlas/Application", "Programs/Atlas", ): try: candidates.append(os.sep.join((item, subitem, "atlas.exe"))) except TypeError: pass for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return "" def windows_browser_apps_to_cmd(*apps): """Create analogue of browser --version command for windows.""" powershell = determine_powershell() first_hit_template = "$tmp = {expression}; if ($tmp) {{echo $tmp; Exit;}};" script = "$ErrorActionPreference='silentlycontinue'; " + " ".join( first_hit_template.format(expression=e) for e in apps ) return '%s -NoProfile "%s"' % (powershell, script) def get_binary_location(browser_type, chromium_ok=False): """Return the full path of the browser binary.""" if browser_type.lower() == "chrome": browser_type = "google-chrome" elif browser_type.lower() == "msedge": browser_type = "edge" else: browser_type = browser_type.lower() cmd_mapping = { ChromeType.GOOGLE: { OSType.LINUX: chrome_on_linux_path(chromium_ok, browser_type), OSType.MAC: r"/Applications/Google Chrome.app" r"/Contents/MacOS/Google Chrome", OSType.WIN: chrome_on_windows_path(browser_type), }, ChromeType.MSEDGE: { OSType.LINUX: edge_on_linux_path(browser_type), OSType.MAC: r"/Applications/Microsoft Edge.app" r"/Contents/MacOS/Microsoft Edge", OSType.WIN: edge_on_windows_path(browser_type), }, ChromeType.OPERA: { OSType.LINUX: opera_on_linux_path(browser_type), OSType.MAC: r"/Applications/Opera.app" r"/Contents/MacOS/Opera", OSType.WIN: opera_on_windows_path(browser_type), }, ChromeType.BRAVE: { OSType.LINUX: brave_on_linux_path(browser_type), OSType.MAC: r"/Applications/Brave Browser.app" r"/Contents/MacOS/Brave Browser", OSType.WIN: brave_on_windows_path(browser_type), }, ChromeType.COMET: { OSType.LINUX: comet_on_linux_path(browser_type), OSType.MAC: r"/Applications/Comet.app" r"/Contents/MacOS/Comet", OSType.WIN: comet_on_windows_path(browser_type), }, ChromeType.ATLAS: { OSType.LINUX: atlas_on_linux_path(browser_type), OSType.MAC: r"/Applications/ChatGPT Atlas.app" r"/Contents/MacOS/ChatGPT Atlas", OSType.WIN: atlas_on_windows_path(browser_type), }, } return cmd_mapping[browser_type][os_name()] def get_browser_version_from_binary(binary_location): try: if not os.path.exists(binary_location): return None path = binary_location pattern = r"\d+\.\d+\.\d+" quad_pattern = r"\d+\.\d+\.\d+\.\d+" if os_name() == OSType.WIN: path = path.replace(r"\ ", r" ").replace("\\", "\\\\") cmd_mapping = ( '''powershell -command "&{(Get-Item -Path '%s')''' '''.VersionInfo.FileVersion}"''' % path ) quad_version = read_version_from_cmd(cmd_mapping, quad_pattern) if quad_version and len(str(quad_version)) >= 9: # Eg. 122.0.0.0 return quad_version return read_version_from_cmd(cmd_mapping, pattern) if binary_location.count(r"\ ") != binary_location.count(" "): binary_location = binary_location.replace(" ", r"\ ") cmd_mapping = binary_location + " --version" quad_version = read_version_from_cmd(cmd_mapping, quad_pattern) if quad_version and len(str(quad_version)) >= 9: return quad_version return read_version_from_cmd(cmd_mapping, pattern) except Exception: return None def get_browser_version_from_os(browser_type): """Return installed browser version.""" cmd_mapping = { ChromeType.GOOGLE: { OSType.LINUX: linux_browser_apps_to_cmd( "google-chrome", "google-chrome-stable", "chrome", "chromium", "chromium-browser", "google-chrome-beta", "google-chrome-dev", "google-chrome-unstable", ), OSType.MAC: r"/Applications/Google\ Chrome.app" r"/Contents/MacOS/Google\ Chrome --version", OSType.WIN: windows_browser_apps_to_cmd( r'(Get-Item -Path "$env:PROGRAMFILES\Google\Chrome' r'\Application\chrome.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Google\Chrome' r'\Application\chrome.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:LOCALAPPDATA\Google\Chrome' r'\Application\chrome.exe").VersionInfo.FileVersion', r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE' r'\Google\Chrome\BLBeacon").version', r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE' r'\Wow6432Node\Microsoft\Windows' r'\CurrentVersion\Uninstall\Google Chrome").version', ), }, ChromeType.MSEDGE: { OSType.LINUX: linux_browser_apps_to_cmd( "microsoft-edge", "microsoft-edge-stable", "microsoft-edge-beta", "microsoft-edge-dev", ), OSType.MAC: r"/Applications/Microsoft\ Edge.app" r"/Contents/MacOS/Microsoft\ Edge --version", OSType.WIN: windows_browser_apps_to_cmd( # stable edge r'(Get-Item -Path "$env:PROGRAMFILES\Microsoft\Edge' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Microsoft' r'\Edge\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE' r'\Microsoft\Edge\BLBeacon").version', r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE' r'\Microsoft\EdgeUpdate\Clients' r'\{56EB18F8-8008-4CBD-B6D2-8C97FE7E9062}").pv', # beta edge r'(Get-Item -Path "$env:LOCALAPPDATA\Microsoft\Edge Beta' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:PROGRAMFILES\Microsoft\Edge Beta' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Microsoft\Edge Beta' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Microsoft' r'\Edge Beta\BLBeacon").version', # dev edge r'(Get-Item -Path "$env:LOCALAPPDATA\Microsoft\Edge Dev' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:PROGRAMFILES\Microsoft\Edge Dev' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-Item -Path "$env:PROGRAMFILES (x86)\Microsoft\Edge Dev' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE\Microsoft' r'\Edge Dev\BLBeacon").version', # canary edge r'(Get-Item -Path "$env:LOCALAPPDATA\Microsoft\Edge SxS' r'\Application\msedge.exe").VersionInfo.FileVersion', r'(Get-ItemProperty -Path Registry::"HKCU\SOFTWARE' r'\Microsoft\Edge SxS\BLBeacon").version', # highest edge r"(Get-Item (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft" r"\Windows\CurrentVersion\App Paths\msedge.exe')." r"'(Default)').VersionInfo.ProductVersion", r"[System.Diagnostics.FileVersionInfo]::GetVersionInfo((" r"Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows" r"\CurrentVersion\App Paths\msedge.exe')." r"'(Default)').ProductVersion", r"Get-AppxPackage -Name *MicrosoftEdge.* | Foreach Version", r'(Get-ItemProperty -Path Registry::"HKLM\SOFTWARE\Wow6432Node' r'\Microsoft\Windows\CurrentVersion\Uninstall' r'\Microsoft Edge").version', ), }, } try: cmd_mapping = cmd_mapping[browser_type][os_name()] pattern = PATTERN[browser_type] quad_pattern = r"\d+\.\d+\.\d+\.\d+" quad_version = read_version_from_cmd(cmd_mapping, quad_pattern) if quad_version and len(str(quad_version)) >= 9: # Eg. 115.0.0.0 return quad_version version = read_version_from_cmd(cmd_mapping, pattern) return version except Exception: raise Exception( "Can not find browser %s installed in your system!" % browser_type ) def format_version(browser_type, version): if not version or version == 'latest': return 'latest' try: pattern = PATTERN[browser_type] result = re.search(pattern, version) return result.group(0) if result else version except Exception: return "latest" def get_browser_version(browser_type, metadata): pattern = PATTERN[browser_type] version_from_os = metadata['version'] result = re.search(pattern, version_from_os) version = result.group(0) if version_from_os else None return version def read_version_from_cmd(cmd, pattern): with subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, shell=True, ) as stream: stdout = stream.communicate()[0].decode() version = re.search(pattern, stdout) version = version.group(0) if version else None return version def determine_powershell(): """Returns "True" if runs in Powershell and "False" if another console.""" cmd = "(dir 2>&1 *`|echo CMD);&<# rem #>echo powershell" with subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL, shell=True, ) as stream: stdout = stream.communicate()[0].decode() return "" if stdout == "powershell" else "powershell" ================================================ FILE: seleniumbase/core/download_helper.py ================================================ import os import shutil import time from seleniumbase.config import settings from seleniumbase.fixtures import constants # The "downloads_folder" is a folder for saving downloaded files. # Works for downloads initiated by Chromium and Firefox WebDriver clicks. # Browser type doesn't matter if using self.download_file(file_url) # or self.save_file_as(file_url, new_file_name) # The "downloads_folder" is cleaned out at the start of each pytest run, # but there is an option to save existing files in "archived_files". DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER abs_path = os.path.abspath(".") downloads_path = os.path.join(abs_path, DOWNLOADS_DIR) def get_downloads_folder(): return downloads_path def reset_downloads_folder(): """Clears the downloads folder. If settings.ARCHIVE_EXISTING_DOWNLOADS is set to True, archives it.""" downloads_dir = constants.Files.DOWNLOADS_FOLDER archive_dir = constants.Files.ARCHIVED_DOWNLOADS_FOLDER if downloads_dir.endswith("/"): downloads_dir = downloads_dir[:-1] if downloads_dir.startswith("/"): downloads_dir = downloads_dir[1:] if archive_dir.endswith("/"): archive_dir = archive_dir[:-1] if archive_dir.startswith("/"): archive_dir = archive_dir[1:] if len(downloads_dir) < 10 or len(archive_dir) < 10: return # Prevent accidental deletions if constants are renamed archived_downloads_folder = os.path.join(os.getcwd(), archive_dir) + os.sep if os.path.exists(downloads_path) and not os.listdir(downloads_path) == []: reset_downloads_folder_assistant(archived_downloads_folder) if os.path.exists(downloads_path) and os.listdir(downloads_path) == []: try: os.rmdir(downloads_path) except OSError: pass if ( os.path.exists(archived_downloads_folder) and os.listdir(archived_downloads_folder) == [] ): try: os.rmdir(archived_downloads_folder) except OSError: pass def reset_downloads_folder_assistant(archived_downloads_folder): if not os.path.exists(archived_downloads_folder): try: os.makedirs(archived_downloads_folder, exist_ok=True) except Exception: pass # Should only be reachable during multi-threaded test runs new_archived_downloads_sub_folder = "%s/downloads_%s" % ( archived_downloads_folder, int(time.time()), ) if os.path.exists(downloads_path): if not os.listdir(downloads_path) == []: try: shutil.move(downloads_path, new_archived_downloads_sub_folder) os.makedirs(downloads_path, exist_ok=True) except Exception: pass if not settings.ARCHIVE_EXISTING_DOWNLOADS: try: shutil.rmtree(new_archived_downloads_sub_folder) except OSError: pass ================================================ FILE: seleniumbase/core/encoded_images.py ================================================ """ Instructions for generating encoded images: > import base64 > with open("YOUR_FILE.png", "rb") as image_file: > encoded_string = base64.b64encode(image_file.read()) """ def get_dash_pie_png1(): DASH_PIE_PNG_1 = ( "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAA" "AAXNSR0IArs4c6QAABqxJREFUeAHtnOtPFFcUwO/sLo9GUKIkUtNWDK+a2qZGm2LShDFS" "oeWhqUatxrSoMSS06R/QD0DSP0MTP1Cb9pMCpUCx0EIaWiBChBYLUWwMEndBEBamuztze" "8+yd5jdndl57Mzu7IZN8N69j3PP+c05587s3hWh7dc2ASMEGCOTjM7xN37RAnMZhJvlZL" "TVBeYGi58VRvUxeIDBzK/QfqOsvSWq36IGy+EAEAKjgujPqtmgCCdyYgiW1aAsgUOAsCH" "vUAUitVszHOkkAMU7W28cvDMgbTaj7jJDCJWxBQWztE1fifUNh9GYYbFDYK/+UxcMPTO9" "yaFfG/kZgcamfuIt/aSXlR9hcStAQqiZQOq/9vdpU3SIGw7kFAIGLnn8ChlwnCjkm57UD" "5Ci+nQ2xAUn5C3NOtdMzHACKV5AhnJOKLfEfWUspwSAHtZjRnAcN5KwdXuOlWDMiCo54C" "RhG8pDuuBYCWbTKKvwkE3NACDNcKwHI3fNzW3TC0gTnHQAQzHrAaQJTuj+hcq3sBQslL0" "lGgBtvVOuqcKB7Vp5usk9iWETVFrLNh8TDgmnFiKJDUpLt3/INq92Jx0TTujhMd2wiPao" "5R9FOM+72r/hXS5OlJSmFezkm5VMk4XzeOYBu15a9PXLhrqhQH7ekNLktGiPEV6ycBjGG" "aTp3fVqpfvzs4denKvqRlkZs5bDwJavILuAkvdEwQGvIfeT5G/zxaPMPO8bRdXzXza4No" "6W3hUyMjy0L21KBe+JgoOQg5UzWkCuwkW28pS78cIsV7a/T25MKrfJeU8UHIZBigkKjPd" "n5ZZ76moqlxrqu/ndu4bNBZKkuAIjiPdE2hIG5/HMVEvkAKX363teq35+5Vzx0qkPf8TZ" "mU+VxulqTyIb0PPaw/oWqb5hcBiGqZB2qtV5lJG/XlJS86zpM447UtpJ8tGy2hw792Mm+" "C2JqGIYHGkiFkdoqAhMRrHneGWt+/qFaV/R6wMapthzSERoiXA2d6n4dPa/klu+ePpk+d" "Llms7A7l3j8UlLzmzpI4UIR2mX0qsiz2Rlr+/dX+u+cr5g9cSxAcHpnNMuI8lJBxQlX/N" "QfSVwaJM5JY9cBSuHD7Oery6tce8UdQqZmeqPIjZgI807Ihy1LdwoMp8j55DnZFWt5+rZ" "0cDePSqPIragw1JbRTi0warStyPvA/flT46+uPhxt39n7qRV65gpN2FwQGmy9Wd79xVWu" "69/mrNa8f4An+FaMNMYs2UlFA5VHh5FVt47wi42XVzg3j7QJ340YoOoojpC6ZK+SXTd58" "p511P1EdpxbH4g5+5gDmK8+YnWIdZ6SfGcSIW8O/exy5cq16YP8rbQh+qXVM8BJZzIvzA" "mDE904K4qcuTLVq+kwpnFI6MdQk/OGvJW2YpKSJmkwBHQ6tB3gTb+EfNvhR2hUJ0SCodB" "/NxQoH3sF+bPM3YLIQpEWiYEDskryxNoZPQnvq+QYzbOSBWwc12EgzFqteIRYkGYHP8e3" "8l+ibyVdgZBdSN7Qiuti3Bog1mlA60N3xbauBn8hDVLZqLliHAOlLzVMjc71RyvAg4UmB" "sReqe68GBNvLKSPT/szoLAMXwD70S+tSl8H7bmAg5tvJlsw4yuf7OsXWQieg4IM5p3/hN" "W7rXhW2geLZwwqpQt5pED31I9wuAgJJBOh+bQcuL18R/wt2t/4UepDSVEBE7CS+GILkQb" "tYQWyStPx/AfU718X5mP4Qrp3FQvpSEFtkR4TuzQcmI/N43uj3fg3jwvJrf8UWhTFw8xJ" "cxrwBJZ8+S8J4BW+27zt5gnaD4tQijyMsqdVY7yHJgkTcwu5J28x/dM/YZGzkcKTJv3JB" "HLHeKWhUMT84Twe08v/rmIQ1z6giFXODIR04suG1bQCd8bk5sezTsXFZhqJeQapZ8hKcI" "BI+F3A6lmrF59I3co6fyYH0tCkpIOTre6mn0x4UCSArdLNyhBexSSsNTWmGFFBwYPNEec" "QKB9KVkSMDdLO1SjQhMcAJA2gDSCAZtjhhUMoC8tpOlYO5dK27aczprhwGS1BCa3gJ3a5" "O6CY+mnOayoEDjcEzx5mUo5SEcoUTuh1A2HTk6ZHGQQDNipK6woGCghBxGyrdI2u9VBv3" "hypWHPoSCCYabxx110TiJKvflFTqe44YDQ4CFDcpbODs9i4C1Kz0pyAGK1mQJHukCychF" "AQYJD9qMHqX566qbDgcUT6UlmekokOEvgSBexApQVXiLVmdYth0MXoiX9fUHwSKuWeyWy" "FSfjf12i+m6X2wT0E/gfn/18pZirCc8AAAAASUVORK5CYII=" ) return DASH_PIE_PNG_1 def get_dash_pie_png2(): DASH_PIE_PNG_2 = ( "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAA" "AAXNSR0IArs4c6QAACQ9JREFUeAHlnH9sFEUUx9/uXXtXKHCWlhatWEoFKddGkWDVCoeQ" "AELxD0QgoIjxZ4yJJmIIicFGJcZo4l8kBhJjAlhA/hABIeHHIS3yo0SsRyUCpWApx6/Yw" "PW6pXe37ndxjuuy1929vd9O0s7d3Mzse5+8ebMzO285SmJaufnUx7hcx1VabeSyeXbOPX" "yoeBBtPl844WMjbc3U5cw01tMWQG7c5Kb2CKKr01MkN2lv1dPybh2H1Ax/SPc7rxGDlWh" "QCYGzcusfrhtdltUMSNc1Ivy9NHiXrOBHZcflXPlvQ12g/VDF5TJluddTTpf/KJeLu/Yt" "kkEBVhiUI1j/+YIqt7Kd2e9xhcOgXLk4yNVxalAYyFRHJ01xXNKUNRocZUMGi4EqneCn4" "lF+eejF05riBuf1da0HGJSHus7QuyMO6gISqbheOJFtAOr89nlkv+WULWrMpOvu4XGyJN" "Nw4FPgYFsPFFGsUJiyG+r6pGHlLWPfjeTMmqhlETFLWvd65TQjfSjrmoIDaznXXOiCg13" "/wHrDlqIUxgwc1lekJT0++7rbDKCY4MC3dHj5A7CWOv8uiuZgmcB68w1zJct5ODbLUV5D" "tqSNq6hy2jUqLQlNi8Vh88pOtb4DTGvLkAOHtxTRl7nr4wZG67pGfy9xttHIJWsIckJey" "G20D0NwGBhL0yX6a0K96WFkVFij9QFo2vrX6OzFtpgA6YYTCWbr2E1G5dRVX9RVy3ilJ1" "d9HRMgXXCSAeaOyonCQxQLIE04yQNj3CKMtjAKaEA42QSGgTQCaEA4mK497kGUKB/DBL6" "bh+5+TOAnAIJe0G+gy0SFgxs83Mfg5i5pKTlsZHXGv7eGoB/0jKafKhwsCXDnixs8PQvG" "aJ2nczmmeWGIh7AehPtQk1UVDtZKWBLE685X7cLpUKblf+6Bw4bTp7MONQatViEdlEikD" "KPnbSdsr2D/SXmdfnBgXjCzUflXaMF8f23fsqcbA4WORmWjbPo+0PDqBwf0QPHdeedk/W" "1DemdwS6ucvS88sZtsOWcTDiVx94ADih7NesJwIq1mivNWuDOe+hy5pdys4NuTrT2Txv4" "Yysm5Hv4xSz5Es54wHArxLt/VQVRdfkVVZZ6CZfbagueDb9ScFcY9tFe1UgYXqllPGE54" "hlp0eUAVrTl9NbbZI2f4l0/ZHSwYdmTAyoZ/TNG4kuSE9eAhAB4KMLFlOLivwWOTpdNbW" "Llmnjfs9ixxWVWFUDdxp2jP7dBsoKdC6tjI0jmmNxA4gAcKZDh4rgRqU50+uZLefxYxUG" "gbkzsn9OYkQXh87A7JH3XpbZuO9UZW3bEe8IB8MhyYEuBEOmIjwvNcqML2TMHc4Gs1p2+" "PedBtpG061VUOLR6z1M2rg6l6tLojNiK81dZXw9eV1QhLntoRKBh20kjbdKlrL/cQeIAL" "rzVLGRXaQn12W1FoLvdyVUng2UfcIYulXX8fKXY6kqD20R7CrA0u4dlKvwL6avIUKLFUO" "1zBd2p9QvWYHaHcXO2lSOrZUKTf4fVO4fqQ3FvLyvc6bc8WzQ0un9QcKB6usRRJPZ1Iv2" "O9V53ElFjzQrXBxZWC4A3stuzylObclJ7fpnlK2LBS01v2RyXiLP7V6vzAlHHuYI7Vq1Y" "vXcqSCocpjaWIZeJ9LvGtp71C1ei94a2R1I8qJqKcJ21Y9bvqf18slt5HLdOLqXfyKHdo" "hyefuO5CtXqpKkuJ5SiVlbZGXL1LJvpOjw+mhTxMvpRaDoQIkdV7kL/w+2bx8EyK6VgDU" "yX+eUrhHONuNG+ho/k+sXtm/FUz32NK4Pg4a+NasSnYRhenmlchcT0kFU6Qs7ZvE8+d2E" "/H5qfbEFJDnBQ4IcrpauL+bt5GzWUC1zNfTZB0LLM898o7XLdvsKv9gt/wfo4ehU5xPSe" "/4nYLzXR2coACBXrapLLOb9/PoJI8J413+usTZjk9XO6RtVyjcEa84EqlsmauzePcLg47" "b9hXbaafcNsQWdq3896d79PGmkwGA4XiZjlByvEd4zubN4tHSwSxZ06YVoZ9wMHvyhevy" "TEW8h1p6QiqL6sk+qRhZEyqdHHcvs+4/Ue/E90ugXoeiamTNGiEE6gIG0BsBcS5Yzl8yJ" "0/wr+6pa1YKhr40QwasdTL5Z38hmv0tYpt01lZJuc4CV8hhQrgBDz0kC0HZ3SHjuimlvO" "Ao50kv9Kxj7+0Z4W4xSGBqdVukRk1hDYngQM7sxxe6OkZWpJfEY5yN46s4H/ybZXWQrc5" "oSwz1NaWElM4XAs4sNphOFqzlk+07P2Cczd9S/trusXujPUrTHFlLpx3kuRapLEUcrPfw" "nBQoGY9t3mbZxPXvvkDvmHGBerMCt/ClGc5HDGibhCWxIYUfusHB9RA745jJnLznXs+FH" "+w/0LHF7KOsjGHI0akDXPETMd+cECt0tldfzOvmJ5o+IcaxKaZAgkVrHI25vJywe4k6B1" "pNdC1HxwUMN+Dm6H/Q4KeWCGoRfjdAwdAEILjdPnp1zXvZTUf6Ac9oa+aoqpw2PAqkcwN" "ZpeNKZoTjtRVFQ4qwMwQL4mwQHSUTQn6XJYC1bQi+TS3tHH09sTPhS4EduFRaaYnvWCgZ" "1TLYRAQI4lQwD+/XsWKMjqPNm2rKaUJB42yxUHDAVeMKqfK6lu6Yj41hxUjisM8iJVESC" "BOXmbSEMNQguVjZtILBnrrhoPKDBAOcmeKD2I+JpYoYUNwGCCcdIeTpuoGemxx+h5Jlm9" "DpNlWa1aCXmrJMBzWCWYxhB15BY8cP8nK0yWHf8F9Gm5HYg28jxkOIOC8bqtn8Gr4IZyl" "SwcrgrVgSSD7F6yXTLxvxxQcGdB/jhqHDBGjhQi4VDhrQMGeDKwFa6VY30IQafmm4TBAO" "H3JXviBCLhkWRKzFOziYbtFXl2bsJa4w4nsEL4IMVvMknCuN96gmJVgzxdQZEvB9qa0H6" "XcdoiUzejnuFiO8qKY8pkl4cAzA4V6iC9AwpFWPcNPnooj3rqEtsxKsBmO3UszfgX9RUs" "JgRN5sUhQKGfv7UI4Af60kvJ9Xagvb4LH2UrU5Eg4HOVFWUQKe8mZ8nfld/byMpQnykKU" "12Tf/wU491oPtPXjKQAAAABJRU5ErkJggg==" ) return DASH_PIE_PNG_2 def get_dash_pie_png3(): DASH_PIE_PNG_3 = ( "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAEcAAABHCAYAAABVsFofAAA" "AAXNSR0IArs4c6QAACThJREFUeAHlnH9sE+cZx587O3FIUuIxnARKISQRtImTdgy1gbIQ" "IAKmAv2DsaWiW9UJbaumSZ20SRPSpk3b+GPqH/zVaVKkaVLpAqhSoRSxKgRXJClh6caPJ" "KvWNE1oSAxetSS1k3Ni+3bf13nds3P2nX//2CvZ53vv/fE8Hz/v8/64e0+w/OqPMmUotF" "RuYjU9JgePRqudFWZpjmZY8hsPJ4xmSzqdOekSdAoAkNVkpQq5gqbu2ljq/hGdTBGXrTY" "bWYNZ6Zh9E3FY6QaVFjgbyiqooawmBOS+i2hY+Xy37DJT+73Gv0eoHzx947Bv/Hr9dE3k" "RedQLU3frWXR4+c6FFBBWBzUiGecJj2zkdmSPk8pHA5FeLCeJgdKQ0B2W6eotfF+wsJW2" "8cIHxZe6CYOi4NqbFxPDVVTrOml0ppSBmd/5ZPEoWya+Zheq3w/KSCxSIZgLYMavXiESo" "btrOntb7JSqiwpaTjwKXCwIz02CkHZkqiVxN83BEGdDlnTTE8HcUt67+HtWIx1ryUFB9b" "iuVtPcLCdj3ZSa8JQdOXUTcCtydk0RtyS9u8lSgZQQnDgW3aUPsms5fD8ZYrmYHU1ikwQ" "v+FElsB8U7U9aEn3z5ykY3sq6IP52wk5bHFF6ToRANPo3kn952z0WnEn/bJGu+fRKSbtl" "2FJ646fYnJCXsgdb4gLDgdj6rtP/278DbVaE/Ut8YqZWHoA2tN5gkbvjbE/NF5AhuGowZ" "zf8mZi0urkSkGr0qxhx8nTCQEyBCcTYIJapQsPUSKAdOFkDozmn57SyHgBxYRTSGA45Xg" "AxYSD7nrIUUrp8jFc4C+PgS9/pvEXAEEv6BcrRIWDAd7INRsb3MUqIKXXMsOGifzEq6eY" "ftAzWtCEgykBRr4Y4OV6dx1NMb14dPPSI0NsPgj3oRU04WCuNK5MCXJ1gKelSCJxev5nB" "RzenH538Hqv32yWEqk0n/JsPnKRJodL2fpTpNxhcGBeWHbYWP6Ajh2d37X00rO9vrXW3s" "hMhXQeq3mFwcHqHSj+5MgnTH/LI9524cUmu/dbz1whS9Fo2qGkbwwYU/Ro1hOCo7aaVvs" "XocJEWrIWbxAO+l952rywfcuFQFHRf0IXC+RHNOtRwbGS+2EpNdc+0FRZJH9Nya41z/t/" "0DIqbd3UrZkojyO1rCcEJ9RDdUzHVNFctNRi+ea69vmXW6/411TciJk47otZaleKnLCeG" "eUmAO6S8MDgYFwzNWSjF/fd4fG6x1UViwfll5rqpcPb3pVLiid1MxhJkD02TDrrvi7GAT" "wQGBzcVwK13XY3izT6ZZJ9ay11xc8Ffrhdkr6+5ZLij4J33owWkGPp1ilLrOAAHggMDkw" "JkWpHHI/cohCot3xjzSH/iZaPFusec8STN5fSRjYtEb3U3MMyat6s7YjjEd5sWWoRD9e0" "SMd3XvKtqbgVT95cSVtSO8R4gIsCJ3YvFa/QJloqsdgCh4TvNVX79j7uCJhM48bLyLLTU" "QQt2TzEem1wCfVWxhUwllIkX7Wp2drm//Eut9RcdylQXKw/Fck+G1L7HdFoF24MycpUZt" "Frt+y1HfK/vH3QV/VVnalI9umo/Y55pTrpiTGvCuzyv9AgSU7fFdPloQ1Fc1/Y01NT6kp" "NW7PSEpH5o2r5oPj95nJf61aHv8js1EqXK3EZhcOVxlTEtO0rbfKPnnVKTZu7Q0sj2W9V" "XER2zFizCqt1+cRk8j5l2ldF3qc3OgKXhspJ8KzVSpetuKxYTqSyytJIm/f4NvdHT/hzQ" "h4uX1YtB0IEyOx8X5y4fVbuP0ACFys3jlmFc1P4fPAcDZS7Zc+B3MARLkVW4LgFc+/rcp" "9/jO7tDhcnt84yCscvmMffkj/5sIduHs21JqT1t2QEToCKZvqEzwbfosEaSVg4qiVILsa" "JnwkTVNNA9NuudWmRb1hYuPUL4YLzDF1vl2ihPi2VpLDQf/61nfEAl7RZzoJQfON1oVf6" "WJ5oS6HsGS1KxHO76+0ueuNqc0oqDpBp/KLofPendKYln8EARsosx09F7pvi1OBZeaBak" "heeSwnpLBQyc7WDGr7tovOK0bARabJ+Z0YQrv5e6Bn4i+xoU/zK41nQKSVV4sl47LHA3g" "oEZjmTnhlqrJynO2NVSlTsWzMs1/KXV1h1609Cr3tEHtunjs/X358qT8LXN87TsLKXAmE" "ZziztqPRQvwNw9IPiVyavifeGLwT+sXWRpKf0c+RHCmnMTqu3u2jSFbSc0ETPSNNS/Io0" "IHx+4+fiO+7zylxoUZBq8kNtfSnVXThPHYKj12u5ZVP3HwRH35+pp8Uje/LWr3DFI4/Sp" "3YqV1wLXAwPITiI0LKeRdEy9KYwfvZnYlf7BE0VhG/hyvMjHHGJsmorK9uS1Pu2wrrySM" "fsEKf+9rb8YZ1E0nd4QYV4jHTEXMcwywG12aoRmltVRc90/Ze65L4DCph6nrgQj/A11SV" "2prfaaqBrGBxEcN+DwdD/Q4CemCFo7fBbAQdAsAXH3jZPH5x6taD5QD+mp6KvVtCEw5sX" "zA1mV4ghmhNW66oJBwlgZmVNymOAdzrYFkF1pnz/zTbQKhvVHt07GnMnX1Q4AIAtgChgW" "ikIBRZCMAoGusaEgwQA1LDHRf86fRKneR/QbW9Q5k/YQawXdOGggEJx0HDA9Rtrabi8P2" "ywFw2SIThw0CgQBaOCfGtikPfaic64wACYIThIqAaUTz6I+5idygKWUYuBvggm8+5Dv2a" "/DHzNLXnJW+6iuq0Bmrr4PDld8+xhHwNZs5IEwxB39yusU3nHdZMgfzzBsOXwQmFBvBez" "Tnfk7EARzR/y6XXXXC+to5DM+3PwvG7Fgwa28xbP0n1NebdEtgOsBVMCjHwxT9SaFhiVM" "Sk4qARPXWJTO7YGYI8WdsDh0bFMB0DBmgxG9ZgrJfoWArXcScNBYQCEpy/ZCz+UrY/YAZ" "cpS+KWghuTWKxK1lpSDkddIDazYc8WtyQ815tqUNxKsOYLKLAULNRhPQo+MVUhJZYTKYz" "akvAAOAeFdNhfgIBHWo00P9YVL791iS+jcCtZrdwUAJRk/AoTJspXWuCo61KDQjw2oCBg" "OwE+egH3kfj7umAhCOmwEi050g4nslK+I4W/5CzyeuQ5f3kZ4tNlIZF18vP/AXYR+dvV3" "FCCAAAAAElFTkSuQmCC" ) return DASH_PIE_PNG_3 def get_report_favicon(): REPORT_FAVICON = ( "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAA" "AAXNSR0IArs4c6QAAAM9JREFUKBVjnNS8goEUwESKYpBaFiD+z/hTJz2EoM4rM9cw/mcH" "aYCA2Nl3YUwEvTBFkZGREchnBEIwAGv4z/6f4T8DWAIk+P8/RA6ZBCkAA4QNi1IUkVVgY" "f9nBwpCNTiLcGNRgSp0FcxF2CC3RhVVAcOjkNtAEYg4hA3kIjRAPYWmCeRdFIDQ8BBsHo" "okmIMmDtXw5s0bTKVoIhCrQBogLHaPUGQVP7avgnA5PMOAjJ87VkO4ZCUNiFa4GRAu3K9" "o4lA/LJ+xF6KOIEmykwBQHy74EMZM3QAAAABJRU5ErkJggg==" ) return REPORT_FAVICON def get_side_by_side_png(): SIDE_BY_SIDE_PNG = ( "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAA" "AAXNSR0IArs4c6QAAAIVJREFUKBVjvPLvPwMpgIkUxSC1LBANOo0oGq/Ug7hYBUm2gWQN" "jBBPa/1lRHbTNWZQSGAVhPqBaRWyeoYrkSAuozaKIMMNEJdkJ5GsAeqkf2Eotl8D8/5fR" "RGEcGB+aEGJb0g8MGEVxGIIXiFy/eC8CuJsmOH1WkAWVkFoxMEUEqZJdhIAo3Aj/iHmzl" "MAAAAASUVORK5CYII=" ) return SIDE_BY_SIDE_PNG def get_no_screenshot_png(): NO_SCREENSHOT = ( "iVBORw0KGgoAAAANSUhEUgAAAIwAAAA8CAAAAACRYQ2XAAAF10lEQVRo3u3WeVATVxwH8" "AiGIDEkEEjEoCCpXGVsFI3EE5DBitd4Y6nngK1a79pRUYeq1FIto9JWO9ra4mCZoiCKR4" "tarMihYAEJJIpXNgmQmJBAIHe+/QORadC2M870mNn9a/f3e2/eZ/f33ttHwX/oopAYEkN" "iSAyJITEkhsSQGBJDYkgMiSExJIbEkBgSQ2JIDIn59zA6RbP5H8Los7bkWQHAdDyt+SXt" "avfEDA1OOiZ/nbFUn33R1TdqNZidMYrRlKDbAKCfwqzr2+PaCCpPGDqQmtDwGpjGIKGmb" "/RO4jd2J4wyikJZ1AagPZ59r0+HZwnuaU81rdenUFJeo1SSYJG2b7TIfYPDGTN24BCP47" "0Ys/LRk97XqOWKVABwk/OmDECHrInoBGwarV2jtALWZkJh7G5pkBNqBwCLqsOhJuSdAGB" "rJQiVHZAEj9NqCaIdAGCUP5TpAIchx+O9FosTRhiUzhop7cHc/2AYx1d0qqfElaxxLQCg" "O/SpCiidNciHt6gazQsTs8cKCDzYyucMXVpqA6wXZvtxBOkEUBO140sBxz+xDlCnh3K5E" "ZntkISMOjyW4zf9hgOoT/HncGN/MJu38Vy8hNXOmGE171PWGrsx8gS3Obs3D2MesnVnm8" "JoS660WgDYgV9DGMtS51Mny+WRnjxufOvjONr01JXM4GtAHtdvzbZJ/ZN0KGOxeYt3jKc" "s6LCl0d/enRpFOwhJmPugWamzXSfI8GDSgPlp64d6f2fNm08dk/bYGRPYJB3BKkB7PLse" "B11W6IGr/nxxd9aSNYjiEbZ83+VmoCOp/14zNHNpZxRCSqJEa9lJ3aiD9RA1yfRkxOCLg" "HKm22mUe3seM6NmOL9BPT6wDqiKmN0hDaPuMUAxmXkV21w3G4BCL4ECl+mbnVeTUhh4H1" "+7x7QY4tli/QT2bQDGpe65z9Om0r0iH9d+nhMvQcofLgVQtqeSGM2rBBRRgTWwOWTh/NY" "c6mqz3YYztFWOcla0Bmifyi1XTxiwX2EyN9TbJSEhUsC62qNQNX5IFQBTIq0QRfSN9r4Y" "KXQLXfa1TWWLiYDRrQAcabSs3lmvFWd/OJISIalkxeq6I0TkWy1ALY/9bkpyynIeW7zPR" "ZicnJwyq9/0zgpmkgnomMG55cjyceUvTi8zQRIs0gC2TR4FDcOi1ACwg3LklRhU83kXEt" "gNTbxoDQD77ucYx0+ZjwDALp5Ey7nBmGHowYxUAVW+jIkx0dExcQubUl2Gx0RHR8fG7TR" "VMJeZgY4ZnFJYSlIFTCp3u1EaLNJ2Y2p4E9sAYDsl69UYWwZ1ushHrOSHEwCMK3rKtIly" "FABg30Q9WsUWPQPw21f3iEiBChAHhorb9Xrt3WpLhstHOr1eryxvQnkP5paxUQV1xX4er" "/r+C8yDEAEBwLyYdv7VGLROdXXn1pvmuZ0CIA0PqO3OnugX3wwAzQnuZ5RCn2tA10pmvi" "JSoAI08Yx8ALWjEw1FA2I1AI7xs3ox5XWj1poA0wJWyYMeTH77NM8CADWDwwgU0Tf2mcB" "jAqUAUOJP4dzDOa/Q7xt/eYe65fl++zTaZW5OcXH2HGqMzHGAFpdf8rGnSCaPFKgAfEuP" "Olt3fppbJnTz3NaV3s0cHFL3AuNbphzne+ThoxOBo568+DJncZIe+WNj8UzqJzYUMybmq" "Z0w44PvA4Bll/sQMYwZXGbQIMYSRY/59jQ6neVFZ8y9B2g3env7McZchzxKqALQmcH1Cv" "TxXqcFGhPofgEDwwuBCt9kM2CY41+G/ADGG8M9A/IgjZisBWxbvQrQtc+HFcT1TFYDD0V" "uAeV/xHRdKujeppW5eTrAcvPA+vSCtt61pD63d8WqzMs6AOj8+Wjm6UdA58UiIwBYbny+" "9fDVTgBoydm562Q9AFVumR2wluSp4LhzaPWarCoH9IVXzICj6rQMMF0/sGF/kR6A4252r" "uqvzjNWp2eH+U/+kbYXd3b7S9IWy98YgDx2/k8wvwNEPGrBGochUwAAAABJRU5ErkJggg" "==" ) return NO_SCREENSHOT ================================================ FILE: seleniumbase/core/jqc_helper.py ================================================ """This module contains methods for opening jquery-confirm boxes. These helper methods SHOULD NOT be called directly from tests.""" from seleniumbase.fixtures import constants from seleniumbase.fixtures import js_utils form_code = """'<form align="center" action="" class="jqc_form">' + '<div class="form-group">' + '<input style="font-size:20px; background-color: #f8fdfd; ' + ' width: 84%%; border: 1px solid blue; ' + ' box-shadow:inset 0 0 2px 2px #f4fafa;"' + ' type="text" class="jqc_input" />' + '</div>' + '</form>'""" def jquery_confirm_button_dialog(driver, message, buttons, options=None): js_utils.activate_jquery_confirm(driver) # These defaults will be overwritten later if set theme = constants.JqueryConfirm.DEFAULT_THEME border_color = constants.JqueryConfirm.DEFAULT_COLOR width = constants.JqueryConfirm.DEFAULT_WIDTH if options: for option in options: if option[0].lower() == "theme": theme = option[1] elif option[0].lower() == "color": border_color = option[1] elif option[0].lower() == "width": width = option[1] else: raise Exception('Unknown option: "%s"' % option[0]) if not message: message = "" key_row = "" if len(buttons) == 1: # There's only one button as an option key_row = "keys: ['enter', 'y', '1']," # Shortcut: "Enter","Y","1" b_html = """button_%s: { btnClass: 'btn-%s', text: '<b>%s</b>', %s action: function(){ jqc_status = '%s'; $jqc_status = jqc_status; jconfirm.lastButtonText = jqc_status; } },""" all_buttons = "" btn_count = 0 for button in buttons: btn_count += 1 text = button[0] text = js_utils.escape_quotes_if_needed(text) if len(buttons) > 1 and text.lower() == "yes": key_row = "keys: ['y']," if btn_count < 10: key_row = "keys: ['y', '%s']," % btn_count elif len(buttons) > 1 and text.lower() == "no": key_row = "keys: ['n']," if btn_count < 10: key_row = "keys: ['n', '%s']," % btn_count elif len(buttons) > 1: if btn_count < 10: key_row = "keys: ['%s']," % btn_count color = button[1] if not color: color = "blue" new_button = b_html % (btn_count, color, text, key_row, text) all_buttons += new_button content = '<div></div><font color="#0066ee">%s</font>' % (message) content = js_utils.escape_quotes_if_needed(content) overlay_opacity = "0.32" if theme.lower() == "supervan": overlay_opacity = "0.56" if theme.lower() == "bootstrap": overlay_opacity = "0.64" if theme.lower() == "modern": overlay_opacity = "0.5" if theme.lower() == "material": overlay_opacity = "0.4" jqcd = """jconfirm({ boxWidth: '%s', useBootstrap: false, containerFluid: true, bgOpacity: %s, type: '%s', theme: '%s', animationBounce: 1, typeAnimated: true, animation: 'scale', draggable: true, dragWindowGap: 1, container: 'body', title: '%s', content: '<div></div>', buttons: { %s } });""" % ( width, overlay_opacity, border_color, theme, content, all_buttons, ) driver.execute_script(jqcd) def jquery_confirm_text_dialog(driver, message, button=None, options=None): js_utils.activate_jquery_confirm(driver) # These defaults will be overwritten later if set theme = constants.JqueryConfirm.DEFAULT_THEME border_color = constants.JqueryConfirm.DEFAULT_COLOR width = constants.JqueryConfirm.DEFAULT_WIDTH if not message: message = "" if button: if not isinstance(button, (list, tuple)) or len(button) != 2: raise Exception('"button" should be a (text, color) tuple!') else: button = ("Submit", "blue") if options: for option in options: if option[0].lower() == "theme": theme = option[1] elif option[0].lower() == "color": border_color = option[1] elif option[0].lower() == "width": width = option[1] else: raise Exception('Unknown option: "%s"' % option[0]) btn_text = button[0] btn_color = button[1] if not btn_color: btn_color = "blue" content = '<div></div><font color="#0066ee">%s</font>' % (message) content = js_utils.escape_quotes_if_needed(content) overlay_opacity = "0.32" if theme.lower() == "supervan": overlay_opacity = "0.56" if theme.lower() == "bootstrap": overlay_opacity = "0.64" if theme.lower() == "modern": overlay_opacity = "0.5" if theme.lower() == "material": overlay_opacity = "0.4" jqcd = """jconfirm({ boxWidth: '%s', useBootstrap: false, containerFluid: true, bgOpacity: %s, type: '%s', theme: '%s', animationBounce: 1, typeAnimated: true, animation: 'scale', draggable: true, dragWindowGap: 1, container: 'body', title: '%s', content: '<div></div>' + %s, buttons: { formSubmit: { btnClass: 'btn-%s', text: '%s', action: function () { jqc_input = this.$content.find('.jqc_input').val(); $jqc_input = this.$content.find('.jqc_input').val(); jconfirm.lastInputText = jqc_input; $jqc_status = '%s'; // There is only one button }, }, }, onContentReady: function () { var jc = this; this.$content.find('form.jqc_form').on('submit', function (e) { // User submits the form by pressing "Enter" in the field e.preventDefault(); jc.$$formSubmit.trigger('click'); // Click the button }); } });""" % ( width, overlay_opacity, border_color, theme, content, form_code, btn_color, btn_text, btn_text, ) driver.execute_script(jqcd) def jquery_confirm_full_dialog(driver, message, buttons, options=None): js_utils.activate_jquery_confirm(driver) # These defaults will be overwritten later if set theme = constants.JqueryConfirm.DEFAULT_THEME border_color = constants.JqueryConfirm.DEFAULT_COLOR width = constants.JqueryConfirm.DEFAULT_WIDTH if not message: message = "" btn_count = 0 b_html = """button_%s: { btnClass: 'btn-%s', text: '%s', action: function(){ jqc_input = this.$content.find('.jqc_input').val(); $jqc_input = this.$content.find('.jqc_input').val(); jconfirm.lastInputText = jqc_input; $jqc_status = '%s'; } },""" b1_html = """formSubmit: { btnClass: 'btn-%s', text: '%s', action: function(){ jqc_input = this.$content.find('.jqc_input').val(); $jqc_input = this.$content.find('.jqc_input').val(); jconfirm.lastInputText = jqc_input; jqc_status = '%s'; $jqc_status = jqc_status; jconfirm.lastButtonText = jqc_status; } },""" one_button_trigger = "" if len(buttons) == 1: # If there's only one button, allow form submit with "Enter/Return" one_button_trigger = "jc.$$formSubmit.trigger('click');" all_buttons = "" for button in buttons: text = button[0] text = js_utils.escape_quotes_if_needed(text) color = button[1] if not color: color = "blue" btn_count += 1 if len(buttons) == 1: new_button = b1_html % (color, text, text) else: new_button = b_html % (btn_count, color, text, text) all_buttons += new_button if options: for option in options: if option[0].lower() == "theme": theme = option[1] elif option[0].lower() == "color": border_color = option[1] elif option[0].lower() == "width": width = option[1] else: raise Exception('Unknown option: "%s"' % option[0]) content = '<div></div><font color="#0066ee">%s</font>' % (message) content = js_utils.escape_quotes_if_needed(content) overlay_opacity = "0.32" if theme.lower() == "supervan": overlay_opacity = "0.56" if theme.lower() == "bootstrap": overlay_opacity = "0.64" if theme.lower() == "modern": overlay_opacity = "0.5" if theme.lower() == "material": overlay_opacity = "0.4" jqcd = """jconfirm({ boxWidth: '%s', useBootstrap: false, containerFluid: true, bgOpacity: %s, type: '%s', theme: '%s', animationBounce: 1, typeAnimated: true, animation: 'scale', draggable: true, dragWindowGap: 1, container: 'body', title: '%s', content: '<div></div>' + %s, buttons: { %s }, onContentReady: function () { var jc = this; this.$content.find('form.jqc_form').on('submit', function (e) { // User submits the form by pressing "Enter" in the field e.preventDefault(); %s }); } });""" % ( width, overlay_opacity, border_color, theme, content, form_code, all_buttons, one_button_trigger, ) driver.execute_script(jqcd) ================================================ FILE: seleniumbase/core/log_helper.py ================================================ import os import shutil import sys import time from contextlib import suppress from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils python3_11_or_newer = False if sys.version_info >= (3, 11): python3_11_or_newer = True py311_patch2 = constants.PatchPy311.PATCH def __is_cdp_swap_needed(driver): """If the driver is disconnected, use a CDP method when available.""" return shared_utils.is_cdp_swap_needed(driver) def log_screenshot(test_logpath, driver, screenshot=None, get=False): screenshot_name = settings.SCREENSHOT_NAME screenshot_path = os.path.join(test_logpath, screenshot_name) screenshot_skipped = constants.Warnings.SCREENSHOT_SKIPPED screenshot_warning = constants.Warnings.SCREENSHOT_UNDEFINED if ( getattr(sb_config, "no_screenshot", None) or screenshot == screenshot_skipped ): if get: return screenshot return try: if not screenshot: element = driver.find_element("tag name", "body") screenshot = element.screenshot_as_base64 if screenshot != screenshot_warning: with open(screenshot_path, mode="wb") as file: file.write(screenshot) with suppress(Exception): shared_utils.make_writable(screenshot_path) else: print("WARNING: %s" % screenshot_warning) if get: return screenshot except Exception: try: driver.get_screenshot_as_file(screenshot_path) except Exception: print("WARNING: %s" % screenshot_warning) def get_master_time(): """Returns (timestamp, the_date, the_time)""" import datetime timestamp = str(int(time.time())) + " (Unix Timestamp)" now = datetime.datetime.now() utc_offset = -time.timezone / 3600.0 utc_offset += time.daylight utc_str = "UTC+0" if utc_offset > 0: if utc_offset < 10: utc_str = "UTC+0%s" % utc_offset else: utc_str = "UTC+%s" % utc_offset elif utc_offset < 0: if utc_offset > -10: utc_str = "UTC-0%s" % abs(utc_offset) else: utc_str = "UTC-%s" % abs(utc_offset) utc_str = utc_str.replace(".5", ".3").replace(".", ":") + "0" time_zone = "" try: time_zone = "(" + time.tzname[time.daylight] + ", " + utc_str + ")" except Exception: time_zone = "(" + utc_str + ")" # Use [Day-of-Week, Month Day, Year] format when time zone < GMT/UTC-3 the_date = now.strftime("%A, %B %d, %Y").replace(" 0", " ") if utc_offset >= -3: # Use [Day-of-Week, Day Month Year] format when time zone >= GMT/UTC-3 the_date = now.strftime("%A, %d %B %Y").replace(" 0", " ") the_time = now.strftime("%I:%M:%S %p ") + time_zone if the_time.startswith("0"): the_time = the_time[1:] return timestamp, the_date, the_time def get_browser_version(driver): if ( python3_11_or_newer and py311_patch2 and hasattr(sb_config, "_browser_version") ): return sb_config._browser_version driver_capabilities = driver.capabilities if "version" in driver_capabilities: browser_version = driver_capabilities["version"] else: browser_version = driver_capabilities["browserVersion"] return browser_version def get_driver_name_and_version(driver, browser): if hasattr(sb_config, "_driver_name_version"): return sb_config._driver_name_version if driver.capabilities["browserName"].lower() == "chrome": cap_dict = driver.capabilities["chrome"] return ("chromedriver", cap_dict["chromedriverVersion"].split(" ")[0]) elif driver.capabilities["browserName"].lower() == "msedge": cap_dict = driver.capabilities["msedge"] return ("msedgedriver", cap_dict["msedgedriverVersion"].split(" ")[0]) elif driver.capabilities["browserName"].lower() == "firefox": return ("geckodriver", driver.capabilities["moz:geckodriverVersion"]) elif browser == "safari": return ("safaridriver", get_browser_version(driver)) elif browser == "ie": return ("iedriver", get_browser_version(driver)) else: return None def log_test_failure_data(test, test_logpath, driver, browser, url=None): import traceback browser_displayed = browser driver_displayed = None browser_version = None driver_version = None driver_name = None duration = None exc_message = None try: browser_version = get_browser_version(driver) except Exception: pass try: driver_name, driver_version = get_driver_name_and_version( driver, browser ) except Exception: pass try: duration = "%.2fs" % (time.time() - (sb_config.start_time_ms / 1000.0)) except Exception: duration = "(Unknown Duration)" if browser_version: headless = "" if test.headless and browser in ["chrome", "edge", "firefox"]: headless = " / headless" if test.headless2 and browser in ["chrome", "edge"]: headless = " / headless2" if browser and len(browser) > 1: # Capitalize the first letter browser = "%s%s" % (browser[0].upper(), browser[1:]) browser_displayed = "%s %s%s" % (browser, browser_version, headless) if driver_name and driver_version: driver_displayed = "%s %s" % (driver_name, driver_version) else: browser_displayed = browser driver_displayed = "(Unknown Driver)" if not driver_displayed: driver_displayed = "(Unknown Driver)" basic_info_name = settings.BASIC_INFO_NAME basic_file_path = os.path.join(test_logpath, basic_info_name) if url: last_page = url else: last_page = get_last_page(driver) sb_config._fail_page = last_page timestamp, the_date, the_time = get_master_time() test_id = get_test_id(test) # pytest runnable display_id (with the "::") data_to_save = [] data_to_save.append("%s" % test_id) data_to_save.append( "--------------------------------------------------------------------" ) data_to_save.append("Last Page: %s" % last_page) data_to_save.append(" Duration: %s" % duration) data_to_save.append(" Browser: %s" % browser_displayed) data_to_save.append(" Driver: %s" % driver_displayed) data_to_save.append("Timestamp: %s" % timestamp) data_to_save.append(" Date: %s" % the_date) data_to_save.append(" Time: %s" % the_time) data_to_save.append( "--------------------------------------------------------------------" ) if hasattr(test, "_outcome") and getattr(test._outcome, "errors", None): try: exc_message = test._outcome.errors[-1][1][1] traceback_address = test._outcome.errors[-1][1][2] traceback_list = traceback.format_list( traceback.extract_tb(traceback_address)[1:] ) updated_list = [] counter = 0 for traceback_item in traceback_list: if "self._callTestMethod(testMethod)" in traceback_item: counter = 1 updated_list.append(traceback_item) # In case not cleared continue elif ( ", in _callTestMethod" in traceback_item.strip() and "method()" in traceback_item.strip() and counter == 1 ): counter = 0 updated_list = [] continue else: counter = 0 updated_list.append(traceback_item) traceback_list = updated_list traceback_message = "".join(traceback_list).strip() except Exception: exc_message = "(Unknown Exception)" traceback_message = "(Unknown Traceback)" traceback_message = str(traceback_message).strip() data_to_save.append("Traceback:\n %s" % traceback_message) data_to_save.append("Exception: %s" % exc_message) else: traceback_message = None if getattr(test, "is_behave", None): if sb_config.behave_scenario.status.name == "failed": if ( hasattr(sb_config, "behave_step") and getattr(sb_config.behave_step, "error_message", None) ): traceback_message = sb_config.behave_step.error_message else: format_exception = traceback.format_exception( sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2] ) if format_exception: updated_list = [] for line in format_exception: if "sb_manager.py" in line and "yield sb" in line: continue updated_list.append(line) format_exception = updated_list traceback_message = "".join(format_exception) if ( not traceback_message or len(str(traceback_message)) < 30 or traceback_message.endswith("StopIteration\n") ): good_stack = [] the_stacks = [] if hasattr(sys, "last_traceback"): the_stacks = traceback.format_list( traceback.extract_tb(sys.last_traceback) ) elif hasattr(sb_config, "_excinfo_tb"): the_stacks = traceback.format_list( traceback.extract_tb(sb_config._excinfo_tb) ) else: message = None if getattr(test, "is_behave", None): message = "Behave step was not implemented or skipped!" else: message = "Traceback not found!" the_stacks = [message] for stack in the_stacks: if "/site-packages/pluggy/" not in stack: if "/site-packages/_pytest/" not in stack: good_stack.append(stack) traceback_message = str("".join(good_stack)).strip() data_to_save.append("Traceback:\n %s" % traceback_message) if hasattr(sys, "last_value"): last_value = sys.last_value if last_value: data_to_save.append("Exception: %s" % last_value) elif hasattr(sb_config, "_excinfo_value"): data_to_save.append("Exception: %s" % sb_config._excinfo_value) else: data_to_save.append("Traceback:\n %s" % traceback_message) if getattr(test, "is_nosetest", None): # Also save the data for the report sb_config._report_test_id = test_id sb_config._report_fail_page = last_page sb_config._report_duration = duration sb_config._report_browser = browser_displayed sb_config._report_driver = driver_displayed sb_config._report_timestamp = timestamp sb_config._report_date = the_date sb_config._report_time = the_time sb_config._report_traceback = traceback_message sb_config._report_exception = exc_message if not os.path.exists(test_logpath): with suppress(Exception): os.makedirs(test_logpath) with suppress(Exception): log_file = open(basic_file_path, mode="w+", encoding="utf-8") log_file.writelines("\r\n".join(data_to_save)) log_file.close() shared_utils.make_writable(basic_file_path) def log_skipped_test_data(test, test_logpath, driver, browser, reason): browser_displayed = browser driver_displayed = None browser_version = None driver_version = None driver_name = None with suppress(Exception): browser_version = get_browser_version(driver) with suppress(Exception): driver_name, driver_version = get_driver_name_and_version( driver, browser ) if browser_version: headless = "" if test.headless and browser in ["chrome", "edge", "firefox"]: headless = " / headless" if test.headless2 and browser in ["chrome", "edge"]: headless = " / headless2" if browser and len(browser) > 1: # Capitalize the first letter browser = "%s%s" % (browser[0].upper(), browser[1:]) browser_displayed = "%s %s%s" % (browser, browser_version, headless) if driver_name and driver_version: driver_displayed = "%s %s" % (driver_name, driver_version) else: browser_displayed = browser driver_displayed = "(Unknown Driver)" if not driver_displayed: driver_displayed = "(Unknown Driver)" timestamp, the_date, the_time = get_master_time() test_id = get_test_id(test) # pytest runnable display_id (with the "::") data_to_save = [] data_to_save.append("%s" % test_id) data_to_save.append( "--------------------------------------------------------------------" ) data_to_save.append(" Outcome: SKIPPED") data_to_save.append(" Browser: %s" % browser_displayed) data_to_save.append(" Driver: %s" % driver_displayed) data_to_save.append(" Timestamp: %s" % timestamp) data_to_save.append(" Date: %s" % the_date) data_to_save.append(" Time: %s" % the_time) data_to_save.append( "--------------------------------------------------------------------" ) data_to_save.append(" * Skip Reason: %s" % reason) data_to_save.append("") file_path = os.path.join(test_logpath, "skip_reason.txt") with suppress(Exception): log_file = open(file_path, mode="w+", encoding="utf-8") log_file.writelines("\r\n".join(data_to_save)) log_file.close() shared_utils.make_writable(file_path) def log_page_source(test_logpath, driver, source=None): html_file_name = settings.PAGE_SOURCE_NAME if source: page_source = source else: try: page_source = None if __is_cdp_swap_needed(driver): page_source = driver.cdp.get_page_source() else: page_source = driver.page_source page_source = get_html_source_with_base_href(driver, page_source) except Exception: source = constants.Warnings.PAGE_SOURCE_UNDEFINED page_source = constants.Warnings.PAGE_SOURCE_UNDEFINED if source == constants.Warnings.PAGE_SOURCE_UNDEFINED: page_source = ( "<h3>Warning: " + source + ( "</h3>\n<h4>The browser window was either unreachable, " "unresponsive, or closed prematurely!</h4>" ) ) if not os.path.exists(test_logpath): with suppress(Exception): os.makedirs(test_logpath) html_file_path = os.path.join(test_logpath, html_file_name) with suppress(Exception): html_file = open(html_file_path, mode="w+", encoding="utf-8") html_file.write(page_source) html_file.close() shared_utils.make_writable(html_file_path) def get_test_id(test): if getattr(test, "is_behave", None): file_name = sb_config.behave_scenario.filename line_num = sb_config.behave_line_num scenario_name = sb_config.behave_scenario.name if " -- @" in scenario_name: scenario_name = scenario_name.split(" -- @")[0] test_id = "%s:%s => %s" % (file_name, line_num, scenario_name) return test_id elif getattr(test, "is_context_manager", None): filename = test.__class__.__module__.split(".")[-1] + ".py" classname = test.__class__.__name__ methodname = test._testMethodName context_id = None if filename == "base_case.py" or methodname == "runTest": import traceback stack_base = traceback.format_stack()[0].split(", in ")[0] test_base = stack_base.split(", in ")[0].split(os.sep)[-1] if getattr(test, "cm_filename", None): filename = test.cm_filename else: filename = test_base.split('"')[0] classname = "SB" methodname = ".py:" + test_base.split(", line ")[-1] context_id = filename.split(".")[0] + methodname + ":" + classname return context_id test_id = None try: test_id = get_test_name(test) except Exception: test_id = "%s.%s.%s" % ( test.__class__.__module__, test.__class__.__name__, test._testMethodName, ) if test._sb_test_identifier and len(str(test._sb_test_identifier)) > 6: test_id = test._sb_test_identifier return test_id def get_test_name(test): if "PYTEST_CURRENT_TEST" in os.environ: full_name = os.environ["PYTEST_CURRENT_TEST"] if "] " in full_name: test_name = full_name.split("] ")[0] + "]" else: test_name = full_name.split(" ")[0] elif test.is_pytest: test_name = "%s.py::%s::%s" % ( test.__class__.__module__.split(".")[-1], test.__class__.__name__, test._testMethodName, ) else: test_name = "%s.py:%s.%s" % ( test.__class__.__module__.split(".")[-1], test.__class__.__name__, test._testMethodName, ) if test._sb_test_identifier and len(str(test._sb_test_identifier)) > 6: test_name = test._sb_test_identifier return test_name def get_last_page(driver): try: last_page = None if __is_cdp_swap_needed(driver): last_page = driver.cdp.get_current_url() else: last_page = driver.current_url except Exception: last_page = "[WARNING! Browser Not Open!]" if len(last_page) < 5: last_page = "[WARNING! Browser Not Open!]" return last_page def get_base_url(full_url): protocol = full_url.split("://")[0] simple_url = full_url.split("://")[1] base_url = simple_url.split("/")[0] full_base_url = "%s://%s" % (protocol, base_url) return full_base_url def get_base_href_html(full_url): """The base href line tells the html what the base page really is. This is important when trying to open the page outside it's home.""" base_url = get_base_url(full_url) return '<base href="%s">' % base_url def get_html_source_with_base_href(driver, page_source): """Combines the domain base href with the html source. Also adds on the meta charset, which may get dropped. This is needed for the page html to render correctly.""" last_page = get_last_page(driver) meta_charset = '<meta charset="utf-8">' if "://" in last_page: base_href_html = get_base_href_html(last_page) if ' charset="' not in page_source: return "%s\n%s\n%s" % (base_href_html, meta_charset, page_source) else: return "%s\n%s" % (base_href_html, page_source) return "" def copytree(src, dst, symlinks=False, ignore=None): if not os.path.exists(dst): os.makedirs(dst) for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) if os.path.isdir(s): copytree(s, d, symlinks, ignore) else: if not os.path.exists(d) or ( os.stat(s).st_mtime - os.stat(d).st_mtime > 1 ): shutil.copy2(s, d) def archive_logs_if_set(log_path, archive_logs=False): """Handle Logging""" arg_join = " ".join(sys.argv) if ("-n" in sys.argv) or ("-n=" in arg_join) or (arg_join == "-c"): return # Skip if multithreaded if log_path.endswith("/"): log_path = log_path[:-1] if not os.path.exists(log_path): try: os.makedirs(log_path) except Exception: pass # Only reachable during multi-threaded runs else: if settings.ARCHIVE_EXISTING_LOGS or archive_logs: if len(os.listdir(log_path)) > 0: saved_folder = "%s/../%s/" % (log_path, constants.Logs.SAVED) archived_folder = os.path.realpath(saved_folder) + "/" log_path = os.path.realpath(log_path) + "/" if not os.path.exists(archived_folder): try: os.makedirs(archived_folder) except Exception: pass # Only reachable during multi-threaded runs time_id = str(int(time.time())) archived_logs = "%slogs_%s" % (archived_folder, time_id) copytree(log_path, archived_logs) def log_folder_setup(log_path, archive_logs=False): """Clean up logs to prepare for another run""" if log_path.endswith("/"): log_path = log_path[:-1] if log_path.startswith("/"): log_path = log_path[1:] if constants.Logs.SAVED.endswith("/"): constants.Logs.SAVED = constants.Logs.SAVED[:-1] if constants.Logs.SAVED.startswith("/"): constants.Logs.SAVED = constants.Logs.SAVED[1:] if len(log_path) < 10 or len(constants.Logs.SAVED) < 10: return # Prevent accidental deletions if constants are renamed if not os.path.exists(log_path): try: os.makedirs(log_path) except Exception: pass # Only reachable during multi-threaded runs else: saved_folder = "%s/../%s/" % (log_path, constants.Logs.SAVED) archived_folder = os.path.realpath(saved_folder) + "/" if not os.path.exists(archived_folder): try: os.makedirs(archived_folder) except Exception: pass # Only reachable during multi-threaded runs archived_logs = "%slogs_%s" % (archived_folder, int(time.time())) if len(os.listdir(log_path)) > 0: try: shutil.move(log_path, archived_logs) os.makedirs(log_path) except Exception: pass # A file was probably open at the time if not settings.ARCHIVE_EXISTING_LOGS and not archive_logs: shutil.rmtree(archived_logs) else: a_join = " ".join(sys.argv) if ("-n" in sys.argv) or ("-n=" in a_join) or (a_join == "-c"): # Logs are saved/archived now if tests are multithreaded pass else: shutil.rmtree(archived_logs) # (Archive test run later) def clear_empty_logs(): latest_logs_dir = os.path.join(os.getcwd(), constants.Logs.LATEST) + os.sep archived_folder = os.path.join(os.getcwd(), constants.Logs.SAVED) + os.sep if os.path.exists(latest_logs_dir) and not os.listdir(latest_logs_dir): try: os.rmdir(latest_logs_dir) except OSError: pass if os.path.exists(archived_folder) and not os.listdir(archived_folder): try: os.rmdir(archived_folder) except OSError: pass ================================================ FILE: seleniumbase/core/mysql.py ================================================ """Wrapper for MySQL DB functions""" class DatabaseManager: """This class wraps MySQL database methods for easy use.""" def __init__(self, database_env="test", conf_creds=None): """Create a connection to the MySQL DB.""" import fasteners import time from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import settings_parser from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) with pip_find_lock: try: import cryptography # noqa: F401 import pymysql except Exception: shared_utils.pip_install("PyMySQL[rsa]", version="1.1.1") import pymysql db_server = settings.DB_HOST db_port = settings.DB_PORT db_user = settings.DB_USERNAME db_pass = settings.DB_PASSWORD db_schema = settings.DB_SCHEMA if getattr(sb_config, "settings_file", None): override = settings_parser.set_settings(sb_config.settings_file) if "DB_HOST" in override.keys(): db_server = override["DB_HOST"] if "DB_PORT" in override.keys(): db_port = override["DB_PORT"] if "DB_USERNAME" in override.keys(): db_user = override["DB_USERNAME"] if "DB_PASSWORD" in override.keys(): db_pass = override["DB_PASSWORD"] if "DB_SCHEMA" in override.keys(): db_schema = override["DB_SCHEMA"] retry_count = 3 backoff = 1.2 # Time to wait (in seconds) between retries. count = 0 while count < retry_count: try: self.conn = pymysql.connect( host=db_server, port=db_port, user=db_user, password=db_pass, database=db_schema, ) self.conn.autocommit(True) self.cursor = self.conn.cursor() return except Exception: time.sleep(backoff) count = count + 1 if retry_count == 3: print("Unable to connect to Database after 3 retries.") raise def query_fetch_all(self, query, values): """Execute db query, get all the values, and close the connection.""" self.cursor.execute(query, values) retval = self.cursor.fetchall() self.__close_db() return retval def query_fetch_one(self, query, values): """Execute db query, get the first value, and close the connection.""" self.cursor.execute(query, values) retval = self.cursor.fetchone() self.__close_db() return retval def execute_query(self, query, values): """Execute db query, close the connection, and return the results.""" retval = self.cursor.execute(query, values) self.__close_db() return retval def __close_db(self): self.cursor.close() self.conn.close() ================================================ FILE: seleniumbase/core/proxy_helper.py ================================================ import os import re import warnings import zipfile from contextlib import suppress from seleniumbase.config import proxy_list from seleniumbase.config import settings from seleniumbase.fixtures import constants from seleniumbase.fixtures import page_utils from seleniumbase.fixtures import shared_utils DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER PROXY_ZIP_PATH = os.path.join(DOWNLOADS_DIR, "proxy.zip") PROXY_ZIP_LOCK = os.path.join(DOWNLOADS_DIR, "proxy.lock") PROXY_DIR_PATH = os.path.join(DOWNLOADS_DIR, "proxy_ext_dir") PROXY_DIR_LOCK = os.path.join(DOWNLOADS_DIR, "proxy_dir.lock") def create_proxy_ext( proxy_string, proxy_user, proxy_pass, proxy_scheme="http", bypass_list=None, zip_it=True, ): """Implementation of https://stackoverflow.com/a/35293284 for https://stackoverflow.com/questions/12848327/ (Run Selenium on a proxy server that requires authentication.) Solution involves creating & adding a Chromium extension at runtime. CHROMIUM-ONLY! *** Only Chrome and Edge browsers are supported. *** """ background_js = None if not bypass_list: bypass_list = "" if proxy_string: proxy_protocol = "" if proxy_string.count("://") == 1: proxy_protocol = proxy_string.split("://")[0] + "://" proxy_string = proxy_string.split("://")[1] proxy_host = proxy_protocol + proxy_string.split(":")[0] proxy_port = proxy_string.split(":")[1] background_js = ( """var config = {\n""" """ mode: "fixed_servers",\n""" """ rules: {\n""" """ singleProxy: {\n""" """ scheme: "%s",\n""" """ host: "%s",\n""" """ port: parseInt("%s")\n""" """ },\n""" """ bypassList: ["%s"]\n""" """ }\n""" """ };\n""" """chrome.proxy.settings.set(""" """{value: config, scope: "regular"}, function() {""" """});\n""" """function callbackFn(details) {\n""" """ return {\n""" """ authCredentials: {\n""" """ username: "%s",\n""" """ password: "%s"\n""" """ }\n""" """ };\n""" """}\n""" """chrome.webRequest.onAuthRequired.addListener(\n""" """ callbackFn,\n""" """ {urls: ["<all_urls>"]},\n""" """ ['blocking']\n""" """);""" % ( proxy_scheme, proxy_host, proxy_port, bypass_list, proxy_user, proxy_pass, ) ) else: background_js = ( """var config = {\n""" """ mode: "fixed_servers",\n""" """ rules: {\n""" """ },\n""" """ bypassList: ["%s"]\n""" """ };\n""" """chrome.proxy.settings.set(""" """{value: config, scope: "regular"}, function() {""" """});\n""" """function callbackFn(details) {\n""" """ return {\n""" """ authCredentials: {\n""" """ username: "%s",\n""" """ password: "%s"\n""" """ }\n""" """ };\n""" """}\n""" """chrome.webRequest.onAuthRequired.addListener(\n""" """ callbackFn,\n""" """ {urls: ["<all_urls>"]},\n""" """ ['blocking']\n""" """);""" % (bypass_list, proxy_user, proxy_pass) ) manifest_json = ( """{\n""" """"version": "1.0.0",\n""" """"manifest_version": 3,\n""" """"name": "Chrome Proxy",\n""" """"permissions": [\n""" """ "proxy",\n""" """ "tabs",\n""" """ "unlimitedStorage",\n""" """ "storage",\n""" """ "webRequest",\n""" """ "webRequestAuthProvider"\n""" """],\n""" """"host_permissions": [\n""" """ "<all_urls>"\n""" """],\n""" """"background": {\n""" """ "service_worker": "background.js"\n""" """},\n""" """"minimum_chrome_version":"88.0.0"\n""" """}""" ) abs_path = os.path.abspath(".") downloads_path = os.path.join(abs_path, DOWNLOADS_DIR) if not os.path.exists(downloads_path): os.mkdir(downloads_path) if zip_it: zf = zipfile.ZipFile(PROXY_ZIP_PATH, mode="w") zf.writestr("background.js", background_js) zf.writestr("manifest.json", manifest_json) zf.close() with suppress(Exception): shared_utils.make_writable(PROXY_ZIP_PATH) else: proxy_ext_dir = PROXY_DIR_PATH if not os.path.exists(proxy_ext_dir): os.mkdir(proxy_ext_dir) with suppress(Exception): shared_utils.make_writable(proxy_ext_dir) manifest_file = os.path.join(proxy_ext_dir, "manifest.json") with open(manifest_file, mode="w") as f: f.write(manifest_json) with suppress(Exception): shared_utils.make_writable(manifest_json) proxy_host = proxy_string.split(":")[0] proxy_port = proxy_string.split(":")[1] background_file = os.path.join(proxy_ext_dir, "background.js") with open(background_file, mode="w") as f: f.write(background_js) with suppress(Exception): shared_utils.make_writable(background_js) def remove_proxy_zip_if_present(): """Remove Chromium extension zip file used for proxy server authentication. Used in the implementation of https://stackoverflow.com/a/35293284 for https://stackoverflow.com/questions/12848327/ """ if os.path.exists(PROXY_ZIP_PATH): with suppress(Exception): os.remove(PROXY_ZIP_PATH) if os.path.exists(PROXY_ZIP_LOCK): with suppress(Exception): os.remove(PROXY_ZIP_LOCK) def validate_proxy_string(proxy_string, keep_scheme=False): if proxy_string in proxy_list.PROXY_LIST.keys(): proxy_string = proxy_list.PROXY_LIST[proxy_string] if not proxy_string: return None proxy_scheme = "http" if proxy_string.startswith("https://"): proxy_scheme = "https" elif proxy_string.startswith("socks4://"): proxy_scheme = "socks4" elif proxy_string.startswith("socks5://"): proxy_scheme = "socks5" valid = False val_ip = re.match( r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$", proxy_string ) if not val_ip: if proxy_string.startswith("http://"): proxy_string = proxy_string.split("http://")[1] elif proxy_string.startswith("https://"): proxy_string = proxy_string.split("https://")[1] elif "://" in proxy_string: if not proxy_string.startswith("socks4://") and not ( proxy_string.startswith("socks5://") ): proxy_string = proxy_string.split("://")[1] chunks = proxy_string.split(":") if len(chunks) == 2: if re.match(r"^\d+$", chunks[1]): if page_utils.is_valid_url("http://" + proxy_string): valid = True elif len(chunks) == 3: if re.match(r"^\d+$", chunks[2]): if page_utils.is_valid_url("http:" + ":".join(chunks[1:])): if chunks[0] == "http": valid = True elif chunks[0] == "https": valid = True elif chunks[0] == "socks4": valid = True elif chunks[0] == "socks5": valid = True else: proxy_string = val_ip.group() valid = True if not valid: __display_proxy_warning(proxy_string) proxy_string = None if keep_scheme: return (proxy_string, proxy_scheme) return proxy_string def __display_proxy_warning(proxy_string): message = ( '\nWARNING: Proxy String ["%s"] is NOT in the expected ' '"ip_address:port" or "server:port" format, ' "(OR the key does not exist in " "seleniumbase.config.proxy_list.PROXY_LIST)." % proxy_string ) if settings.RAISE_INVALID_PROXY_STRING_EXCEPTION: raise Exception(message) else: message += " *** DEFAULTING to NOT USING a Proxy Server! ***" warnings.simplefilter("always", Warning) # See Warnings warnings.warn(message, category=Warning, stacklevel=2) warnings.simplefilter("default", Warning) # Set Default ================================================ FILE: seleniumbase/core/recorder_helper.py ================================================ """Generating SeleniumBase Python code from the Recorder""" def generate_sbase_code(srt_actions): sb_actions = [] for action in srt_actions: if action[0] == "begin" or action[0] == "_url_": if "%" in action[2]: try: from urllib.parse import unquote action[2] = unquote(action[2], errors="strict") except Exception: pass if '"' not in action[2]: sb_actions.append('self.open("%s")' % action[2]) elif "'" not in action[2]: sb_actions.append("self.open('%s')" % action[2]) else: sb_actions.append( 'self.open("%s")' % action[2].replace('"', '\\"') ) elif action[0] == "f_url": if "%" in action[2]: try: from urllib.parse import unquote action[2] = unquote(action[2], errors="strict") except Exception: pass if '"' not in action[2]: sb_actions.append('self.open_if_not_url("%s")' % action[2]) elif "'" not in action[2]: sb_actions.append("self.open_if_not_url('%s')" % action[2]) else: sb_actions.append( 'self.open_if_not_url("%s")' % action[2].replace('"', '\\"') ) elif action[0] == "click": method = "click" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "dbclk": method = "double_click" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "js_cl": method = "js_click" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "js_ca": method = "js_click_all" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "jq_cl": method = "jquery_click" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "jq_ca": method = "jquery_click_all" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "r_clk": method = "context_click" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "canva": method = "click_with_offset" selector = action[1][0] p_x = action[1][1] p_y = action[1][2] if '"' not in selector: sb_actions.append( 'self.%s("%s", %s, %s)' % (method, selector, p_x, p_y) ) else: sb_actions.append( "self.%s('%s', %s, %s)" % (method, selector, p_x, p_y) ) elif ( action[0] == "input" or action[0] == "js_ty" or action[0] == "jq_ty" or action[0] == "pkeys" ): method = "type" if action[0] == "js_ty": method = "js_type" elif action[0] == "jq_ty": method = "jquery_type" elif action[0] == "pkeys": method = "press_keys" text = action[2].replace("\n", "\\n") if '"' not in action[1] and '"' not in text: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], text) ) elif '"' not in action[1] and '"' in text: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], text) ) elif '"' in action[1] and '"' not in text: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], text) ) elif '"' in action[1] and '"' in text: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], text) ) elif action[0] == "hover": method = "hover" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "e_mfa": method = "enter_mfa_code" text = action[2].replace("\n", "\\n") if '"' not in action[1] and '"' not in text: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], text) ) elif '"' not in action[1] and '"' in text: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], text) ) elif '"' in action[1] and '"' not in text: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], text) ) elif '"' in action[1] and '"' in text: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], text) ) elif action[0] == "h_clk": method = "hover_and_click" if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], action[2]) ) elif action[0] == "ddrop": method = "drag_and_drop" if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], action[2]) ) elif action[0] == "s_opt": method = "select_option_by_text" if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], action[2]) ) elif action[0] == "set_v": method = "set_value" if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], action[2]) ) elif action[0] == "cho_f": method = "choose_file" action[2] = action[2].replace("\\", "\\\\") if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], action[2]) ) elif action[0] == "sw_fr": method = "switch_to_frame" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "sw_dc": sb_actions.append("self.switch_to_default_content()") elif action[0] == "sw_pf": sb_actions.append("self.switch_to_parent_frame()") elif action[0] == "s_c_f": method = "set_content_to_frame" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "s_c_d": method = "set_content_to_default" nested = action[1] if nested: method = "set_content_to_parent" sb_actions.append("self.%s()" % method) else: sb_actions.append("self.%s()" % method) elif action[0] == "sleep": method = "sleep" sb_actions.append("self.%s(%s)" % (method, action[1])) elif action[0] == "wf_el": method = "wait_for_element" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "as_el": method = "assert_element" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "as_ep": method = "assert_element_present" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "asenv": method = "assert_element_not_visible" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "s_at_" or action[0] == "s_ats": method = "set_attribute" if action[0] == "s_ats": method = "set_attributes" if '"' not in action[1][0]: sb_actions.append( 'self.%s("%s", "%s", "%s")' % (method, action[1][0], action[1][1], action[1][2]) ) elif "'" not in action[1][0]: sb_actions.append( "self.%s('%s', \"%s\", \"%s\")" % (method, action[1][0], action[1][1], action[1][2]) ) else: sb_actions.append( 'self.%s("""%s""", "%s", "%s")' % (method, action[1][0], action[1][1], action[1][2]) ) elif action[0] == "acc_a": sb_actions.append("self.accept_alert()") elif action[0] == "dis_a": sb_actions.append("self.dismiss_alert()") elif action[0] == "hi_li": method = "highlight" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "as_lt": method = "assert_link_text" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "as_ti": method = "assert_title" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "as_tc": method = "assert_title_contains" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "a_url": method = "assert_url" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "a_u_c": method = "assert_url_contains" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "as_df": method = "assert_downloaded_file" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "do_fi": method = "download_file" file_url = action[1][0] dest = action[1][1] if not dest: sb_actions.append('self.%s("%s")' % (method, file_url)) else: sb_actions.append( 'self.%s("%s", "%s")' % (method, file_url, dest) ) elif action[0] == "as_at": method = "assert_attribute" if ('"' not in action[1][0]) and action[1][2]: sb_actions.append( 'self.%s("%s", "%s", "%s")' % (method, action[1][0], action[1][1], action[1][2]) ) elif ('"' not in action[1][0]) and not action[1][2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1][0], action[1][1]) ) elif ('"' in action[1][0]) and action[1][2]: sb_actions.append( 'self.%s(\'%s\', "%s", "%s")' % (method, action[1][0], action[1][1], action[1][2]) ) else: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1][0], action[1][1]) ) elif ( action[0] == "as_te" or action[0] == "as_et" or action[0] == "astnv" or action[0] == "aetnv" or action[0] == "da_te" or action[0] == "da_et" ): import unicodedata text_list = False try: action[1][0] = unicodedata.normalize("NFKC", action[1][0]) action[1][0] = action[1][0].replace("\n", "\\n") action[1][0] = action[1][0].replace("\u00B6", "") except Exception: text_list = True method = "assert_text" if action[0] == "as_et": method = "assert_exact_text" elif action[0] == "astnv": method = "assert_text_not_visible" elif action[0] == "aetnv": method = "assert_exact_text_not_visible" elif action[0] == "da_te": method = "deferred_assert_text" elif action[0] == "da_et": method = "deferred_assert_exact_text" if action[1][1] != "html": if text_list and '"' not in action[1][1]: sb_actions.append( 'self.%s(%s, "%s")' % (method, action[1][0], action[1][1]) ) elif text_list and "'" not in action[1][1]: sb_actions.append( "self.%s(%s, '%s')" % (method, action[1][0], action[1][1]) ) elif '"' not in action[1][0] and '"' not in action[1][1]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1][0], action[1][1]) ) elif '"' not in action[1][0] and '"' in action[1][1]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1][0], action[1][1]) ) elif '"' in action[1] and '"' not in action[1][1]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1][0], action[1][1]) ) elif '"' in action[1] and '"' in action[1][1]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1][0], action[1][1]) ) else: if text_list: sb_actions.append( 'self.%s(%s)' % (method, action[1][0]) ) elif '"' not in action[1][0]: sb_actions.append( 'self.%s("%s")' % (method, action[1][0]) ) else: sb_actions.append( "self.%s('%s')" % (method, action[1][0]) ) elif action[0] == "asnet": method = "assert_non_empty_text" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "da_el": method = "deferred_assert_element" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "da_ep": method = "deferred_assert_element_present" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "danet": method = "deferred_assert_non_empty_text" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) elif "'" not in action[1]: sb_actions.append("self.%s('%s')" % (method, action[1])) else: sb_actions.append( 'self.%s("""%s""")' % (method, action[1]) ) elif action[0] == "s_scr": method = "save_screenshot" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) elif action[0] == "ss_tf": method = "save_screenshot" action[2] = action[1][1] action[1] = action[1][0] if '"' not in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s("%s", "%s")' % (method, action[1], action[2]) ) elif '"' not in action[1] and '"' in action[2]: sb_actions.append( 'self.%s("%s", \'%s\')' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' not in action[2]: sb_actions.append( 'self.%s(\'%s\', "%s")' % (method, action[1], action[2]) ) elif '"' in action[1] and '"' in action[2]: sb_actions.append( "self.%s('%s', '%s')" % (method, action[1], action[2]) ) elif action[0] == "ss_tl": method = "save_screenshot_to_logs" sb_actions.append("self.%s()" % method) elif action[0] == "pdftl": method = "save_as_pdf_to_logs" sb_actions.append("self.%s()" % method) elif action[0] == "spstl": method = "save_page_source_to_logs" sb_actions.append("self.%s()" % method) elif action[0] == "sh_fc": method = "show_file_choosers" sb_actions.append("self.%s()" % method) elif action[0] == "pr_da": sb_actions.append("self.process_deferred_asserts()") elif action[0] == "a_d_m": sb_actions.append("self.activate_demo_mode()") elif action[0] == "d_d_m": sb_actions.append("self.deactivate_demo_mode()") elif action[0] == "c_l_s": sb_actions.append("self.clear_local_storage()") elif action[0] == "c_s_s": sb_actions.append("self.clear_session_storage()") elif action[0] == "d_a_c": sb_actions.append("self.delete_all_cookies()") elif action[0] == "go_bk": sb_actions.append("self.go_back()") elif action[0] == "go_fw": sb_actions.append("self.go_forward()") elif action[0] == "c_box": method = "check_if_unchecked" if action[2] == "no": method = "uncheck_if_checked" if '"' not in action[1]: sb_actions.append('self.%s("%s")' % (method, action[1])) else: sb_actions.append("self.%s('%s')" % (method, action[1])) return sb_actions ================================================ FILE: seleniumbase/core/report_helper.py ================================================ import os import shutil import sys import time from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core.style_sheet import get_report_style LATEST_REPORT_DIR = settings.LATEST_REPORT_DIR ARCHIVE_DIR = settings.REPORT_ARCHIVE_DIR HTML_REPORT = settings.HTML_REPORT RESULTS_TABLE = settings.RESULTS_TABLE def get_timestamp(): return str(int(time.time() * 1000)) def process_successes(test, test_count, duration): return '"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"' % ( test_count, "Passed!", "*", "*", "*", test.browser, get_timestamp()[:-3], duration, test.id(), "*", ) def save_test_failure_data(test, name, folder=None): """ Saves failure data to the current directory, or to a subfolder if provided. If {name} does not end in ".txt", it will get added to it. If the folder provided doesn't exist, it will get created. """ if not name.endswith(".txt"): name = name + ".txt" if folder: abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, folder) if not os.path.exists(file_path): os.makedirs(file_path) failure_data_file_path = os.path.join(file_path, name) else: failure_data_file_path = name failure_data_file = open( failure_data_file_path, mode="w+", encoding="utf-8" ) data_to_save = [] if not hasattr(sb_config, "_report_test_id"): exc_message = "(Unknown Exception)" traceback_message = "" if hasattr(sb_config, "_report_traceback"): traceback_message = str(sb_config._report_traceback) if hasattr(sb_config, "_report_exception"): if type(sb_config._report_exception) is tuple: exc_message = str(sb_config._report_exception[1].message) else: exc_message = str(sb_config._report_exception) data_to_save.append(test.id()) data_to_save.append( "----------------------------------------------------------------" ) data_to_save.append("Last Page: %s" % test._last_page_url) data_to_save.append(" Browser: %s" % test.browser) data_to_save.append("Timestamp: %s" % get_timestamp()[:-3]) data_to_save.append( "----------------------------------------------------------------" ) data_to_save.append("Traceback: %s" % traceback_message) data_to_save.append("Exception: %s" % exc_message) failure_data_file.writelines("\r\n".join(data_to_save)) failure_data_file.close() return data_to_save.append(sb_config._report_test_id) data_to_save.append( "--------------------------------------------------------------------" ) data_to_save.append("Last Page: %s" % sb_config._report_fail_page) data_to_save.append(" Duration: %s" % sb_config._report_duration) data_to_save.append(" Browser: %s" % sb_config._report_browser) data_to_save.append(" Driver: %s" % sb_config._report_driver) data_to_save.append("Timestamp: %s" % sb_config._report_timestamp) data_to_save.append(" Date: %s" % sb_config._report_date) data_to_save.append(" Time: %s" % sb_config._report_time) data_to_save.append( "--------------------------------------------------------------------" ) data_to_save.append("Traceback: %s" % sb_config._report_traceback) data_to_save.append("Exception: %s" % sb_config._report_exception) failure_data_file.writelines("\r\n".join(data_to_save)) failure_data_file.close() def process_failures(test, test_count, duration): bad_page_image = "failure_%s.png" % test_count bad_page_data = "failure_%s.txt" % test_count screenshot_path = os.path.join(LATEST_REPORT_DIR, bad_page_image) if getattr(test, "_last_page_screenshot", None): with open(screenshot_path, mode="wb") as file: file.write(test._last_page_screenshot) save_test_failure_data(test, bad_page_data, folder=LATEST_REPORT_DIR) exc_message = None if hasattr(test, "_outcome") and getattr(test._outcome, "errors", None): try: exc_message = test._outcome.errors[-1][1][1] except Exception: exc_message = "(Unknown Exception)" else: try: exc_message = sys.last_value except Exception: exc_message = "(Unknown Exception)" if not hasattr(test, "_last_page_url"): test._last_page_url = "about:blank" return '"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"' % ( test_count, "FAILED!", bad_page_data, bad_page_image, test._last_page_url, test.browser, get_timestamp()[:-3], duration, test.id(), exc_message, ) def clear_out_old_report_logs(archive_past_runs=True, get_log_folder=False): abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, LATEST_REPORT_DIR) if not os.path.exists(file_path): try: os.makedirs(file_path) except Exception: pass # Should only be reachable during multi-threaded runs if archive_past_runs: archive_timestamp = int(time.time()) archive_dir_root = os.path.join(file_path, "..", ARCHIVE_DIR) if not os.path.exists(archive_dir_root): os.makedirs(archive_dir_root) archive_dir = os.path.join( archive_dir_root, "report_%s" % archive_timestamp ) shutil.move(file_path, archive_dir) os.makedirs(file_path) if get_log_folder: return archive_dir else: # Just delete bad pages to make room for the latest run. filelist = [ f for f in os.listdir(os.path.join(".", LATEST_REPORT_DIR)) if f.startswith("failure_") or (f == HTML_REPORT) or (f.startswith("automation_failure")) or (f == RESULTS_TABLE) ] for f in filelist: os.remove(os.path.join(file_path, f)) def add_bad_page_log_file(page_results_list): abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, LATEST_REPORT_DIR) log_file = os.path.join(file_path, RESULTS_TABLE) f = open(log_file, mode="w") h_p1 = '"Num","Result","Stacktrace","Screenshot",' h_p2 = '"URL","Browser","Epoch Time","Duration",' h_p3 = '"Test Case Address","Additional Info"\n' page_header = h_p1 + h_p2 + h_p3 f.write(page_header) for line in page_results_list: f.write("%s\n" % line) f.close() def archive_new_report_logs(): log_string = clear_out_old_report_logs(get_log_folder=True) log_folder = log_string.split("/")[-1] abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, ARCHIVE_DIR) report_log_path = os.path.join(file_path, log_folder) return report_log_path def add_results_page(html): abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, LATEST_REPORT_DIR) results_file_name = HTML_REPORT results_file = os.path.join(file_path, results_file_name) f = open(results_file, mode="w") f.write(html) f.close() return results_file def build_report( report_log_path, page_results_list, successes, failures, browser_type, show_report, ): web_log_path = "file://%s" % report_log_path successes_count = len(successes) failures_count = len(failures) total_test_count = successes_count + failures_count tf_color = "#11BB11" if failures_count > 0: tf_color = "#EE3A3A" summary_table = """<div><table><thead><tr> <th>TESTING SUMMARY</th> <th>        </th> </tr></thead><tbody> <tr style="color:#00BB00"><td>TESTS PASSING: <td>%s</tr> <tr style="color:%s" ><td>TESTS FAILING: <td>%s</tr> <tr style="color:#4D4DDD"><td>TOTAL TESTS: <td>%s</tr> </tbody></table>""" % ( successes_count, tf_color, failures_count, total_test_count, ) summary_table = ( """<h1 id="ContextHeader" class="sectionHeader" title=""> %s</h1>""" % summary_table ) log_link_shown = os.path.join( "..", "%s%s" % (ARCHIVE_DIR, web_log_path.split(ARCHIVE_DIR)[1]) ) csv_link = os.path.join(web_log_path, RESULTS_TABLE) csv_link_shown = "%s" % RESULTS_TABLE log_table = """<p><p><p><p><h2><table><tbody> <tr><td>LOG FILES LINK:  <td><a href="%s">%s</a></tr> <tr><td>RESULTS TABLE:  <td><a href="%s">%s</a></tr> </tbody></table></h2><p><p><p><p>""" % ( web_log_path, log_link_shown, csv_link, csv_link_shown, ) failure_table = "<h2><table><tbody></div>" any_screenshots = False for line in page_results_list: line = line.split(",") if line[1] == '"FAILED!"': if not any_screenshots: any_screenshots = True failure_table += """<thead><tr> <th>STACKTRACE  </th> <th>SCREENSHOT  </th> <th>LOCATION OF FAILURE</th> </tr></thead>""" display_url = line[4] actual_url = line[4] if len(display_url) < 7: display_url = sb_config._report_fail_page actual_url = sb_config._report_fail_page if len(display_url) > 60: display_url = display_url[0:58] + "..." line = ( '<a href="%s">%s</a>' % ("file://" + report_log_path + "/" + line[2], line[2]) + """    """ + '<td><a href="%s">%s</a>' % ("file://" + report_log_path + "/" + line[3], line[3]) + """    """ + '<td><a href="%s">%s</a>' % (actual_url, display_url) ) line = line.replace('"', "") failure_table += "<tr><td>%s</tr>\n" % line failure_table += "</tbody></table></h2>" failing_list = "" if failures: failing_list = "<h2><table><tbody>" failing_list += """<thead><tr><th>LIST OF FAILING TESTS          </th></tr></thead>""" for failure in failures: failing_list += '<tr style="color:#EE3A3A"><td>%s</tr>\n' % failure failing_list += "</tbody></table></h2>" passing_list = "" if successes: passing_list = "<h2><table><tbody>" passing_list += """<thead><tr><th>LIST OF PASSING TESTS          </th></tr></thead>""" for success in successes: passing_list += '<tr style="color:#00BB00"><td>%s</tr>\n' % success passing_list += "</tbody></table></h2>" table_view = "%s%s%s%s%s" % ( summary_table, log_table, failure_table, failing_list, passing_list, ) report_html = "<html><head>%s</head><body>%s</body></html>" % ( get_report_style(), table_view, ) results_file = add_results_page(report_html) archived_results_file = report_log_path + "/" + HTML_REPORT shutil.copyfile(results_file, archived_results_file) print("\n* The latest html report page is located at:\n" + results_file) print( "\n* Files saved for this report are located at:\n" + report_log_path ) print("") if show_report: from seleniumbase import get_driver driver = get_driver(browser_type, headless=False) driver.get("file://%s" % archived_results_file) print("\n*** Close the html report window to continue. ***") while len(driver.window_handles): time.sleep(0.1) driver.quit() ================================================ FILE: seleniumbase/core/s3_manager.py ================================================ """Methods for uploading/managing files on Amazon S3.""" already_uploaded_files = [] class S3LoggingBucket(object): """A class for uploading log files from tests to Amazon S3. Those files can then be shared easily.""" from seleniumbase.config import settings def __init__( self, log_bucket=settings.S3_LOG_BUCKET, bucket_url=settings.S3_BUCKET_URL, selenium_access_key=settings.S3_SELENIUM_ACCESS_KEY, selenium_secret_key=settings.S3_SELENIUM_SECRET_KEY, ): import fasteners from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) with pip_find_lock: try: import boto3 except Exception: shared_utils.pip_install("boto3") import boto3 self.conn = boto3.Session( aws_access_key_id=selenium_access_key, aws_secret_access_key=selenium_secret_key, ) self.bucket = log_bucket self.bucket_url = bucket_url def get_key(self, file_name): """Create a new S3 connection instance with the given name.""" return self.conn.resource("s3").Object(self.bucket, file_name) def get_bucket(self): """Return the bucket being used.""" return self.bucket def upload_file(self, file_name, file_path): """Upload a given file from the file_path to the bucket with the new name/path file_name.""" upload_key = self.get_key(file_name) content_type = "text/plain" if file_name.endswith(".html"): content_type = "text/html" elif file_name.endswith(".jpg"): content_type = "image/jpeg" elif file_name.endswith(".png"): content_type = "image/png" upload_key.Bucket().upload_file( file_path, file_name, ExtraArgs={"ACL": "public-read", "ContentType": content_type}, ) def upload_index_file( self, test_address, timestamp, data_path, save_data_to_logs ): """Create an index.html file with links to all the log files that were just uploaded.""" import os global already_uploaded_files already_uploaded_files = list(set(already_uploaded_files)) already_uploaded_files.sort() file_name = "%s/%s/index.html" % (test_address, timestamp) index = self.get_key(file_name) index_str = [] for completed_file in already_uploaded_files: index_str.append( "<a href='" + self.bucket_url + "" "%s'>%s</a>" % (completed_file, completed_file) ) index_page = str("<br>".join(index_str)) save_data_to_logs(index_page, "index.html") file_path = os.path.join(data_path, "index.html") index.Bucket().upload_file( file_path, file_name, ExtraArgs={"ACL": "public-read", "ContentType": "text/html"}, ) return "%s%s" % (self.bucket_url, file_name) def save_uploaded_file_names(self, files): """Keep a record of all file names that have been uploaded. Upload log files related to each test after its execution. Once done, use already_uploaded_files to create an index file.""" global already_uploaded_files # noqa already_uploaded_files.extend(files) ================================================ FILE: seleniumbase/core/sb_cdp.py ================================================ """Add CDP methods to extend the driver""" import asyncio import fasteners import mycdp import os import random import re import sys import time from contextlib import suppress from filelock import FileLock from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.fixtures import constants from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_utils from seleniumbase.fixtures import shared_utils from seleniumbase.undetected.cdp_driver import cdp_util from seleniumbase.undetected.cdp_driver import tab as cdp_tab class CDPMethods(): def __init__(self, loop, page, driver): self.loop = loop self.page = page self.driver = driver def _swap_driver(self, driver): self.driver = driver self.page = driver.cdp.page self.loop = driver.cdp.loop def __slow_mode_pause_if_set(self): if ( (hasattr(sb_config, "demo_mode") and sb_config.demo_mode) or "--demo" in sys.argv ): time.sleep(0.48) elif ( (hasattr(sb_config, "slow_mode") and sb_config.slow_mode) or "--slow" in sys.argv ): time.sleep(0.24) def __add_light_pause(self): time.sleep(0.007) def __convert_to_css_if_xpath(self, selector): if page_utils.is_xpath_selector(selector): with suppress(Exception): css = js_utils.convert_to_css_selector(selector, "xpath") if css: return css return selector def __add_sync_methods(self, element): if not element: return element element.clear_input = lambda: self.__clear_input(element) element.click = lambda: self.__click(element) element.flash = lambda *args, **kwargs: self.__flash( element, *args, **kwargs ) element.focus = lambda: self.__focus(element) element.gui_click = ( lambda *args, **kwargs: self.__gui_click(element, *args, **kwargs) ) element.highlight_overlay = lambda: self.__highlight_overlay(element) element.mouse_click = lambda: self.__mouse_click(element) element.click_with_offset = ( lambda *args, **kwargs: self.__mouse_click_with_offset_async( element, *args, **kwargs ) ) element.mouse_drag = ( lambda destination: self.__mouse_drag(element, destination) ) element.mouse_move = lambda: self.__mouse_move(element) element.press_keys = lambda text: self.__press_keys(element, text) element.query_selector = ( lambda selector: self.__query_selector(element, selector) ) element.querySelector = element.query_selector element.query_selector_all = ( lambda selector: self.__query_selector_all(element, selector) ) element.querySelectorAll = element.query_selector_all element.remove_from_dom = lambda: self.__remove_from_dom(element) element.save_screenshot = ( lambda *args, **kwargs: self.__save_screenshot( element, *args, **kwargs) ) element.save_to_dom = lambda: self.__save_to_dom(element) element.scroll_into_view = lambda: self.__scroll_into_view(element) element.select_option = lambda: self.__select_option(element) element.send_file = ( lambda *file_paths: self.__send_file(element, *file_paths) ) element.send_keys = lambda text: self.__send_keys(element, text) element.set_text = lambda value: self.__set_text(element, value) element.set_value = lambda value: self.__set_value(element, value) element.type = lambda text: self.__type(element, text) element.get_position = lambda: self.__get_position(element) element.get_html = lambda: self.__get_html(element) element.get_js_attributes = lambda: self.__get_js_attributes(element) element.get_attribute = ( lambda attribute: self.__get_attribute(element, attribute) ) # element.get_parent() should come last element.get_parent = lambda: self.__get_parent(element) return element def get(self, url, **kwargs): url = shared_utils.fix_url_as_needed(url) driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base load_timeout = 60.0 wait_timeout = 30.0 if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy: load_timeout = 90.0 wait_timeout = 45.0 try: task = self.page.get(url, **kwargs) self.loop.run_until_complete( asyncio.wait_for(task, timeout=load_timeout) ) except asyncio.TimeoutError: print("Timeout loading %s" % url) url_protocol = url.split(":")[0] safe_url = True if url_protocol not in ["about", "data", "chrome"]: safe_url = False if not safe_url: time.sleep(constants.UC.CDP_MODE_OPEN_WAIT) if shared_utils.is_windows(): time.sleep(constants.UC.EXTRA_WINDOWS_WAIT) else: time.sleep(0.012) self.__slow_mode_pause_if_set() try: self.loop.run_until_complete( asyncio.wait_for(self.page.wait(), timeout=wait_timeout) ) except asyncio.TimeoutError: pass except Exception: pass def open(self, url, **kwargs): self.get(url, **kwargs) def reload(self, ignore_cache=True, script_to_evaluate_on_load=None): self.loop.run_until_complete( self.page.reload( ignore_cache=ignore_cache, script_to_evaluate_on_load=script_to_evaluate_on_load, ) ) def refresh(self, *args, **kwargs): self.reload(*args, **kwargs) def get_event_loop(self): return self.loop def get_rd_host(self): """Returns the remote-debugging host (likely 127.0.0.1)""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return driver.config.host def get_rd_port(self): """Returns the remote-debugging port (commonly 9222)""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return driver.config.port def get_rd_url(self): """Returns the remote-debugging URL, which is used for allowing the Playwright integration to launch stealthy, and also applies nest-asyncio for nested event loops so that SeleniumBase methods can be called from Playwright without encountering event loop error messages such as: Cannot run the event loop while another loop is running. Also sets an environment variable to hide this warning: Deprecation: "url.parse() behavior is not standardized". (github.com/microsoft/playwright-python/issues/3016)""" import nest_asyncio nest_asyncio.apply() os.environ["NODE_NO_WARNINGS"] = "1" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base host = driver.config.host port = driver.config.port return f"http://{host}:{port}" def get_endpoint_url(self): """Same as get_rd_url(), which returns the remote-debugging URL.""" return self.get_rd_url() def get_port(self): """Same as get_rd_port(), which returns the remote-debugging port.""" return self.get_rd_port() def get_websocket_url(self): """Returns the websocket URL of the active tab. The websocket URL starts with `ws://`.""" return self.get_active_tab().websocket_url def add_handler(self, event, handler): self.page.add_handler(event, handler) def find_element(self, selector, best_match=False, timeout=None): """Similar to select(), but also finds elements by text content. When using text-based searches, if best_match=False, then will find the first element with the text. If best_match=True, then if multiple elements have that text, then will use the element with the closest text-length to the text being searched for.""" if not timeout: timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) early_failure = False if (":contains(") in selector: selector, _ = page_utils.recalculate_selector( selector, by="css selector", xp_ok=True ) failure = False try: if early_failure: raise Exception("Failed!") if ( "contains(" not in selector and not page_utils.is_xpath_selector(selector) and not re.findall(r"\.\s", selector) and not re.findall(r"\.$", selector) and not re.findall(r"#\s", selector) and not re.findall(r"#$", selector) and ( selector in ["html", "body"] or "[" in selector or re.findall(r"\.\S", selector) or re.findall(r"#\S", selector) or " > " in selector ) ): element = self.loop.run_until_complete( self.page.select(selector, timeout=timeout) ) else: element = self.loop.run_until_complete( self.page.find( selector, best_match=best_match, timeout=timeout ) ) except Exception: failure = True plural = "s" if timeout == 1: plural = "" message = "\n Element {%s} was not found after %s second%s!" % ( selector, timeout, plural, ) if failure: raise Exception(message) element = self.__add_sync_methods(element) self.__slow_mode_pause_if_set() return element def find_element_by_text(self, text, tag_name=None, timeout=None): """Returns an element by matching text. Optionally, provide a tag_name to narrow down the search to an element with the given tag. (Eg: a, button, div, script, span)""" if not timeout: timeout = settings.SMALL_TIMEOUT if tag_name: try: return self.find_element( '%s:contains("%s")' % (tag_name, text), timeout=timeout ) except Exception: pass # The exception will be raised later else: self.__add_light_pause() self.assert_text(text, timeout=timeout) elements = self.loop.run_until_complete( self.page.find_elements_by_text(text=text) ) for element in elements: if element: element = self.__add_sync_methods(element) return self.__add_sync_methods(element) plural = "s" if timeout == 1: plural = "" raise Exception( "Text {%s} with tag {%s} was not found after %s second%s!" % (text, tag_name, timeout, plural) ) def find_all(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) elements = self.loop.run_until_complete( self.page.find_all(selector, timeout=timeout) ) updated_elements = [] for element in elements: element = self.__add_sync_methods(element) updated_elements.append(element) return updated_elements def find_elements_by_text(self, text, tag_name=None): """Returns a list of elements by matching text. Optionally, provide a tag_name to narrow down the search to only elements with the given tag. (Eg: a, button, div, script, span)""" self.__add_light_pause() elements = self.loop.run_until_complete( self.page.find_elements_by_text(text=text) ) updated_elements = [] if tag_name: tag_name = tag_name.lower().strip() for element in elements: if element and not tag_name: element = self.__add_sync_methods(element) if element not in updated_elements: updated_elements.append(element) elif ( element and tag_name in element.tag_name.lower() and text.strip() in element.text ): element = self.__add_sync_methods(element) if element not in updated_elements: updated_elements.append(element) elif ( element and element.parent and tag_name in element.parent.tag_name.lower() and text.strip() in element.parent.text ): element = self.__add_sync_methods(element.parent) if element not in updated_elements: updated_elements.append(element) elif ( element and element.parent and element.parent.parent and tag_name in element.parent.parent.tag_name.lower() and text.strip() in element.parent.parent.text ): element = self.__add_sync_methods(element.parent.parent) if element not in updated_elements: updated_elements.append(element) return updated_elements def select(self, selector, timeout=None): """Similar to find_element().""" if not timeout: timeout = settings.SMALL_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) if (":contains(" in selector): return self.find_element(selector, timeout=timeout) failure = False try: element = self.loop.run_until_complete( self.page.select(selector, timeout=timeout) ) except Exception: failure = True plural = "s" if timeout == 1: plural = "" msg = "\n Element {%s} was not found after %s second%s!" message = msg % (selector, timeout, plural) if failure: raise Exception(message) element = self.__add_sync_methods(element) self.__slow_mode_pause_if_set() return element def select_all(self, selector, timeout=None): if not timeout: timeout = settings.MINI_TIMEOUT self.__add_light_pause() selector = self.__convert_to_css_if_xpath(selector) if not self.is_element_present(selector): self.sleep(1) timeout = timeout - 1 if timeout < 1: timeout = 1 try: self.select(selector, timeout=timeout) except Exception: return [] elements = self.loop.run_until_complete( self.page.select_all(selector, timeout=0.1) ) updated_elements = [] for element in elements: element = self.__add_sync_methods(element) updated_elements.append(element) return updated_elements def find_elements(self, selector, timeout=None): if not timeout: timeout = settings.MINI_TIMEOUT return self.select_all(selector, timeout=timeout) def find_visible_elements(self, selector, timeout=None): if not timeout: timeout = settings.MINI_TIMEOUT visible_elements = [] elements = self.select_all(selector, timeout=timeout) for element in elements: with suppress(Exception): position = element.get_position() if (position.width != 0 or position.height != 0): visible_elements.append(element) return visible_elements def click_nth_element(self, selector, number): elements = self.select_all(selector) if len(elements) < number: raise Exception( "Not enough matching {%s} elements to " "click number %s!" % (selector, number) ) number = number - 1 if number < 0: number = 0 element = elements[number] element.scroll_into_view() element.click() def click_nth_visible_element(self, selector, number): """Finds all matching page elements and clicks the nth visible one. Example: self.click_nth_visible_element('[type="checkbox"]', 5) (Clicks the 5th visible checkbox on the page.)""" elements = self.find_visible_elements(selector) if len(elements) < number: raise Exception( "Not enough matching {%s} elements to " "click number %s!" % (selector, number) ) number = number - 1 if number < 0: number = 0 element = elements[number] element.scroll_into_view() element.click() def click_link(self, link_text): self.find_elements_by_text(link_text, "a")[0].click() def go_back(self): self.loop.run_until_complete(self.page.back()) def go_forward(self): self.loop.run_until_complete(self.page.forward()) def get_navigation_history(self): return self.loop.run_until_complete(self.page.get_navigation_history()) def __clear_input(self, element): return ( self.loop.run_until_complete(element.clear_input_async()) ) def __click(self, element): result = ( self.loop.run_until_complete(element.click_async()) ) self.loop.run_until_complete(self.page.wait(0.2)) return result def __flash(self, element, *args, **kwargs): element.scroll_into_view() if len(args) < 3 and "x_offset" not in kwargs: x_offset = self.__get_x_scroll_offset() kwargs["x_offset"] = x_offset if len(args) < 3 and "y_offset" not in kwargs: y_offset = self.__get_y_scroll_offset() kwargs["y_offset"] = y_offset return ( self.loop.run_until_complete( element.flash_async(*args, **kwargs) ) ) def __focus(self, element): return ( self.loop.run_until_complete(element.focus_async()) ) def __gui_click(self, element, timeframe=None): element.scroll_into_view() self.__add_light_pause() position = element.get_position() x = position.x y = position.y e_width = position.width e_height = position.height # Relative to window element_rect = {"height": e_height, "width": e_width, "x": x, "y": y} window_rect = self.get_window_rect() w_bottom_y = window_rect["y"] + window_rect["height"] viewport_height = window_rect["innerHeight"] x = window_rect["x"] + element_rect["x"] y = w_bottom_y - viewport_height + element_rect["y"] y_scroll_offset = window_rect["pageYOffset"] y = y - y_scroll_offset x = x + window_rect["scrollX"] y = y + window_rect["scrollY"] # Relative to screen element_rect = {"height": e_height, "width": e_width, "x": x, "y": y} e_width = element_rect["width"] e_height = element_rect["height"] e_x = element_rect["x"] e_y = element_rect["y"] x, y = ((e_x + e_width / 2.0) + 0.5), ((e_y + e_height / 2.0) + 0.5) if not timeframe or not isinstance(timeframe, (int, float)): timeframe = 0.25 if timeframe > 3: timeframe = 3 self.gui_click_x_y(x, y, timeframe=timeframe) return self.loop.run_until_complete(self.page.wait(0.2)) def __highlight_overlay(self, element): return ( self.loop.run_until_complete(element.highlight_overlay_async()) ) def __mouse_click(self, element): result = ( self.loop.run_until_complete(element.mouse_click_async()) ) self.loop.run_until_complete(self.page.wait(0.2)) return result def __mouse_click_with_offset_async(self, element, *args, **kwargs): result = ( self.loop.run_until_complete( element.mouse_click_with_offset_async(*args, **kwargs) ) ) self.loop.run_until_complete(self.page.wait(0.2)) return result def __mouse_drag(self, element, destination): return ( self.loop.run_until_complete(element.mouse_drag_async(destination)) ) def __mouse_move(self, element): return ( self.loop.run_until_complete(element.mouse_move_async()) ) def __press_keys(self, element, text): element.scroll_into_view() submit = False if text.endswith("\n") or text.endswith("\r"): submit = True text = text[:-1] for key in text: element.send_keys(key) time.sleep(float(0.042 + (random.random() / 110.0))) if submit: element.send_keys("\r\n") time.sleep(0.044) self.__slow_mode_pause_if_set() return self.loop.run_until_complete(self.page.sleep(0.025)) def __query_selector(self, element, selector): selector = self.__convert_to_css_if_xpath(selector) element2 = self.loop.run_until_complete( element.query_selector_async(selector) ) element2 = self.__add_sync_methods(element2) return element2 def __query_selector_all(self, element, selector): selector = self.__convert_to_css_if_xpath(selector) elements = self.loop.run_until_complete( element.query_selector_all_async(selector) ) updated_elements = [] for element in elements: element = self.__add_sync_methods(element) updated_elements.append(element) self.__slow_mode_pause_if_set() return updated_elements def __remove_from_dom(self, element): return ( self.loop.run_until_complete(element.remove_from_dom_async()) ) def __save_screenshot(self, element, *args, **kwargs): return ( self.loop.run_until_complete( element.save_screenshot_async(*args, **kwargs) ) ) def __save_to_dom(self, element): return ( self.loop.run_until_complete(element.save_to_dom_async()) ) def __scroll_into_view(self, element): self.loop.run_until_complete(element.scroll_into_view_async()) self.__add_light_pause() return None def __select_option(self, element): return ( self.loop.run_until_complete(element.select_option_async()) ) def __send_file(self, element, *file_paths): return ( self.loop.run_until_complete(element.send_file_async(*file_paths)) ) def __send_keys(self, element, text): return ( self.loop.run_until_complete(element.send_keys_async(text)) ) def __set_text(self, element, value): return ( self.loop.run_until_complete(element.set_text_async(value)) ) def __set_value(self, element, value): return ( self.loop.run_until_complete(element.set_value_async(value)) ) def __type(self, element, text): with suppress(Exception): element.clear_input() element.send_keys(text) def __get_position(self, element): return ( self.loop.run_until_complete(element.get_position_async()) ) def __get_html(self, element): return ( self.loop.run_until_complete(element.get_html_async()) ) def __get_js_attributes(self, element): return ( self.loop.run_until_complete(element.get_js_attributes_async()) ) def __get_attribute(self, element, attribute): try: return element.get_js_attributes()[attribute] except Exception: if not attribute: raise try: attribute_str = element.get_js_attributes() locate = ' %s="' % attribute if locate in attribute_str.outerHTML: outer_html = attribute_str.outerHTML attr_start = outer_html.find(locate) + len(locate) attr_end = outer_html.find('"', attr_start) value = outer_html[attr_start:attr_end] return value except Exception: pass return None def __get_parent(self, element): return self.__add_sync_methods(element.parent) def __get_x_scroll_offset(self): x_scroll_offset = self.loop.run_until_complete( self.page.evaluate("window.pageXOffset") ) return x_scroll_offset or 0 def __get_y_scroll_offset(self): y_scroll_offset = self.loop.run_until_complete( self.page.evaluate("window.pageYOffset") ) return y_scroll_offset or 0 def tile_windows(self, windows=None, max_columns=0): """Tile windows and return the grid of tiled windows.""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( driver.tile_windows(windows, max_columns) ) def grant_permissions(self, permissions, origin=None): """Grant specific permissions to the current window. Applies to all origins if no origin is specified.""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( driver.grant_permissions(permissions, origin) ) def grant_all_permissions(self): """Grant all permissions to the current window for all origins.""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete(driver.grant_all_permissions()) def reset_permissions(self): """Reset permissions for all origins on the current window.""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete(driver.reset_permissions()) def get_all_cookies(self, *args, **kwargs): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( driver.cookies.get_all(*args, **kwargs) ) def set_all_cookies(self, *args, **kwargs): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( driver.cookies.set_all(*args, **kwargs) ) def save_cookies(self, *args, **kwargs): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( driver.cookies.save(*args, **kwargs) ) def load_cookies(self, *args, **kwargs): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete( driver.cookies.load(*args, **kwargs) ) def clear_cookies(self): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return self.loop.run_until_complete(driver.cookies.clear()) def sleep(self, seconds): time.sleep(seconds) def bring_active_window_to_front(self): self.loop.run_until_complete(self.page.bring_to_front()) self.__add_light_pause() def get_active_element(self): return self.loop.run_until_complete( self.page.js_dumps("document.activeElement") ) def get_active_element_css(self): from seleniumbase.js_code import active_css_js js_code = active_css_js.get_active_element_css js_code = js_code.replace("return getBestSelector", "getBestSelector") return self.loop.run_until_complete(self.page.evaluate(js_code)) def click(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.find_element(selector, timeout=timeout) element.scroll_into_view() tag_name = element.tag_name if tag_name: tag_name = tag_name.lower().strip() if ( tag_name in [ "a", "button", "canvas", "div", "input", "li", "span", "label" ] and "contains(" not in selector ): try: element.mouse_click() # Simulated click (NOT PyAutoGUI) except Exception: element.click() # Standard CDP click else: element.click() # Standard CDP click self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def click_active_element(self): self.loop.run_until_complete( self.page.evaluate("document.activeElement.click()") ) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def click_if_visible(self, selector, timeout=0): if self.is_element_visible(selector): with suppress(Exception): self.click(selector, timeout=1) elif timeout == 0: return else: with suppress(Exception): self.find_element(selector, timeout=timeout) if self.is_element_visible(selector): self.click(selector, timeout=1) def click_visible_elements(self, selector, limit=0): """Finds all matching page elements and clicks visible ones in order. If a click reloads or opens a new page, the clicking will stop. If no matching elements appear, an Exception will be raised. If "limit" is set and > 0, will only click that many elements. Also clicks elements that become visible from previous clicks. Works best for actions such as clicking all checkboxes on a page. Example: self.click_visible_elements('input[type="checkbox"]')""" elements = self.select_all(selector) click_count = 0 for element in elements: if limit and limit > 0 and click_count >= limit: return try: width = 0 height = 0 try: position = element.get_position() width = position.width height = position.height except Exception: continue if (width != 0 or height != 0): element.scroll_into_view() element.click() click_count += 1 time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) except Exception: break def mouse_click(self, selector, timeout=None): """(Attempt simulating a mouse click)""" if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.find_element(selector, timeout=timeout) element.scroll_into_view() element.mouse_click() self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def nested_click(self, parent_selector, selector): """ Find parent element and click on child element inside it. (This can be used to click on elements inside an iframe.) """ element = self.find_element(parent_selector) element.query_selector(selector).mouse_click() self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def get_nested_element(self, parent_selector, selector): """(Can be used to find an element inside an iframe)""" element = self.find_element(parent_selector) return element.query_selector(selector) def select_option_by_text(self, dropdown_selector, option): element = self.find_element(dropdown_selector) element.scroll_into_view() options = element.query_selector_all("option") for found_option in options: if found_option.text.strip() == option.strip(): found_option.select_option() return raise Exception( "Unable to find text option {%s} in dropdown {%s}!" % (dropdown_selector, option) ) def select_option_by_index(self, dropdown_selector, option): element = self.find_element(dropdown_selector) element.scroll_into_view() options = element.query_selector_all("option") count = 0 for found_option in options: if count == int(option): found_option.select_option() return count += 1 raise Exception( "Unable to find index option {%s} in dropdown {%s}!" % (dropdown_selector, option) ) def select_option_by_value(self, dropdown_selector, option): element = self.find_element(dropdown_selector) element.scroll_into_view() options = element.query_selector_all("option") for found_option in options: if ( "value" in found_option.attrs and str(found_option.attrs["value"]) == str(option) ): found_option.select_option() return raise Exception( "Unable to find value option {%s} in dropdown {%s}!" % (dropdown_selector, option) ) def flash( self, selector, # The CSS Selector to flash duration=1, # (seconds) flash duration color="44CC88", # RGB hex flash color pause=0, # (seconds) If 0, the next action starts during flash ): """Paint a quickly-vanishing dot over an element.""" selector = self.__convert_to_css_if_xpath(selector) element = self.find_element(selector) element.scroll_into_view() x_offset = self.__get_x_scroll_offset() y_offset = self.__get_y_scroll_offset() element.flash(duration, color, x_offset, y_offset) if pause and isinstance(pause, (int, float)): time.sleep(pause) def highlight(self, selector): """Highlight an element with multi-colors.""" selector = self.__convert_to_css_if_xpath(selector) element = self.find_element(selector) element.scroll_into_view() x_offset = self.__get_x_scroll_offset() y_offset = self.__get_y_scroll_offset() element.flash(0.46, "44CC88", x_offset, y_offset) time.sleep(0.15) element.flash(0.42, "8844CC", x_offset, y_offset) time.sleep(0.15) element.flash(0.38, "CC8844", x_offset, y_offset) time.sleep(0.15) element.flash(0.30, "44CC88", x_offset, y_offset) time.sleep(0.30) def focus(self, selector): element = self.find_element(selector) element.scroll_into_view() element.focus() def highlight_overlay(self, selector): self.find_element(selector).highlight_overlay() def get_parent(self, element): if isinstance(element, str): element = self.select(element) return self.__add_sync_methods(element.parent) def remove_element(self, selector): self.select(selector).remove_from_dom() def remove_from_dom(self, selector): self.select(selector).remove_from_dom() def remove_elements(self, selector): """Remove all elements on the page that match the selector.""" css_selector = self.__convert_to_css_if_xpath(selector) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = js_utils.escape_quotes_if_needed(css_selector) js_code = ( """var $elements = document.querySelectorAll('%s'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].remove();}""" % css_selector ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) def send_keys(self, selector, text, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() if text.endswith("\n") or text.endswith("\r"): text = text[:-1] + "\r\n" elif ( element.tag_name == "textarea" and "\n" in text and "\r" not in text ): text = text.replace("\n", "\r") element.send_keys(text) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def press_keys(self, selector, text, timeout=None): """Similar to send_keys(), but presses keys at human speed.""" if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() submit = False if text.endswith("\n") or text.endswith("\r"): submit = True text = text[:-1] elif ( element.tag_name == "textarea" and "\n" in text and "\r" not in text ): text = text.replace("\n", "\r") for key in text: element.send_keys(key) time.sleep(float(0.042 + (random.random() / 110.0))) if submit: element.send_keys("\r\n") time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def type(self, selector, text, timeout=None): """Similar to send_keys(), but clears the text field first.""" if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() with suppress(Exception): element.clear_input() if text.endswith("\n") or text.endswith("\r"): text = text[:-1] + "\r\n" elif ( element.tag_name == "textarea" and "\n" in text and "\r" not in text ): text = text.replace("\n", "\r") element.send_keys(text) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def clear_input(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() element = self.select(selector, timeout=timeout) element.scroll_into_view() element.clear_input() self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def set_value(self, selector, text, timeout=None): """Similar to send_keys(), but clears the text field first.""" if not timeout: timeout = settings.SMALL_TIMEOUT self.__slow_mode_pause_if_set() selector = self.__convert_to_css_if_xpath(selector) element = self.select(selector, timeout=timeout) element.scroll_into_view() press_enter = False if text.endswith("\n"): text = text[:-1] press_enter = True value = js_utils.escape_quotes_if_needed(re.escape(text)) css_selector = re.escape(selector) css_selector = js_utils.escape_quotes_if_needed(css_selector) set_value_script = ( """m_elm = document.querySelector('%s');""" """m_elm.value = '%s';""" % (css_selector, value) ) self.loop.run_until_complete(self.page.evaluate(set_value_script)) the_type = self.get_element_attribute(selector, "type") if the_type == "range": # Some input sliders need a mouse event to trigger listeners. with suppress(Exception): mouse_move_script = ( """m_elm = document.querySelector('%s');""" """m_evt = new Event('mousemove');""" """m_elm.dispatchEvent(m_evt);""" % css_selector ) self.loop.run_until_complete( self.page.evaluate(mouse_move_script) ) elif press_enter: self.__add_light_pause() self.send_keys(selector, "\n") self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def submit(self, selector): submit_script = ( """elm = document.querySelector('%s'); const event = new KeyboardEvent("keydown", { key: "Enter", keyCode: 13, code: "Enter", which: 13, bubbles: true }); elm.dispatchEvent(event);""" % selector ) self.loop.run_until_complete(self.page.evaluate(submit_script)) def evaluate(self, expression): """Run a JavaScript expression and return the result.""" expression = expression.strip() exp_list = expression.split("\n") if exp_list and exp_list[-1].strip().startswith("return "): expression = ( "\n".join(exp_list[0:-1]) + "\n" + exp_list[-1].strip()[len("return "):] ).strip() return self.loop.run_until_complete(self.page.evaluate(expression)) def execute_script(self, expression): return self.evaluate(expression) def js_dumps(self, obj_name): """Similar to evaluate(), but for dictionary results.""" if obj_name.startswith("return "): obj_name = obj_name[len("return "):] return self.loop.run_until_complete(self.page.js_dumps(obj_name)) def maximize(self): try: if self.get_window()[1].window_state.value == "maximized": return elif self.get_window()[1].window_state.value == "minimized": self.loop.run_until_complete(self.page.maximize()) time.sleep(0.044) return self.loop.run_until_complete(self.page.maximize()) except Exception: with suppress(Exception): width = self.evaluate("screen.availWidth;") height = self.evaluate("screen.availHeight;") self.__set_window_rect(0, 0, width, height) return def minimize(self): if self.get_window()[1].window_state.value != "minimized": return self.loop.run_until_complete(self.page.minimize()) def medimize(self): if self.get_window()[1].window_state.value == "minimized": self.loop.run_until_complete(self.page.medimize()) time.sleep(0.044) return self.loop.run_until_complete(self.page.medimize()) def __set_window_rect(self, x, y, width, height, uc_lock=False): if uc_lock: gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.__make_sure_pyautogui_lock_is_writable() if self.get_window()[1].window_state.value == "minimized": self.loop.run_until_complete( self.page.set_window_size( left=x, top=y, width=width, height=height) ) time.sleep(0.044) return self.loop.run_until_complete( self.page.set_window_size( left=x, top=y, width=width, height=height) ) else: if self.get_window()[1].window_state.value == "minimized": self.loop.run_until_complete( self.page.set_window_size( left=x, top=y, width=width, height=height) ) time.sleep(0.044) return self.loop.run_until_complete( self.page.set_window_size( left=x, top=y, width=width, height=height) ) def set_window_rect(self, x, y, width, height): return self.__set_window_rect(x, y, width, height, uc_lock=True) def reset_window_size(self): x = settings.WINDOW_START_X y = settings.WINDOW_START_Y width = settings.CHROME_START_WIDTH height = settings.CHROME_START_HEIGHT self.__set_window_rect(x, y, width, height, uc_lock=True) self.__add_light_pause() def open_new_window(self, url=None, switch_to=True): return self.open_new_tab(url=url, switch_to=switch_to) def switch_to_window(self, window): self.switch_to_tab(window) def switch_to_newest_window(self): self.switch_to_tab(-1) def open_new_tab(self, url=None, switch_to=True, **kwargs): driver = self.driver if not isinstance(url, str): url = "about:blank" if hasattr(driver, "cdp_base"): try: self.loop.run_until_complete( self.page.get(url, new_tab=True, **kwargs) ) except Exception: original_targets = self.loop.run_until_complete( self.page.send(mycdp.target.get_targets()) ) tab_url = driver.cdp_base.tabs[0].websocket_url if not self.driver.is_connected(): self.driver.connect() self.driver.open_new_tab() targets = self.loop.run_until_complete( self.page.send(mycdp.target.get_targets()) ) new_targets = [] for target in targets: if target not in original_targets: new_targets.append(target) if new_targets: found_target = new_targets[0] t_str = str(new_targets[0]) target_id = ( t_str.split("target_id=TargetID('")[-1].split("')")[0] ) pre_tab_url = tab_url.split("/page/")[0] + "/page/" new_tab_url = pre_tab_url + target_id new_tab = cdp_tab.Tab( new_tab_url, found_target, driver.cdp_base ) driver.cdp_base.targets.append(new_tab) driver.cdp_base.tabs.append(new_tab) self.driver.disconnect() self.switch_to_newest_tab() self.open(url) return elif getattr(sb_config, "guest_mode", None): print(" open_new_tab() failed! (Known Guest Mode issue)") if switch_to: self.switch_to_newest_tab() return target_id = self.loop.run_until_complete( self.page.send(mycdp.target.create_target(url)) ) if not target_id and getattr(sb_config, "guest_mode", None): print(" open_new_tab() failed! (Known Guest Mode issue)") found_target = None targets = self.loop.run_until_complete( self.page.send(mycdp.target.get_targets()) ) if target_id: for target in targets: if str(target_id) in str(target): found_target = target break if found_target: tab_url = driver.tabs[0].websocket_url pre_tab_url = tab_url.split("/page/")[0] + "/page/" new_tab_url = pre_tab_url + target_id new_tab = cdp_tab.Tab(new_tab_url, found_target, driver) driver.targets.append(new_tab) driver.tabs.append(new_tab) if switch_to: self.switch_to_newest_tab() def switch_to_tab(self, tab): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base if isinstance(tab, int): self.page = driver.tabs[tab] elif isinstance(tab, cdp_util.Tab): self.page = tab else: raise Exception("`tab` must be an int or a Tab type!") self.bring_active_window_to_front() def switch_to_newest_tab(self): self.switch_to_tab(-1) def close_active_tab(self): """Close the active tab. The active tab is the one currenly controlled by CDP. The active tab MIGHT NOT be the currently visible tab! (If a page opens a new tab, the new tab WON'T be active) To switch the active tab, call: sb.switch_to_tab(tab)""" return self.loop.run_until_complete(self.page.close()) def get_active_tab(self): """Return the active tab. The active tab is the one currenly controlled by CDP. The active tab MIGHT NOT be the currently visible tab! (If a page opens a new tab, the new tab WON'T be active) To switch the active tab, call: sb.switch_to_tab(tab)""" return self.page def get_tabs(self): driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base return driver.tabs def get_window(self): return self.loop.run_until_complete(self.page.get_window()) def get_text(self, selector): return self.find_element(selector).text_all def get_title(self): return self.loop.run_until_complete( self.page.evaluate("document.title") ) def get_current_url(self): return self.loop.run_until_complete( self.page.evaluate("window.location.href") ) def get_origin(self): return self.loop.run_until_complete( self.page.evaluate("window.location.origin") ) def get_html(self, include_shadow_dom=True): return self.get_page_source( include_shadow_dom=include_shadow_dom ) def get_page_source(self, include_shadow_dom=True): if include_shadow_dom: return self.find_element("html").get_html() try: source = self.loop.run_until_complete( self.page.evaluate("document.documentElement.outerHTML") ) except Exception: time.sleep(constants.UC.CDP_MODE_OPEN_WAIT) source = self.loop.run_until_complete( self.page.evaluate("document.documentElement.outerHTML") ) return source def get_user_agent(self): return self.loop.run_until_complete( self.page.evaluate("navigator.userAgent") ) def get_cookie_string(self): return self.loop.run_until_complete( self.page.evaluate("document.cookie") ) def get_locale_code(self): return self.loop.run_until_complete( self.page.evaluate("navigator.language || navigator.languages[0]") ) def get_local_storage_item(self, key): js_code = """localStorage.getItem('%s');""" % key with suppress(Exception): return self.loop.run_until_complete(self.page.evaluate(js_code)) def get_session_storage_item(self, key): js_code = """sessionStorage.getItem('%s');""" % key with suppress(Exception): return self.loop.run_until_complete(self.page.evaluate(js_code)) def get_screen_rect(self): coordinates = self.loop.run_until_complete( self.page.js_dumps("window.screen") ) return coordinates def get_window_rect(self): coordinates = {} innerWidth = self.loop.run_until_complete( self.page.evaluate("window.innerWidth") ) innerHeight = self.loop.run_until_complete( self.page.evaluate("window.innerHeight") ) outerWidth = self.loop.run_until_complete( self.page.evaluate("window.outerWidth") ) outerHeight = self.loop.run_until_complete( self.page.evaluate("window.outerHeight") ) pageXOffset = self.loop.run_until_complete( self.page.evaluate("window.pageXOffset") ) pageYOffset = self.loop.run_until_complete( self.page.evaluate("window.pageYOffset") ) scrollX = self.loop.run_until_complete( self.page.evaluate("window.scrollX") ) scrollY = self.loop.run_until_complete( self.page.evaluate("window.scrollY") ) screenLeft = self.loop.run_until_complete( self.page.evaluate("window.screenLeft") ) screenTop = self.loop.run_until_complete( self.page.evaluate("window.screenTop") ) x = self.loop.run_until_complete( self.page.evaluate("window.screenX") ) y = self.loop.run_until_complete( self.page.evaluate("window.screenY") ) coordinates["innerWidth"] = innerWidth coordinates["innerHeight"] = innerHeight coordinates["outerWidth"] = outerWidth coordinates["outerHeight"] = outerHeight coordinates["width"] = outerWidth coordinates["height"] = outerHeight coordinates["pageXOffset"] = pageXOffset if pageXOffset else 0 coordinates["pageYOffset"] = pageYOffset if pageYOffset else 0 coordinates["scrollX"] = scrollX if scrollX else 0 coordinates["scrollY"] = scrollY if scrollY else 0 coordinates["screenLeft"] = screenLeft if screenLeft else 0 coordinates["screenTop"] = screenTop if screenTop else 0 coordinates["x"] = x if x else 0 coordinates["y"] = y if y else 0 return coordinates def get_window_size(self): coordinates = {} outerWidth = self.loop.run_until_complete( self.page.evaluate("window.outerWidth") ) outerHeight = self.loop.run_until_complete( self.page.evaluate("window.outerHeight") ) coordinates["width"] = outerWidth coordinates["height"] = outerHeight return coordinates def get_window_position(self): coordinates = {} x = self.loop.run_until_complete( self.page.evaluate("window.screenX") ) y = self.loop.run_until_complete( self.page.evaluate("window.screenY") ) coordinates["x"] = x if x else 0 coordinates["y"] = y if y else 0 return coordinates def get_element_rect(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT selector = self.__convert_to_css_if_xpath(selector) element = self.select(selector, timeout=timeout) self.__add_light_pause() coordinates = None if ":contains(" in selector: position = element.get_position() x = position.x y = position.y width = position.width height = position.height coordinates = {"x": x, "y": y, "width": width, "height": height} else: coordinates = self.loop.run_until_complete( self.page.js_dumps( """document.querySelector('%s').getBoundingClientRect()""" % js_utils.escape_quotes_if_needed(re.escape(selector)) ) ) return coordinates def get_element_size(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT element_rect = self.get_element_rect(selector, timeout=timeout) coordinates = {} coordinates["width"] = element_rect["width"] coordinates["height"] = element_rect["height"] return coordinates def get_element_position(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT element_rect = self.get_element_rect(selector, timeout=timeout) coordinates = {} coordinates["x"] = element_rect["x"] coordinates["y"] = element_rect["y"] return coordinates def get_gui_element_rect(self, selector, timeout=None): """(Coordinates are relative to the screen. Not the window.)""" if not timeout: timeout = settings.SMALL_TIMEOUT element_rect = self.get_element_rect(selector, timeout=timeout) e_width = element_rect["width"] e_height = element_rect["height"] window_rect = self.get_window_rect() w_bottom_y = window_rect["y"] + window_rect["height"] viewport_height = window_rect["innerHeight"] x = window_rect["x"] + element_rect["x"] y = w_bottom_y - viewport_height + element_rect["y"] y_scroll_offset = window_rect["pageYOffset"] if ( hasattr(sb_config, "_cdp_browser") and sb_config._cdp_browser == "opera" ): # Handle special case where Opera side panel shifts coordinates x_offset = window_rect["outerWidth"] - window_rect["innerWidth"] if x_offset > 56: x_offset = 56 elif x_offset < 22: x_offset = 0 x = x + x_offset y = y - y_scroll_offset x = x + window_rect["scrollX"] y = y + window_rect["scrollY"] return ({"height": e_height, "width": e_width, "x": x, "y": y}) def get_gui_element_center(self, selector, timeout=None): """(Coordinates are relative to the screen. Not the window.)""" if not timeout: timeout = settings.SMALL_TIMEOUT element_rect = self.get_gui_element_rect(selector, timeout=timeout) e_width = element_rect["width"] e_height = element_rect["height"] e_x = element_rect["x"] e_y = element_rect["y"] return ((e_x + e_width / 2.0) + 0.5, (e_y + e_height / 2.0) + 0.5) def get_document(self): return self.loop.run_until_complete(self.page.get_document()) def get_flattened_document(self): return self.loop.run_until_complete(self.page.get_flattened_document()) def get_element_attributes(self, selector): selector = self.__convert_to_css_if_xpath(selector) return self.loop.run_until_complete( self.page.js_dumps( """document.querySelector('%s')""" % js_utils.escape_quotes_if_needed(re.escape(selector)) ) ) def get_element_attribute(self, selector, attribute): """Find an element and return the value of an attribute. Raises an exception if there's no such element or attribute.""" attributes = self.get_element_attributes(selector) with suppress(Exception): return attributes[attribute] locate = ' %s="' % attribute value = self.get_attribute(selector, attribute) if not value and locate not in attributes: raise KeyError(attribute) return value def get_attribute(self, selector, attribute): """Find an element and return the value of an attribute. If the element doesn't exist: Raises an exception. If the attribute doesn't exist: Returns None.""" return self.find_element(selector).get_attribute(attribute) def get_element_html(self, selector): """Find an element and return the outerHTML.""" selector = self.__convert_to_css_if_xpath(selector) self.find_element(selector) self.__add_light_pause() return self.loop.run_until_complete( self.page.evaluate( """document.querySelector('%s').outerHTML""" % js_utils.escape_quotes_if_needed(re.escape(selector)) ) ) def get_mfa_code(self, totp_key=None): """Returns a time-based one-time password based on the Google Authenticator algorithm for multi-factor authentication.""" return shared_utils.get_mfa_code(totp_key) def enter_mfa_code(self, selector, totp_key=None, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT mfa_code = self.get_mfa_code(totp_key) self.type(selector, mfa_code + "\n", timeout=timeout) def activate_messenger(self): js_utils.activate_messenger(self) self.__add_light_pause() def set_messenger_theme( self, theme="default", location="default", max_messages="default" ): """Sets a theme for posting messages. Themes: ["flat", "future", "block", "air", "ice"] Locations: ["top_left", "top_center", "top_right", "bottom_left", "bottom_center", "bottom_right"] max_messages: The limit of concurrent messages to display.""" if not theme: theme = "default" # "flat" if not location: location = "default" # "bottom_right" if not max_messages: max_messages = "default" # "8" else: max_messages = str(max_messages) # Value must be in string format js_utils.set_messenger_theme( self, theme=theme, location=location, max_messages=max_messages, ) self.__add_light_pause() def post_message(self, message, duration=None, pause=True, style="info"): """Post a message on the screen with Messenger. Arguments: message: The message to display. duration: The time until the message vanishes. (Default: 2.55s) pause: If True, the program waits until the message completes. style: "info", "success", or "error".""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base if style not in ["info", "success", "error"]: style = "info" if not duration: duration = settings.DEFAULT_MESSAGE_DURATION if ( ( driver.config.headless or (hasattr(sb_config, "xvfb") and sb_config.xvfb) ) and float(duration) > 0.75 ): duration = 0.75 try: js_utils.post_message(self, message, duration, style=style) except Exception: print(" * %s message: %s" % (style.upper(), message)) if pause: duration = float(duration) + 0.15 time.sleep(float(duration)) def set_locale(self, locale): """(Settings will take effect on the next page load)""" self.loop.run_until_complete(self.page.set_locale(locale)) def set_local_storage_item(self, key, value): js_code = """localStorage.setItem('%s','%s');""" % (key, value) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) def set_session_storage_item(self, key, value): js_code = """sessionStorage.setItem('%s','%s');""" % (key, value) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) def set_attributes(self, selector, attribute, value): """This method uses JavaScript to set/update a common attribute. All matching selectors from querySelectorAll() are used. Example => (Make all links on a website redirect to Google): self.set_attributes("a", "href", "https://google.com")""" attribute = re.escape(attribute) attribute = js_utils.escape_quotes_if_needed(attribute) value = re.escape(value) value = js_utils.escape_quotes_if_needed(value) css_selector = self.__convert_to_css_if_xpath(selector) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = js_utils.escape_quotes_if_needed(css_selector) js_code = ( """var $elements = document.querySelectorAll('%s'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].setAttribute('%s','%s');}""" % ( css_selector, attribute, value, ) ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) def is_attribute_present(self, selector, attribute, value=None): try: element = self.find_element(selector, timeout=0.1) found_value = element.get_attribute(attribute) if found_value is None: return False if value is not None: if found_value == value: return True else: return False else: return True except Exception: return False def is_online(self): js_code = "navigator.onLine;" return self.loop.run_until_complete(self.page.evaluate(js_code)) def __make_sure_pyautogui_lock_is_writable(self): with suppress(Exception): shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK) def __verify_pyautogui_has_a_headed_browser(self): """PyAutoGUI requires a headed browser so that it can focus on the correct element when performing actions.""" driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base if driver.config.headless: raise Exception( "PyAutoGUI can't be used in headless mode!" ) def __install_pyautogui_if_missing(self): self.__verify_pyautogui_has_a_headed_browser() driver = self.driver if hasattr(driver, "cdp_base"): driver = driver.cdp_base pip_find_lock = fasteners.InterProcessLock( constants.PipInstall.FINDLOCK ) with pip_find_lock: # Prevent issues with multiple processes with suppress(Exception): shared_utils.make_writable(constants.PipInstall.FINDLOCK) try: import pyautogui with suppress(Exception): use_pyautogui_ver = constants.PyAutoGUI.VER u_pv = shared_utils.make_version_tuple(use_pyautogui_ver) pv = shared_utils.make_version_tuple(pyautogui.__version__) if pv < u_pv: del pyautogui # To get newer ver shared_utils.pip_install("pyautogui", version="Latest") import pyautogui except Exception: print("\nPyAutoGUI required! Installing now...") shared_utils.pip_install("pyautogui", version="Latest") try: import pyautogui except Exception: if ( shared_utils.is_linux() and ( not sb_config.headed or (hasattr(sb_config, "xvfb") and sb_config.xvfb) ) and not driver.config.headless and ( not hasattr(sb_config, "_virtual_display") or not sb_config._virtual_display ) ): from sbvirtualdisplay import Display xvfb_width = 1366 xvfb_height = 768 if ( hasattr(sb_config, "_xvfb_width") and sb_config._xvfb_width and isinstance(sb_config._xvfb_width, int) and hasattr(sb_config, "_xvfb_height") and sb_config._xvfb_height and isinstance(sb_config._xvfb_height, int) ): xvfb_width = sb_config._xvfb_width xvfb_height = sb_config._xvfb_height if xvfb_width < 1024: xvfb_width = 1024 sb_config._xvfb_width = xvfb_width if xvfb_height < 768: xvfb_height = 768 sb_config._xvfb_height = xvfb_height with suppress(Exception): xvfb_display = Display( visible=True, size=(xvfb_width, xvfb_height), backend="xvfb", use_xauth=True, ) if "--debug-display" in sys.argv: print( "Starting VDisplay from sb_cdp: (%s, %s)" % (xvfb_width, xvfb_height) ) xvfb_display.start() def __get_configured_pyautogui(self, pyautogui_copy): if ( shared_utils.is_linux() and hasattr(pyautogui_copy, "_pyautogui_x11") and "DISPLAY" in os.environ.keys() ): if ( hasattr(sb_config, "_pyautogui_x11_display") and sb_config._pyautogui_x11_display and hasattr(pyautogui_copy._pyautogui_x11, "_display") and ( sb_config._pyautogui_x11_display == pyautogui_copy._pyautogui_x11._display ) ): pass else: import Xlib.display pyautogui_copy._pyautogui_x11._display = ( Xlib.display.Display(os.environ['DISPLAY']) ) sb_config._pyautogui_x11_display = ( pyautogui_copy._pyautogui_x11._display ) return pyautogui_copy def gui_press_key(self, key): self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.__make_sure_pyautogui_lock_is_writable() pyautogui.press(key) time.sleep(0.044) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def gui_press_keys(self, keys): self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.__make_sure_pyautogui_lock_is_writable() for key in keys: pyautogui.press(key) time.sleep(float(0.042 + (random.random() / 110.0))) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def gui_write(self, text): self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.__make_sure_pyautogui_lock_is_writable() pyautogui.write(text) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.sleep(0.025)) def __gui_click_x_y(self, x, y, timeframe=0.25, uc_lock=False): self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() if x < 0 or y < 0 or x > screen_width or y > screen_height: raise Exception( "PyAutoGUI cannot click on point (%s, %s)" " outside screen. (Width: %s, Height: %s)" % (x, y, screen_width, screen_height) ) if uc_lock: gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.__make_sure_pyautogui_lock_is_writable() pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad) if timeframe >= 0.25: time.sleep(0.056) # Wait if moving at human-speed if "--debug" in sys.argv: print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y)) pyautogui.click(x=x, y=y) else: # Called from a method where the gui_lock is already active pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad) if timeframe >= 0.25: time.sleep(0.056) # Wait if moving at human-speed if "--debug" in sys.argv: print(" <DEBUG> pyautogui.click(%s, %s)" % (x, y)) pyautogui.click(x=x, y=y) def gui_click_x_y(self, x, y, timeframe=0.25): gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.__make_sure_pyautogui_lock_is_writable() self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) width_ratio = 1.0 if shared_utils.is_windows(): window_rect = self.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] scr_width = pyautogui.size().width win_width = self.evaluate("screen.availWidth;") width_ratio = round(float(scr_width) / float(win_width), 2) width_ratio += 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio with suppress(Exception): self.get_window() # If this fails, skip the rest self.minimize() self.__add_light_pause() self.__set_window_rect(win_x, win_y, width, height) self.__add_light_pause() x = x * (width_ratio + 0.03) y = y * (width_ratio - 0.03) self.bring_active_window_to_front() self.__gui_click_x_y(x, y, timeframe=timeframe, uc_lock=False) def gui_click_element(self, selector, timeframe=0.25): self.__slow_mode_pause_if_set() x, y = self.get_gui_element_center(selector) self.__add_light_pause() self.gui_click_x_y(x, y, timeframe=timeframe) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def gui_click_with_offset( self, selector, x, y, timeframe=0.25, center=False ): """Click an element at an {X,Y}-offset location. {0,0} is the top-left corner of the element. If center==True, {0,0} becomes the center of the element. The timeframe is the time spent moving the mouse.""" if center: px, py = self.get_gui_element_center(selector) self.gui_click_x_y(px + x, py + y, timeframe=timeframe) else: element_rect = self.get_gui_element_rect(selector) px = element_rect["x"] py = element_rect["y"] self.gui_click_x_y(px + x, py + y, timeframe=timeframe) def click_with_offset(self, selector, x, y, center=False): element = self.find_element(selector) element.scroll_into_view() if "--debug" in sys.argv: displayed_selector = "`%s`" % selector if '"' not in selector: displayed_selector = '"%s"' % selector elif "'" not in selector: displayed_selector = "'%s'" % selector print( " <DEBUG> sb.click_with_offset(%s, %s, %s, center=%s)" % (displayed_selector, x, y, center) ) element.click_with_offset(x=x, y=y, center=center) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def _on_a_cf_turnstile_page(self, source=None): if not source or len(source) < 400: time.sleep(0.2) source = self.get_page_source() if ( ( 'data-callback="onCaptchaSuccess"' in source and 'title="reCAPTCHA"' not in source and 'id="recaptcha-token"' not in source ) or "/challenge-platform/h/b/" in source or 'id="challenge-widget-' in source or "challenges.cloudf" in source or "cf-turnstile-" in source ): return True return False def _on_an_incapsula_hcaptcha_page(self, *args, **kwargs): self.loop.run_until_complete(self.page.wait(0.1)) if ( self.is_element_visible('iframe[src*="_Incapsula_Resource?"]') or self.is_element_visible("iframe[data-hcaptcha-widget-id]") ): return True return False def _on_a_datadome_slider_page(self, *args, **kwargs): self.loop.run_until_complete(self.page.wait(0.1)) if ( self.is_element_visible( 'body > iframe[src*="/geo.captcha-delivery.com/captcha/"]' ) ): return True return False def _on_a_g_recaptcha_page(self, *args, **kwargs): time.sleep(0.4) # reCAPTCHA may need a moment to appear self.loop.run_until_complete(self.page.wait(0.1)) source = self.get_page_source() if ( ( 'id="recaptcha-token"' in source or 'title="reCAPTCHA"' in source ) and self.is_element_visible('iframe[title="reCAPTCHA"]') ): try: self.loop.run_until_complete(self.page.wait(0.1)) except Exception: time.sleep(0.1) return True elif "com/recaptcha/api.js" in source: time.sleep(1.2) # Maybe still loading try: self.loop.run_until_complete(self.page.wait(0.1)) except Exception: time.sleep(0.1) return True return False def __gui_click_recaptcha(self, use_cdp=False): selector = None if self.is_element_visible('iframe[title="reCAPTCHA"]'): selector = 'iframe[title="reCAPTCHA"]' else: return False time.sleep(0.25) self.loop.run_until_complete(self.page.wait(0.1)) time.sleep(0.25) with suppress(Exception): element_rect = self.get_element_rect(selector, timeout=0.1) e_x = element_rect["x"] e_y = element_rect["y"] window_rect = self.get_window_rect() win_width = window_rect["innerWidth"] win_height = window_rect["innerHeight"] if ( e_x > 1040 and e_y > 640 and abs(win_width - e_x) < 110 and abs(win_height - e_y) < 110 ): # Probably the invisible reCAPTCHA in the bottom right corner return False gui_element_rect = self.get_gui_element_rect(selector, timeout=1) gui_e_x = gui_element_rect["x"] gui_e_y = gui_element_rect["y"] x_offset = 26 y_offset = 35 if shared_utils.is_windows(): x_offset = 29 x = gui_e_x + x_offset y = gui_e_y + y_offset sb_config._saved_cf_x_y = (x, y) time.sleep(0.08) if use_cdp: self.sleep(0.03) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.bring_active_window_to_front() time.sleep(0.056) self.click_with_offset(selector, x_offset, y_offset) time.sleep(0.056) else: self.gui_click_x_y(x, y) return True return False def __gui_slide_datadome_captcha(self): iframe = 'body > iframe[src*="/geo.captcha-delivery.com/captcha/"]' if not self.is_element_visible(iframe): return False src = self.get_attribute(iframe, "src") tab = self.get_active_tab() self.open_new_tab(url=src) time.sleep(0.41) self.loop.run_until_complete(self.page.wait(0.1)) time.sleep(0.25) x1, y1 = self.get_gui_element_center("div.slider") x2, y2 = self.get_gui_element_center("div.sliderTarget") self.close_active_tab() self.switch_to_tab(tab) self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=0.55) time.sleep(0.25) self.loop.run_until_complete(self.page.wait(0.2)) time.sleep(0.15) return True def __cdp_click_incapsula_hcaptcha(self): selector = "iframe[data-hcaptcha-widget-id]" if self.is_element_visible('iframe[src*="_Incapsula_Resource?"]'): outer_selector = 'iframe[src*="_Incapsula_Resource?"]' element = self.get_nested_element(outer_selector, selector) if not element: return False elif self.is_element_visible(selector): element = self.select(selector, timeout=0.1) if not element: return False else: return False time.sleep(0.05) self.loop.run_until_complete(self.page.wait(0.1)) time.sleep(0.05) x_offset = 30 y_offset = 36 was_clicked = False gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.bring_active_window_to_front() time.sleep(0.056) if "--debug" in sys.argv: displayed_selector = "`%s`" % selector if '"' not in selector: displayed_selector = '"%s"' % selector elif "'" not in selector: displayed_selector = "'%s'" % selector print( " <DEBUG> click_with_offset(%s, %s, %s)" % (displayed_selector, x_offset, y_offset) ) with suppress(Exception): element.click_with_offset(x_offset, y_offset) was_clicked = True time.sleep(0.075) if was_clicked: # Wait a moment for the click to succeed time.sleep(0.75) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.1)) if "--debug" in sys.argv: print(" <DEBUG> hCaptcha was clicked!") return True if "--debug" in sys.argv: print(" <DEBUG> hCaptcha was NOT clicked!") return False def solve_captcha(self): self.__click_captcha(use_cdp=True) def click_captcha(self): """Same as solve_captcha()""" self.__click_captcha(use_cdp=True) def gui_click_captcha(self): """Use PyAutoGUI to click the CAPTCHA""" self.__click_captcha(use_cdp=False) def __click_captcha(self, use_cdp=False): """Uses PyAutoGUI unless use_cdp == True""" self.sleep(0.075) self.loop.run_until_complete(self.page.wait(0.1)) self.sleep(0.025) source = self.get_page_source() if self._on_a_cf_turnstile_page(source): pass elif self._on_a_g_recaptcha_page(source): result = self.__gui_click_recaptcha(use_cdp) return result elif self._on_an_incapsula_hcaptcha_page(): result = self.__cdp_click_incapsula_hcaptcha() return result elif self._on_a_datadome_slider_page(): result = self.__gui_slide_datadome_captcha() return result else: return False selector = None if self.is_element_present('[class="cf-turnstile"]'): selector = '[class="cf-turnstile"]' elif self.is_element_present("#challenge-form div > div"): selector = "#challenge-form div > div" elif self.is_element_present('[style="display: grid;"] div div'): selector = '[style="display: grid;"] div div' elif self.is_element_present("[class*=spacer] + div div"): selector = '[class*=spacer] + div div' elif self.is_element_present(".spacer div:not([class])"): selector = ".spacer div:not([class])" elif self.is_element_present('[data-testid*="challenge-"] div'): selector = '[data-testid*="challenge-"] div' elif self.is_element_present("div#turnstile-widget div:not([class])"): selector = "div#turnstile-widget div:not([class])" elif self.is_element_present("ngx-turnstile div:not([class])"): selector = "ngx-turnstile div:not([class])" elif self.is_element_present( 'form div:not([class]):has(input[name*="cf-turn"])' ): selector = 'form div:not([class]):has(input[name*="cf-turn"])' elif self.is_element_present("form div:not(:has(*))"): selector = "form div:not(:has(*))" elif self.is_element_present("body > div#check > div:not([class])"): selector = "body > div#check > div:not([class])" elif self.is_element_present(".cf-turnstile-wrapper"): selector = ".cf-turnstile-wrapper" elif self.is_element_present( '[id*="turnstile"] div:not([class])' ): selector = '[id*="turnstile"] div:not([class])' elif self.is_element_present( '[class*="turnstile"] div:not([class])' ): selector = '[class*="turnstile"] div:not([class])' elif self.is_element_present( "iframe[data-hcaptcha-widget-id]" ): selector = "iframe[data-hcaptcha-widget-id]" elif self.is_element_present( '[data-callback="onCaptchaSuccess"]' ): selector = '[data-callback="onCaptchaSuccess"]' elif self.is_element_present( "div:not([class]):not([id]):not([aria-label]) > " "div:not([class]):not([id]):not([aria-label])" ): selector = ( "div:not([class]):not([id]):not([aria-label]) > " "div:not([class]):not([id]):not([aria-label])" ) else: return False if not selector: return False if ( self.is_element_present("form") and ( self.is_element_present('form[class*="center"]') or self.is_element_present('form[class*="right"]') or self.is_element_present('form div[class*="center"]') or self.is_element_present('form div[class*="right"]') ) ): script = ( """var $elements = document.querySelectorAll( 'form[class], form div[class]'); var index = 0, length = $elements.length; for(; index < length; index++){ the_class = $elements[index].getAttribute('class'); new_class = the_class.replaceAll('center', 'left'); new_class = new_class.replaceAll('right', 'left'); $elements[index].setAttribute('class', new_class);}""" ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) self.loop.run_until_complete(self.page.wait(0.1)) elif ( self.is_element_present("form") and ( self.is_element_present('form div[style*="center"]') or self.is_element_present('form div[style*="right"]') ) ): script = ( """var $elements = document.querySelectorAll( 'form[style], form div[style]'); var index = 0, length = $elements.length; for(; index < length; index++){ the_style = $elements[index].getAttribute('style'); new_style = the_style.replaceAll('center', 'left'); new_style = new_style.replaceAll('right', 'left'); $elements[index].setAttribute('style', new_style);}""" ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) self.loop.run_until_complete(self.page.wait(0.1)) elif ( self.is_element_present( 'form [id*="turnstile"] div:not([class])' ) or self.is_element_present( 'form [class*="turnstile"] div:not([class])' ) ): script = ( """var $elements = document.querySelectorAll( 'form [id*="turnstile"]'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].setAttribute('align', 'left');} var $elements = document.querySelectorAll( 'form [class*="turnstile"]'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].setAttribute('align', 'left');}""" ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) self.loop.run_until_complete(self.page.wait(0.1)) elif ( self.is_element_present( '[style*="text-align: center;"] div:not([class])' ) ): script = ( """var $elements = document.querySelectorAll( '[style*="text-align: center;"]'); var index = 0, length = $elements.length; for(; index < length; index++){ the_style = $elements[index].getAttribute('style'); new_style = the_style.replaceAll('center', 'left'); $elements[index].setAttribute('style', new_style);}""" ) with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(script)) self.loop.run_until_complete(self.page.wait(0.1)) with suppress(Exception): time.sleep(0.05) element_rect = self.get_gui_element_rect(selector, timeout=1) e_x = element_rect["x"] e_y = element_rect["y"] x_offset = 28 y_offset = 32 if shared_utils.is_windows(): y_offset = 28 x = e_x + x_offset y = e_y + y_offset sb_config._saved_cf_x_y = (x, y) time.sleep(0.05) if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy: time.sleep(0.22) # CAPTCHA may load slower with proxy if use_cdp: self.sleep(0.03) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.bring_active_window_to_front() time.sleep(0.05) self.click_with_offset(selector, x_offset, y_offset) time.sleep(0.05) else: self.gui_click_x_y(x, y) return True return False def __gui_drag_drop(self, x1, y1, x2, y2, timeframe=0.25, uc_lock=False): self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() if x1 < 0 or y1 < 0 or x1 > screen_width or y1 > screen_height: raise Exception( "PyAutoGUI cannot drag-drop from point (%s, %s)" " outside screen. (Width: %s, Height: %s)" % (x1, y1, screen_width, screen_height) ) if x2 < 0 or y2 < 0 or x2 > screen_width or y2 > screen_height: raise Exception( "PyAutoGUI cannot drag-drop to point (%s, %s)" " outside screen. (Width: %s, Height: %s)" % (x2, y2, screen_width, screen_height) ) if uc_lock: gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes if "--debug" in sys.argv: print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1)) pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad) self.__add_light_pause() if "--debug" in sys.argv: print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2)) pyautogui.dragTo(x2, y2, button="left", duration=timeframe) else: # Called from a method where the gui_lock is already active if "--debug" in sys.argv: print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x1, y1)) pyautogui.moveTo(x1, y1, 0.25, pyautogui.easeOutQuad) self.__add_light_pause() if "--debug" in sys.argv: print(" <DEBUG> pyautogui.dragTo(%s, %s)" % (x2, y2)) pyautogui.dragTo(x2, y2, button="left", duration=timeframe) def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35): """Use PyAutoGUI to drag-and-drop from one point to another. Can simulate click-and-hold when using the same point twice.""" gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) width_ratio = 1.0 if shared_utils.is_windows(): window_rect = self.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] scr_width = pyautogui.size().width win_width = self.evaluate("screen.availWidth;") width_ratio = round(float(scr_width) / float(win_width), 2) width_ratio += 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio with suppress(Exception): self.get_window() # If this fails, skip the rest self.minimize() self.__add_light_pause() self.__set_window_rect(win_x, win_y, width, height) self.__add_light_pause() x1 = x1 * width_ratio y1 = y1 * (width_ratio - 0.02) x2 = x2 * width_ratio y2 = y2 * (width_ratio - 0.02) self.bring_active_window_to_front() self.__gui_drag_drop( x1, y1, x2, y2, timeframe=timeframe, uc_lock=False ) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.2)) def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35): """Use PyAutoGUI to drag-and-drop from one selector to another. Can simulate click-and-hold when using the same selector twice.""" self.__slow_mode_pause_if_set() self.bring_active_window_to_front() x1, y1 = self.get_gui_element_center(drag_selector) self.__add_light_pause() x2, y2 = self.get_gui_element_center(drop_selector) self.__add_light_pause() self.gui_drag_drop_points(x1, y1, x2, y2, timeframe=timeframe) def gui_click_and_hold(self, selector, timeframe=0.35): """Use PyAutoGUI to click-and-hold a selector.""" self.__slow_mode_pause_if_set() self.bring_active_window_to_front() x, y = self.get_gui_element_center(selector) self.__add_light_pause() self.gui_drag_drop_points(x, y, x, y, timeframe=timeframe) def __gui_hover_x_y(self, x, y, timeframe=0.25, uc_lock=False): self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) screen_width, screen_height = pyautogui.size() if x < 0 or y < 0 or x > screen_width or y > screen_height: raise Exception( "PyAutoGUI cannot hover on point (%s, %s)" " outside screen. (Width: %s, Height: %s)" % (x, y, screen_width, screen_height) ) if uc_lock: gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes if "--debug" in sys.argv: print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y)) pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad) time.sleep(0.056) else: # Called from a method where the gui_lock is already active if "--debug" in sys.argv: print(" <DEBUG> pyautogui.moveTo(%s, %s)" % (x, y)) pyautogui.moveTo(x, y, timeframe, pyautogui.easeOutQuad) time.sleep(0.056) def gui_hover_x_y(self, x, y, timeframe=0.25): gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: # Prevent issues with multiple processes self.__install_pyautogui_if_missing() import pyautogui pyautogui = self.__get_configured_pyautogui(pyautogui) width_ratio = 1.0 if ( shared_utils.is_windows() and ( not hasattr(sb_config, "_saved_width_ratio") or not sb_config._saved_width_ratio ) ): window_rect = self.get_window_rect() width = window_rect["width"] height = window_rect["height"] win_x = window_rect["x"] win_y = window_rect["y"] if ( hasattr(sb_config, "_saved_width_ratio") and sb_config._saved_width_ratio ): width_ratio = sb_config._saved_width_ratio else: scr_width = pyautogui.size().width win_width = self.evaluate("screen.availWidth;") width_ratio = round(float(scr_width) / float(win_width), 2) width_ratio += 0.01 if width_ratio < 0.45 or width_ratio > 2.55: width_ratio = 1.01 sb_config._saved_width_ratio = width_ratio with suppress(Exception): self.__set_window_rect(win_x, win_y, width, height) self.__add_light_pause() self.bring_active_window_to_front() elif ( shared_utils.is_windows() and hasattr(sb_config, "_saved_width_ratio") and sb_config._saved_width_ratio ): width_ratio = sb_config._saved_width_ratio self.bring_active_window_to_front() if shared_utils.is_windows(): x = x * width_ratio y = y * width_ratio self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False) return self.bring_active_window_to_front() self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False) def gui_hover_element(self, selector, timeframe=0.25): self.__slow_mode_pause_if_set() element_rect = self.get_gui_element_rect(selector) width = element_rect["width"] height = element_rect["height"] if width > 0 and height > 0: x, y = self.get_gui_element_center(selector) self.bring_active_window_to_front() self.__gui_hover_x_y(x, y, timeframe=timeframe, uc_lock=False) self.__slow_mode_pause_if_set() self.loop.run_until_complete(self.page.wait(0.1)) def hover_element(self, selector, timeframe=0.25): element = self.select(selector) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.bring_active_window_to_front() self.sleep(0.02) element.mouse_move() self.sleep(timeframe) def hover_and_click(self, hover_selector, click_selector): if getattr(sb_config, "_cdp_mobile_mode", None): self.select(click_selector).click() return hover_element = self.select(hover_selector) gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.bring_active_window_to_front() self.sleep(0.02) hover_element.mouse_move() self.sleep(0.25) try: self.click(click_selector, timeout=0.5) except Exception: self.select(click_selector, timeout=2).click() def gui_hover_and_click(self, hover_selector, click_selector): if getattr(sb_config, "_cdp_mobile_mode", None): self.select(click_selector).click() return gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: self.__make_sure_pyautogui_lock_is_writable() self.bring_active_window_to_front() self.gui_hover_element(hover_selector) time.sleep(0.15) self.gui_hover_element(click_selector) self.click(click_selector) def internalize_links(self): """All `target="_blank"` links become `target="_self"`. This prevents those links from opening in a new tab.""" self.set_attributes('[target="_blank"]', "target", "_self") def is_checked(self, selector): """Return True if checkbox (or radio button) is checked.""" selector = self.__convert_to_css_if_xpath(selector) self.find_element(selector, timeout=settings.SMALL_TIMEOUT) return bool(self.get_element_attribute(selector, "checked")) def is_selected(self, selector): selector = self.__convert_to_css_if_xpath(selector) return self.is_checked(selector) def check_if_unchecked(self, selector): selector = self.__convert_to_css_if_xpath(selector) if not self.is_checked(selector): self.click(selector) def select_if_unselected(self, selector): selector = self.__convert_to_css_if_xpath(selector) self.check_if_unchecked(selector) def uncheck_if_checked(self, selector): selector = self.__convert_to_css_if_xpath(selector) if self.is_checked(selector): self.click(selector) def unselect_if_selected(self, selector): selector = self.__convert_to_css_if_xpath(selector) self.uncheck_if_checked(selector) def is_element_present(self, selector): try: self.select(selector, timeout=0.01) return True except Exception: return False def is_element_visible(self, selector): selector = self.__convert_to_css_if_xpath(selector) element = None if ":contains(" not in selector: try: element = self.select(selector, timeout=0.01) except Exception: return False if not element: return False try: position = element.get_position() return (position.width != 0 or position.height != 0) except Exception: return False else: with suppress(Exception): tag_name = selector.split(":contains(")[0].split(" ")[-1] text = selector.split(":contains(")[1].split(")")[0][1:-1] self.loop.run_until_complete( self.page.select(tag_name, timeout=0.1) ) self.loop.run_until_complete(self.page.find(text, timeout=0.1)) return True return False def is_text_visible(self, text, selector="body"): selector = self.__convert_to_css_if_xpath(selector) text = text.strip() element = None try: element = self.find_element(selector, timeout=0.1) except Exception: return False with suppress(Exception): if text in element.text_all: return True return False def is_exact_text_visible(self, text, selector="body"): selector = self.__convert_to_css_if_xpath(selector) text = text.strip() element = None try: element = self.find_element(selector, timeout=0.1) except Exception: return False with suppress(Exception): if text == element.text_all.strip(): return True return False def wait_for_text(self, text, selector="body", timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) text = text.strip() element = None try: element = self.find_element(selector, timeout=timeout) except Exception: raise Exception("Element {%s} not found!" % selector) for i in range(int(timeout * 10)): with suppress(Exception): element = self.find_element(selector, timeout=0.1) if text in element.text_all: return True now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) raise Exception( "Text {%s} not found in {%s}! Actual text: {%s}" % (text, selector, element.text_all) ) def wait_for_text_not_visible(self, text, selector="body", timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT text = text.strip() start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) for i in range(int(timeout * 10)): if not self.is_text_visible(text, selector): return True now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) plural = "s" if timeout == 1: plural = "" raise Exception( "Text {%s} in {%s} was still visible after %s second%s!" % (text, selector, timeout, plural) ) def wait_for_element_visible(self, selector, timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: raise Exception("Element {%s} was not found!" % selector) for i in range(30): if self.is_element_visible(selector): return self.select(selector) time.sleep(0.1) raise Exception("Element {%s} was not visible!" % selector) def wait_for_element(self, selector, **kwargs): """Same as wait_for_element_visible()""" return self.wait_for_element_visible(selector, **kwargs) def wait_for_element_not_visible(self, selector, timeout=None): """Wait for element to not be visible on page. (May still be in DOM)""" if not timeout: timeout = settings.SMALL_TIMEOUT start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) for i in range(int(timeout * 10)): if not self.is_element_present(selector): return True elif not self.is_element_visible(selector): return True now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) plural = "s" if timeout == 1: plural = "" raise Exception( "Element {%s} was still visible after %s second%s!" % (selector, timeout, plural) ) def wait_for_element_absent(self, selector, timeout=None): """Wait for element to not be present in the DOM.""" if not timeout: timeout = settings.SMALL_TIMEOUT start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) for i in range(int(timeout * 10)): if not self.is_element_present(selector): return True now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) plural = "s" if timeout == 1: plural = "" raise Exception( "Element {%s} was still present after %s second%s!" % (selector, timeout, plural) ) def wait_for_any_of_elements_visible(self, *args, **kwargs): """Waits for at least one of the elements to be visible. Returns the first element that is found. The input is a list of elements. (Should be CSS selectors) Optional kwargs include: "timeout" (used by all selectors). Raises an exception if no elements are visible by the timeout. Examples: sb.cdp.wait_for_any_of_elements_visible("h1", "h2", "h3") OR sb.cdp.wait_for_any_of_elements_visible(["h1", "h2", "h3"]) """ selectors = [] timeout = None for kwarg in kwargs: if kwarg == "timeout": timeout = kwargs["timeout"] elif kwarg == "by": pass # Autodetected elif kwarg == "selector" or kwarg == "selectors": selector = kwargs[kwarg] if isinstance(selector, str): selectors.append(selector) elif isinstance(selector, list): selectors_list = selector for selector in selectors_list: if isinstance(selector, str): selectors.append(selector) else: raise Exception('Unknown kwarg: "%s"!' % kwarg) if not timeout: timeout = settings.SMALL_TIMEOUT for arg in args: if isinstance(arg, list): for selector in arg: if isinstance(selector, str): selectors.append(selector) elif isinstance(arg, str): selectors.append(arg) if not selectors: raise Exception("The selectors list was empty!") start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) any_present = False for i in range(int(timeout * 10)): for selector in selectors: if self.is_element_visible(selector): return self.select(selector) if self.is_element_present(selector): any_present = True now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) plural = "s" if timeout == 1: plural = "" if not any_present: # None of the elements exist in the HTML raise Exception( "None of the elements {%s} were present after %s second%s!" % ( str(selectors), timeout, plural, ) ) raise Exception( "None of the elements %s were visible after %s second%s!" % ( str(selectors), timeout, plural, ) ) def wait_for_any_of_elements_present(self, *args, **kwargs): """Waits for at least one of the elements to be present. Visibility not required, but element must be in the DOM. Returns the first element that is found. The input is a list of elements. (Should be CSS selectors) Optional kwargs include: "timeout" (used by all selectors). Raises an exception if no elements are present by the timeout. Examples: self.wait_for_any_of_elements_present("style", "script") OR self.wait_for_any_of_elements_present(["style", "script"]) """ selectors = [] timeout = None for kwarg in kwargs: if kwarg == "timeout": timeout = kwargs["timeout"] elif kwarg == "by": pass # Autodetected elif kwarg == "selector" or kwarg == "selectors": selector = kwargs[kwarg] if isinstance(selector, str): selectors.append(selector) elif isinstance(selector, list): selectors_list = selector for selector in selectors_list: if isinstance(selector, str): selectors.append(selector) else: raise Exception('Unknown kwarg: "%s"!' % kwarg) if not timeout: timeout = settings.SMALL_TIMEOUT for arg in args: if isinstance(arg, list): for selector in arg: if isinstance(selector, str): selectors.append(selector) elif isinstance(arg, str): selectors.append(arg) if not selectors: raise Exception("The selectors list was empty!") start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) for i in range(int(timeout * 10)): for selector in selectors: if self.is_element_present(selector): return self.select(selector) now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) plural = "s" if timeout == 1: plural = "" # None of the elements exist in the HTML raise Exception( "None of the elements %s were present after %s second%s!" % ( str(selectors), timeout, plural, ) ) def assert_any_of_elements_visible(self, *args, **kwargs): """Like wait_for_any_of_elements_visible(), but returns nothing.""" self.wait_for_any_of_elements_visible(*args, **kwargs) return True def assert_any_of_elements_present(self, *args, **kwargs): """Like wait_for_any_of_elements_present(), but returns nothing.""" self.wait_for_any_of_elements_present(*args, **kwargs) return True def assert_element(self, selector, timeout=None): """Same as assert_element_visible()""" self.assert_element_visible(selector, timeout=timeout) return True def assert_element_visible(self, selector, timeout=None): """Same as assert_element()""" if not timeout: timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: raise Exception("Element {%s} was not found!" % selector) for i in range(30): if self.is_element_visible(selector): return True time.sleep(0.1) raise Exception("Element {%s} was not visible!" % selector) def assert_element_present(self, selector, timeout=None): """Assert element is present in the DOM. (Visibility NOT required)""" if not timeout: timeout = settings.SMALL_TIMEOUT try: self.select(selector, timeout=timeout) except Exception: raise Exception("Element {%s} was not found!" % selector) return True def assert_element_absent(self, selector, timeout=None): """Assert element is not present in the DOM.""" self.wait_for_element_absent(selector, timeout=timeout) return True def assert_element_not_visible(self, selector, timeout=None): """Assert element is not visible on page. (May still be in DOM)""" self.wait_for_element_not_visible(selector, timeout=timeout) return True def assert_element_attribute(self, selector, attribute, value=None): attributes = self.get_element_attributes(selector) if attribute not in attributes: raise Exception( "Attribute {%s} was not found in element {%s}!" % (attribute, selector) ) if value and attributes[attribute] != value: raise Exception( "Expected value {%s} of attribute {%s} " "was not found in element {%s}! " "(Actual value was {%s})" % (value, attribute, selector, attributes[attribute]) ) def assert_title(self, title): expected = title.strip() actual = self.get_title().strip() error = ( "Expected page title [%s] does not match the actual title [%s]!" ) try: if expected != actual: raise Exception(error % (expected, actual)) except Exception: time.sleep(2) actual = self.get_title().strip() if expected != actual: raise Exception(error % (expected, actual)) def assert_title_contains(self, substring): expected = substring.strip() actual = self.get_title().strip() error = ( "Expected title substring [%s] does not appear " "in the actual page title [%s]!" ) try: if expected not in actual: raise Exception(error % (expected, actual)) except Exception: time.sleep(2) actual = self.get_title().strip() if expected not in actual: raise Exception(error % (expected, actual)) def assert_url(self, url): expected = url.strip() actual = self.get_current_url().strip() error = "Expected URL [%s] does not match the actual URL [%s]!" try: if expected != actual: raise Exception(error % (expected, actual)) except Exception: time.sleep(2) actual = self.get_current_url().strip() if expected != actual: raise Exception(error % (expected, actual)) def assert_url_contains(self, substring): expected = substring.strip() actual = self.get_current_url().strip() error = ( "Expected URL substring [%s] does not appear " "in the full URL [%s]!" ) try: if expected not in actual: raise Exception(error % (expected, actual)) except Exception: time.sleep(2) actual = self.get_current_url().strip() if expected not in actual: raise Exception(error % (expected, actual)) def assert_text(self, text, selector="body", timeout=None): """Same as wait_for_text()""" self.wait_for_text(text, selector=selector, timeout=timeout) return True def assert_exact_text(self, text, selector="body", timeout=None): if not timeout: timeout = settings.SMALL_TIMEOUT start_ms = time.time() * 1000.0 stop_ms = start_ms + (timeout * 1000.0) text = text.strip() element = None try: element = self.select(selector, timeout=timeout) except Exception: raise Exception("Element {%s} not found!" % selector) for i in range(int(timeout * 10)): with suppress(Exception): element = self.select(selector, timeout=0.1) if ( self.is_element_visible(selector) and text.strip() == element.text_all.strip() ): return True now_ms = time.time() * 1000.0 if now_ms >= stop_ms: break time.sleep(0.1) raise Exception( "Expected Text {%s}, is not equal to {%s} in {%s}!" % (text, element.text_all, selector) ) def assert_text_not_visible(self, text, selector="body", timeout=None): """Raises an exception if the text is still visible after timeout.""" self.wait_for_text_not_visible( text, selector=selector, timeout=timeout ) return True def assert_true(self, expression): if not expression: raise AssertionError("%s is not true" % expression) def assert_false(self, expression): if expression: raise AssertionError("%s is not false" % expression) def assert_equal(self, first, second): if first != second: raise AssertionError("%s is not equal to %s" % (first, second)) def assert_not_equal(self, first, second): if first == second: raise AssertionError("%s is equal to %s" % (first, second)) def assert_in(self, first, second): if first not in second: raise AssertionError("%s is not in %s" % (first, second)) def assert_not_in(self, first, second): if first in second: raise AssertionError("%s is in %s" % (first, second)) def scroll_into_view(self, selector): self.find_element(selector).scroll_into_view() self.loop.run_until_complete(self.page.wait(0.1)) def scroll_to_y(self, y): y = int(y) js_code = "window.scrollTo(0, %s);" % y with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) self.loop.run_until_complete(self.page.wait(0.1)) def scroll_by_y(self, y): y = int(y) js_code = "window.scrollBy(0, %s);" % y with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) self.loop.run_until_complete(self.page.wait(0.1)) def scroll_to_top(self): js_code = "window.scrollTo(0, 0);" with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) self.loop.run_until_complete(self.page.wait(0.1)) def scroll_to_bottom(self): js_code = "window.scrollTo(0, 10000);" with suppress(Exception): self.loop.run_until_complete(self.page.evaluate(js_code)) self.loop.run_until_complete(self.page.wait(0.1)) def scroll_up(self, amount=25): """Scrolls up as a percentage of the page.""" try: self.loop.run_until_complete(self.page.scroll_up(amount)) except Exception: amount = self.get_window_size()["height"] * amount / 100 self.execute_script("window.scrollBy(0, -%s);" % amount) self.loop.run_until_complete(self.page.wait(0.1)) def scroll_down(self, amount=25): """Scrolls down as a percentage of the page.""" try: self.loop.run_until_complete(self.page.scroll_down(amount)) except Exception: amount = self.get_window_size()["height"] * amount / 100 self.execute_script("window.scrollBy(0, %s);" % amount) self.loop.run_until_complete(self.page.wait(0.1)) def save_page_source(self, name, folder=None): from seleniumbase.core import log_helper if not name.endswith(".html"): name = name + ".html" if folder: abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, folder) if not os.path.exists(file_path): os.makedirs(file_path) html_file_path = os.path.join(file_path, name) else: html_file_path = name page_source = self.get_page_source() last_page = self.get_current_url() meta_charset = '<meta charset="utf-8">' rendered_source = "" if "://" in last_page: base_href_html = log_helper.get_base_href_html(last_page) if ' charset="' not in page_source: rendered_source = "%s\n%s\n%s" % ( base_href_html, meta_charset, page_source ) else: rendered_source = "%s\n%s" % (base_href_html, page_source) else: rendered_source = page_source html_file = open(html_file_path, mode="w+", encoding="utf-8") html_file.write(rendered_source) html_file.close() def save_as_html(self, *args, **kwargs): self.save_page_source(*args, **kwargs) def save_screenshot(self, name, folder=None, selector=None): filename = name if folder: filename = os.path.join(folder, name) if not selector: self.loop.run_until_complete( self.page.save_screenshot(filename) ) else: self.select(selector).save_screenshot(filename) def print_to_pdf(self, name, folder=None): filename = name if folder: filename = os.path.join(folder, name) self.loop.run_until_complete(self.page.print_to_pdf(filename)) def save_as_pdf(self, *args, **kwargs): self.print_to_pdf(*args, **kwargs) class Chrome(CDPMethods): def __init__(self, url=None, **kwargs): if not url: url = "about:blank" driver = cdp_util.start_sync(**kwargs) loop = asyncio.new_event_loop() page = loop.run_until_complete(driver.get(url)) wait_timeout = 30.0 if hasattr(sb_config, "_cdp_proxy") and sb_config._cdp_proxy: wait_timeout = 45.0 try: loop.run_until_complete( asyncio.wait_for(page.wait(), timeout=wait_timeout) ) except asyncio.TimeoutError: pass except Exception: pass super().__init__(loop, page, driver) ================================================ FILE: seleniumbase/core/sb_driver.py ================================================ """Add new methods to extend the driver""" from contextlib import suppress from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.remote.webelement import WebElement from seleniumbase.config import settings from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_actions from seleniumbase.fixtures import page_utils from seleniumbase.fixtures import shared_utils class DriverMethods(WebDriver): def __init__(self, driver): self.driver = driver if hasattr(driver, "session_id"): self.session_id = driver.session_id if hasattr(driver, "command_executor"): self.command_executor = driver.command_executor def __is_cdp_swap_needed(self): """If the driver is disconnected, use a CDP method when available.""" return shared_utils.is_cdp_swap_needed(self.driver) def find_element(self, by=None, value=None): if not value: value = by by = "css selector" elif not by: by = "css selector" else: value, by = page_utils.swap_selector_and_by_if_reversed(value, by) return self.driver.default_find_element(by=by, value=value) def find_elements(self, by=None, value=None): if not value: value = by by = "css selector" elif not by: by = "css selector" else: value, by = page_utils.swap_selector_and_by_if_reversed(value, by) return self.driver.default_find_elements(by=by, value=value) def add_cookie(self, *args, **kwargs): page_actions._reconnect_if_disconnected(self.driver) self.driver.default_add_cookie(*args, **kwargs) def get_cookie(self, *args, **kwargs): page_actions._reconnect_if_disconnected(self.driver) self.driver.default_get_cookie(*args, **kwargs) def delete_cookie(self, *args, **kwargs): page_actions._reconnect_if_disconnected(self.driver) self.driver.default_delete_cookie(*args, **kwargs) def back(self): if self.__is_cdp_swap_needed(): self.driver.cdp.go_back() return self.driver.default_back() def forward(self): if self.__is_cdp_swap_needed(): self.driver.cdp.go_forward() return self.driver.default_forward() def refresh(self, *args, **kwargs): if self.__is_cdp_swap_needed(): self.driver.cdp.refresh(*args, **kwargs) return self.driver.default_refresh() def locator(self, selector, by=None): if not by: by = "css selector" else: selector, by = page_utils.swap_selector_and_by_if_reversed( selector, by ) with suppress(Exception): return self.driver.default_find_element(by=by, value=selector) raise Exception('No such Element: {%s} (by="%s")!' % (selector, by)) def get_attribute(self, selector, attribute, by="css selector"): element = self.locator(selector, by=by) return element.get_attribute(attribute) def get_parent(self, element): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_parent(element) if isinstance(element, str): element = self.locator(element) return element.find_element(by="xpath", value="..") def get_current_url(self): if self.__is_cdp_swap_needed(): current_url = self.driver.cdp.get_current_url() else: current_url = self.driver.current_url return current_url def get_page_source(self): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_page_source() return self.driver.page_source def get_title(self): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_title() return self.driver.title def open_url(self, *args, **kwargs): page_actions.open_url(self.driver, *args, **kwargs) def click(self, *args, **kwargs): page_actions.click(self.driver, *args, **kwargs) def click_link(self, *args, **kwargs): page_actions.click_link(self.driver, *args, **kwargs) def click_if_visible(self, *args, **kwargs): page_actions.click_if_visible(self.driver, *args, **kwargs) def click_active_element(self, *args, **kwargs): page_actions.click_active_element(self.driver, *args, **kwargs) def send_keys(self, *args, **kwargs): page_actions.send_keys(self.driver, *args, **kwargs) def press_keys(self, *args, **kwargs): page_actions.press_keys(self.driver, *args, **kwargs) def update_text(self, *args, **kwargs): page_actions.update_text(self.driver, *args, **kwargs) def submit(self, *args, **kwargs): page_actions.submit(self.driver, *args, **kwargs) def assert_element_visible(self, *args, **kwargs): page_actions.assert_element_visible(self.driver, *args, **kwargs) def assert_element_present(self, *args, **kwargs): page_actions.assert_element_present(self.driver, *args, **kwargs) def assert_element_not_visible(self, *args, **kwargs): page_actions.assert_element_not_visible(self.driver, *args, **kwargs) def assert_text(self, *args, **kwargs): page_actions.assert_text(self.driver, *args, **kwargs) def assert_exact_text(self, *args, **kwargs): page_actions.assert_exact_text(self.driver, *args, **kwargs) def assert_non_empty_text(self, *args, **kwargs): return page_actions.assert_non_empty_text( self.driver, *args, **kwargs ) def assert_text_not_visible(self, *args, **kwargs): return page_actions.assert_text_not_visible( self.driver, *args, **kwargs ) def wait_for_element(self, *args, **kwargs): return page_actions.wait_for_element(self.driver, *args, **kwargs) def wait_for_element_visible(self, *args, **kwargs): return page_actions.wait_for_element(self.driver, *args, **kwargs) def wait_for_element_present(self, *args, **kwargs): return page_actions.wait_for_selector(self.driver, *args, **kwargs) def wait_for_element_absent(self, *args, **kwargs): return page_actions.wait_for_element_absent( self.driver, *args, **kwargs ) def wait_for_element_not_visible(self, *args, **kwargs): return page_actions.wait_for_element_not_visible( self.driver, *args, **kwargs ) def wait_for_selector(self, *args, **kwargs): return page_actions.wait_for_selector(self.driver, *args, **kwargs) def wait_for_text(self, *args, **kwargs): return page_actions.wait_for_text(self.driver, *args, **kwargs) def wait_for_exact_text(self, *args, **kwargs): return page_actions.wait_for_exact_text(self.driver, *args, **kwargs) def wait_for_non_empty_text(self, *args, **kwargs): return page_actions.wait_for_non_empty_text( self.driver, *args, **kwargs ) def wait_for_text_not_visible(self, *args, **kwargs): return page_actions.wait_for_text_not_visible( self.driver, *args, **kwargs ) def wait_for_and_accept_alert(self, *args, **kwargs): return page_actions.wait_for_and_accept_alert( self.driver, *args, **kwargs ) def wait_for_and_dismiss_alert(self, *args, **kwargs): return page_actions.wait_for_and_dismiss_alert( self.driver, *args, **kwargs ) def is_element_present(self, *args, **kwargs): return page_actions.is_element_present(self.driver, *args, **kwargs) def is_element_visible(self, *args, **kwargs): return page_actions.is_element_visible(self.driver, *args, **kwargs) def is_text_visible(self, *args, **kwargs): return page_actions.is_text_visible(self.driver, *args, **kwargs) def is_exact_text_visible(self, *args, **kwargs): return page_actions.is_exact_text_visible(self.driver, *args, **kwargs) def is_attribute_present(self, *args, **kwargs): return page_actions.has_attribute(self.driver, *args, **kwargs) def is_non_empty_text_visible(self, *args, **kwargs): return page_actions.is_non_empty_text_visible( self.driver, *args, **kwargs ) def is_valid_url(self, url): """Return True if the url is a valid url.""" return page_utils.is_valid_url(url) def is_alert_present(self): try: self.driver.switch_to.alert return True except Exception: return False def is_online(self): if self.__is_cdp_swap_needed(): return self.driver.cdp.evaluate("navigator.onLine;") return self.driver.execute_script("return navigator.onLine;") def is_connected(self): """ Return True if WebDriver is connected to the browser. Note that the stealthy CDP-Driver isn't a WebDriver. In CDP Mode, the CDP-Driver controls the web browser. The CDP-Driver can be connected while WebDriver isn't. """ if shared_utils.is_windows(): return ( not hasattr(self.driver, "_is_connected") or self.driver._is_connected ) try: self.driver.window_handles return True except Exception: return False def is_uc_mode_active(self): """Return True if the driver is using UC Mode. False otherwise.""" return ( hasattr(self.driver, "_is_using_uc") and self.driver._is_using_uc ) def is_cdp_mode_active(self): """CDP Mode is a special mode within UC Mode. Activated separately. Return True if CDP Mode is loaded in the driver. False otherwise.""" return ( hasattr(self.driver, "_is_using_cdp") and self.driver._is_using_cdp ) def js_click(self, *args, **kwargs): return page_actions.js_click(self.driver, *args, **kwargs) def get_text(self, *args, **kwargs): return page_actions.get_text(self.driver, *args, **kwargs) def get_active_element_css(self, *args, **kwargs): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_active_element_css() return js_utils.get_active_element_css(self.driver, *args, **kwargs) def get_locale_code(self, *args, **kwargs): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_locale_code() return js_utils.get_locale_code(self.driver, *args, **kwargs) def get_screen_rect(self, *args, **kwargs): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_screen_rect() return js_utils.get_screen_rect(self.driver, *args, **kwargs) def get_origin(self, *args, **kwargs): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_origin() return js_utils.get_origin(self.driver, *args, **kwargs) def get_user_agent(self, *args, **kwargs): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_user_agent() return js_utils.get_user_agent(self.driver, *args, **kwargs) def get_cookie_string(self, *args, **kwargs): if self.__is_cdp_swap_needed(): return self.driver.cdp.get_cookie_string() return js_utils.get_cookie_string(self.driver, *args, **kwargs) def highlight(self, *args, **kwargs): if self.__is_cdp_swap_needed(): selector = None if "selector" in kwargs: selector = kwargs["selector"] else: selector = args[0] self.driver.cdp.highlight(selector) return if "scroll" in kwargs: kwargs.pop("scroll") w_args = kwargs.copy() if "loops" in w_args: w_args.pop("loops") element = page_actions.wait_for_element(self.driver, *args, **w_args) browser = self.driver.capabilities["browserName"].lower() js_utils.slow_scroll_to_element(self.driver, element, browser) if "timeout" in kwargs: kwargs.pop("timeout") js_utils.highlight(self.driver, *args, **kwargs) def highlight_click(self, *args, **kwargs): self.highlight(*args, **kwargs) if "loops" in kwargs: kwargs.pop("loops") if "scroll" in kwargs: kwargs.pop("scroll") page_actions.click(self.driver, *args, **kwargs) def highlight_if_visible( self, selector, by="css selector", loops=4, scroll=True ): if self.is_element_visible(selector, by=by): self.highlight(selector, by=by, loops=loops, scroll=scroll) def switch_to_default_window(self): self.driver.switch_to.window(self.driver.window_handles[0]) def switch_to_newest_window(self): self.driver.switch_to.window(self.driver.window_handles[-1]) def open_new_window(self, switch_to=True): if switch_to: try: self.driver.switch_to.new_window("tab") except Exception: self.driver.execute_script("window.open('');") self.switch_to_newest_window() else: self.driver.execute_script("window.open('');") def open_new_tab(self, switch_to=True): self.open_new_window(switch_to=switch_to) def switch_to_window(self, *args, **kwargs): page_actions.switch_to_window(self.driver, *args, **kwargs) def switch_to_tab(self, *args, **kwargs): self.switch_to_window(*args, **kwargs) def switch_to_frame(self, frame="iframe"): if isinstance(frame, WebElement): self.driver.switch_to.frame(frame) else: iframe = self.locator(frame) self.driver.switch_to.frame(iframe) def reset_window_size(self): if self.__is_cdp_swap_needed(): self.driver.cdp.reset_window_size() return x = settings.WINDOW_START_X y = settings.WINDOW_START_Y width = settings.CHROME_START_WIDTH height = settings.CHROME_START_HEIGHT self.driver.set_window_rect(x, y, width, height) def set_wire_proxy(self, string): """Set a proxy server for selenium-wire mode ("--wire") Examples: (ONLY avilable if using selenium-wire mode!) driver.set_wire_proxy("SERVER:PORT") driver.set_wire_proxy("socks5://SERVER:PORT") driver.set_wire_proxy("USERNAME:PASSWORD@SERVER:PORT") """ the_http = "http" the_https = "https" if string.startswith("socks4://"): the_http = "socks4" the_https = "socks4" elif string.startswith("socks5://"): the_http = "socks5" the_https = "socks5" string = string.split("//")[-1] if hasattr(self.driver, "proxy"): self.driver.proxy = { "http": "%s://%s" % (the_http, string), "https": "%s://%s" % (the_https, string), "no_proxy": "localhost,127.0.0.1", } ================================================ FILE: seleniumbase/core/session_helper.py ================================================ from seleniumbase import config as sb_config def end_reused_class_session_as_needed(): if ( getattr(sb_config, "reuse_class_session", None) and getattr(sb_config, "shared_driver", None) ): if ( hasattr(sb_config.shared_driver, "service") and sb_config.shared_driver.service.process ): try: sb_config.shared_driver.quit() except Exception: sb_config.shared_driver = None ================================================ FILE: seleniumbase/core/settings_parser.py ================================================ import re from seleniumbase.config import settings def set_settings(settings_file): if not settings_file.endswith(".py"): raise Exception("\n\n`%s` is not a Python file!\n\n" % settings_file) f = open(settings_file, "r") all_code = f.read() f.close() override_settings = {} num_settings = 0 code_lines = all_code.split("\n") for line in code_lines: # KEY = "VALUE" data = re.match(r'^\s*([\S]+)\s*=\s*"([\S\s]+)"\s*$', line) if data: key = data.group(1) value = '"' + data.group(2) + '"' override_settings[key] = value num_settings += 1 continue # KEY = 'VALUE' data = re.match(r"^\s*([\S]+)\s*=\s*'([\S\s]+)'\s*$", line) if data: key = data.group(1) value = "'" + data.group(2) + "'" override_settings[key] = value num_settings += 1 continue # KEY = VALUE data = re.match(r"^\s*([\S]+)\s*=\s*([\S]+)\s*$", line) if data: key = data.group(1) value = data.group(2) override_settings[key] = value num_settings += 1 continue for key in override_settings.keys(): value = override_settings[key] if value.replace(".", "1").isdigit(): if value.count(".") == 1: override_settings[key] = float(value) elif value.count(".") == 0: override_settings[key] = int(value) else: continue elif value == "True": override_settings[key] = True elif value == "False": override_settings[key] = False elif len(value) > 1 and value.startswith('"') and value.endswith('"'): override_settings[key] = value[1:-1] elif len(value) > 1 and value.startswith("'") and value.endswith("'"): override_settings[key] = value[1:-1] else: continue if key == "MINI_TIMEOUT": settings.MINI_TIMEOUT = override_settings[key] elif key == "SMALL_TIMEOUT": settings.SMALL_TIMEOUT = override_settings[key] elif key == "LARGE_TIMEOUT": settings.LARGE_TIMEOUT = override_settings[key] elif key == "EXTREME_TIMEOUT": settings.EXTREME_TIMEOUT = override_settings[key] elif key == "PAGE_LOAD_TIMEOUT": settings.PAGE_LOAD_TIMEOUT = override_settings[key] elif key == "ARCHIVE_EXISTING_LOGS": settings.ARCHIVE_EXISTING_LOGS = override_settings[key] elif key == "ARCHIVE_EXISTING_DOWNLOADS": settings.ARCHIVE_EXISTING_DOWNLOADS = override_settings[key] elif key == "SCREENSHOT_WITH_BACKGROUND": settings.SCREENSHOT_WITH_BACKGROUND = override_settings[key] elif key == "SCREENSHOT_NAME": settings.SCREENSHOT_NAME = override_settings[key] elif key == "BASIC_INFO_NAME": settings.BASIC_INFO_NAME = override_settings[key] elif key == "PAGE_SOURCE_NAME": settings.PAGE_SOURCE_NAME = override_settings[key] elif key == "LATEST_REPORT_DIR": settings.LATEST_REPORT_DIR = override_settings[key] elif key == "REPORT_ARCHIVE_DIR": settings.REPORT_ARCHIVE_DIR = override_settings[key] elif key == "HTML_REPORT": settings.HTML_REPORT = override_settings[key] elif key == "RESULTS_TABLE": settings.RESULTS_TABLE = override_settings[key] elif key == "SWITCH_TO_NEW_TABS_ON_CLICK": settings.SWITCH_TO_NEW_TABS_ON_CLICK = override_settings[key] elif key == "WAIT_FOR_RSC_ON_PAGE_LOADS": settings.WAIT_FOR_RSC_ON_PAGE_LOADS = override_settings[key] elif key == "WAIT_FOR_RSC_ON_CLICKS": settings.WAIT_FOR_RSC_ON_CLICKS = override_settings[key] elif key == "WAIT_FOR_ANGULARJS": settings.WAIT_FOR_ANGULARJS = override_settings[key] elif key == "DEFAULT_DEMO_MODE_TIMEOUT": settings.DEFAULT_DEMO_MODE_TIMEOUT = override_settings[key] elif key == "HIGHLIGHTS": settings.HIGHLIGHTS = override_settings[key] elif key == "DEFAULT_MESSAGE_DURATION": settings.DEFAULT_MESSAGE_DURATION = override_settings[key] elif key == "DISABLE_CSP_ON_FIREFOX": settings.DISABLE_CSP_ON_FIREFOX = override_settings[key] elif key == "DISABLE_CSP_ON_CHROME": settings.DISABLE_CSP_ON_CHROME = override_settings[key] elif key == "RAISE_INVALID_PROXY_STRING_EXCEPTION": settings.RAISE_INVALID_PROXY_STRING_EXCEPTION = override_settings[ key ] elif key == "WINDOW_START_X": settings.WINDOW_START_X = override_settings[key] elif key == "WINDOW_START_Y": settings.WINDOW_START_Y = override_settings[key] elif key == "CHROME_START_WIDTH": settings.CHROME_START_WIDTH = override_settings[key] elif key == "CHROME_START_HEIGHT": settings.CHROME_START_HEIGHT = override_settings[key] elif key == "HEADLESS_START_WIDTH": settings.HEADLESS_START_WIDTH = override_settings[key] elif key == "HEADLESS_START_HEIGHT": settings.HEADLESS_START_HEIGHT = override_settings[key] elif key == "HIDE_DRIVER_DOWNLOADS": settings.HIDE_DRIVER_DOWNLOADS = override_settings[key] elif key == "MASTERQA_DEFAULT_VALIDATION_MESSAGE": settings.MASTERQA_DEFAULT_VALIDATION_MESSAGE = override_settings[ key ] elif key == "MASTERQA_WAIT_TIME_BEFORE_VERIFY": settings.MASTERQA_WAIT_TIME_BEFORE_VERIFY = override_settings[key] elif key == "MASTERQA_START_IN_FULL_SCREEN_MODE": settings.MASTERQA_START_IN_FULL_SCREEN_MODE = override_settings[ key ] elif key == "MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT": settings.MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT = override_settings[ key ] elif key == "TOTP_KEY": settings.TOTP_KEY = override_settings[key] elif key == "DB_HOST": settings.DB_HOST = override_settings[key] elif key == "DB_PORT": settings.DB_PORT = override_settings[key] elif key == "DB_USERNAME": settings.DB_USERNAME = override_settings[key] elif key == "DB_PASSWORD": settings.DB_PASSWORD = override_settings[key] elif key == "DB_SCHEMA": settings.DB_SCHEMA = override_settings[key] elif key == "S3_LOG_BUCKET": settings.S3_LOG_BUCKET = override_settings[key] elif key == "S3_BUCKET_URL": settings.S3_BUCKET_URL = override_settings[key] elif key == "S3_SELENIUM_ACCESS_KEY": settings.S3_SELENIUM_ACCESS_KEY = override_settings[key] elif key == "S3_SELENIUM_SECRET_KEY": settings.S3_SELENIUM_SECRET_KEY = override_settings[key] elif key == "ENCRYPTION_KEY": settings.ENCRYPTION_KEY = override_settings[key] elif key == "OBFUSCATION_START_TOKEN": settings.OBFUSCATION_START_TOKEN = override_settings[key] elif key == "OBFUSCATION_END_TOKEN": settings.OBFUSCATION_END_TOKEN = override_settings[key] else: continue if num_settings == 0: raise Exception("Unable to parse the settings file!") return override_settings ================================================ FILE: seleniumbase/core/style_sheet.py ================================================ import textwrap from seleniumbase.fixtures import constants class Saved: # Storing data to prevent extra loading pass def get_report_style(): if hasattr(Saved, "report_style"): return Saved.report_style # Uses caching to prevent extra method calls REPORT_FAVICON = constants.Report.get_favicon() title = """<meta id="OGTitle" property="og:title" content="SeleniumBase"> <title>Test Report """ % REPORT_FAVICON style = ( title + """""" ) style = textwrap.dedent(style) Saved.report_style = style return style def get_bt_backdrop_style(): # Bootstrap Tour Backdrop Style if hasattr(Saved, "bt_backdrop_style"): return Saved.bt_backdrop_style bt_backdrop_style = """ .tour-tour-element { pointer-events: none !important; } :not(.tour-tour-element) .orphan.tour-tour { box-shadow: 0 0 0 88422px rgba(0, 0, 0, 0.42); pointer-events: auto !important; }""" bt_backdrop_style = textwrap.dedent(bt_backdrop_style) Saved.bt_backdrop_style = bt_backdrop_style return bt_backdrop_style def get_dt_backdrop_style(): # DriverJS Tour Backdrop Style if hasattr(Saved, "dt_backdrop_style"): return Saved.dt_backdrop_style dt_backdrop_style = """ .driver-fix-stacking { pointer-events: none !important; } #driver-popover-item, .popover-class { pointer-events: auto !important; } button.driver-prev-btn.driver-disabled { visibility: hidden; }""" dt_backdrop_style = textwrap.dedent(dt_backdrop_style) Saved.dt_backdrop_style = dt_backdrop_style return dt_backdrop_style def get_messenger_style(): if hasattr(Saved, "messenger_style"): return Saved.messenger_style font_family = '"open-sans",Arial,sans-serif !important' messenger_style = """ .messenger-message-inner { font-family: %s; font-size: 17px; } ul.messenger-theme-flat, ul.messenger-theme-future { box-shadow: 2px 2px 9px 4px rgba(32, 142, 120, 0.28), 2px 2px 9px 4px rgba(200, 240, 80, 0.34) !important; }""" % font_family messenger_style = textwrap.dedent(messenger_style) Saved.messenger_style = messenger_style return messenger_style def get_sh_style_test(): if hasattr(Saved, "sh_style_test"): return Saved.sh_style_test sh_style_test = """ var test_tour = new Shepherd.Tour({ defaults: { classes: 'shepherd-theme-dark', scrollTo: true } });""" sh_style_test = textwrap.dedent(sh_style_test) Saved.sh_style_test = sh_style_test return sh_style_test def get_hops_backdrop_style(): # Hopscotch Backdrop Style if hasattr(Saved, "hops_backdrop_style"): return Saved.hops_backdrop_style hops_backdrop_style = """ .hopscotch-bubble-container { font-size: 110%; }""" hops_backdrop_style = textwrap.dedent(hops_backdrop_style) Saved.hops_backdrop_style = hops_backdrop_style return hops_backdrop_style def get_introjs_style(): # IntroJS Style if hasattr(Saved, "introjs_style"): return Saved.introjs_style introjs_style = """ .introjs-button.introjs-nextbutton, .introjs-button.introjs-donebutton { color: #fff !important; background-color: %s !important; border: 1px solid %s !important; text-shadow: none; box-shadow: none; } .introjs-button.introjs-nextbutton:hover, .introjs-button.introjs-donebutton:hover { color: #fff !important; background-color: %s !important; border: 1px solid %s !important; } .introjs-button { box-sizing: content-box; text-decoration: none; } .introjs-button.introjs-skipbutton { color: %s; } .introjs-tooltip, .introjs-floating { box-sizing: content-box; position: absolute; }""" introjs_style = textwrap.dedent(introjs_style) Saved.introjs_style = introjs_style return introjs_style def get_sh_backdrop_style(): # Shepherd Backdrop Style if hasattr(Saved, "sh_backdrop_style"): return Saved.sh_backdrop_style sh_backdrop_style = """ body.shepherd-active .shepherd-target.shepherd-enabled { box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.20); pointer-events: none !important; z-index: 9999; } body.shepherd-active .shepherd-orphan { box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.20); pointer-events: auto; z-index: 9999; } body.shepherd-active .shepherd-enabled.shepherd-element-attached-top { position: relative; } body.shepherd-active .shepherd-enabled.shepherd-element-attached-bottom { position: relative; } body.shepherd-active .shepherd-step { pointer-events: auto; z-index: 9999; } body.shepherd-active { pointer-events: none !important; }""" sh_backdrop_style = textwrap.dedent(sh_backdrop_style) Saved.sh_backdrop_style = sh_backdrop_style return sh_backdrop_style def get_pytest_style(): # pytest html-report Style if hasattr(Saved, "pytest_style"): return Saved.pytest_style pytest_style = """ body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; min-width: 800px; color: #999; } h1 { font-size: 24px; color: black; } h2 { font-size: 16px; color: black; } p { color: black; } a { color: #999; } table { border-collapse: collapse; } #environment td { padding: 5px; border: 1px solid #E6E6E6; } #environment tr:nth-child(odd) { background-color: #f6f6f6; } span.passed, .passed .col-result { color: green; } span.skipped, span.xfailed, span.rerun, .skipped .col-result, .xfailed .col-result, .rerun .col-result { color: orange; } span.error, span.failed, span.xpassed, .error .col-result, .failed .col-result, .xpassed .col-result { color: red; } #results-table { border: 1px solid #e6e6e6; color: #999; font-size: 12px; width: 100% } #results-table th, #results-table td { padding: 5px; border: 1px solid #E6E6E6; text-align: left } #results-table th { font-weight: bold } .log:only-child { height: inherit } .log { background-color: #e6e6e6; border: 1px solid #e6e6e6; color: black; display: block; font-family: "Courier New", Courier, monospace; height: 230px; overflow-y: scroll; padding: 5px; white-space: pre-wrap } div.image { border: 1px solid #e6e6e6; float: right; height: 240px; margin-left: 5px; overflow: hidden; width: 320px } div.image img { width: 320px } .collapsed { display: none; } .expander::after { content: " (show details)"; color: #BBB; font-style: italic; cursor: pointer; } .collapser::after { content: " (hide details)"; color: #BBB; font-style: italic; cursor: pointer; } .sortable { cursor: pointer; } .sort-icon { font-size: 0px; float: left; margin-right: 5px; margin-top: 5px; width: 0; height: 0; border-left: 8px solid transparent; border-right: 8px solid transparent; } .inactive .sort-icon { border-top: 8px solid #E6E6E6; } .asc.active .sort-icon { border-bottom: 8px solid #999; } .desc.active .sort-icon { border-top: 8px solid #999; }""" pytest_style = textwrap.dedent(pytest_style) Saved.pytest_style = pytest_style return pytest_style ================================================ FILE: seleniumbase/core/testcase_manager.py ================================================ from seleniumbase.core.mysql import DatabaseManager class TestcaseManager: def __init__(self, database_env): self.database_env = database_env def insert_execution_data(self, execution_query_payload): """Inserts a test execution row into the database. Returns the execution guid. "execution_start_time" is defined by milliseconds since the Epoch. (See https://currentmillis.com to convert that to a real date.)""" query = """INSERT INTO test_execution (guid, execution_start, total_execution_time, username) VALUES (%(guid)s,%(execution_start_time)s, %(total_execution_time)s,%(username)s)""" DatabaseManager(self.database_env).execute_query( query, execution_query_payload.get_params() ) return execution_query_payload.guid def update_execution_data(self, execution_guid, execution_time): """Updates an existing test execution row in the database.""" query = """UPDATE test_execution SET total_execution_time=%(execution_time)s WHERE guid=%(execution_guid)s """ DatabaseManager(self.database_env).execute_query( query, { "execution_guid": execution_guid, "execution_time": execution_time, }, ) def insert_testcase_data(self, testcase_run_payload): """Inserts all data for the test in the DB. Returns new row guid.""" query = """INSERT INTO test_run_data( guid, browser, state, execution_guid, env, start_time, test_address, runtime, retry_count, message, stack_trace) VALUES ( %(guid)s, %(browser)s, %(state)s, %(execution_guid)s, %(env)s, %(start_time)s, %(test_address)s, %(runtime)s, %(retry_count)s, %(message)s, %(stack_trace)s) """ DatabaseManager(self.database_env).execute_query( query, testcase_run_payload.get_params() ) def update_testcase_data(self, testcase_payload): """Updates an existing test run in the database.""" query = """UPDATE test_run_data SET runtime=%(runtime)s, state=%(state)s, retry_count=%(retry_count)s, stack_trace=%(stack_trace)s, message=%(message)s WHERE guid=%(guid)s """ DatabaseManager(self.database_env).execute_query( query, testcase_payload.get_params() ) def update_testcase_log_url(self, testcase_payload): query = """UPDATE test_run_data SET log_url=%(log_url)s WHERE guid=%(guid)s """ DatabaseManager(self.database_env).execute_query( query, testcase_payload.get_params() ) class ExecutionQueryPayload: def __init__(self): self.execution_start_time = None self.total_execution_time = -1 self.username = "Default" self.guid = None def get_params(self): return { "execution_start_time": self.execution_start_time, "total_execution_time": self.total_execution_time, "username": self.username, "guid": self.guid, } class TestcaseDataPayload: def __init__(self): self.guid = None self.test_address = None self.browser = None self.state = None self.execution_guid = None self.env = None self.start_time = None self.runtime = None self.retry_count = 0 self.stack_trace = None self.message = None self.log_url = None def get_params(self): return { "guid": self.guid, "test_address": self.test_address, "browser": self.browser, "state": self.state, "execution_guid": self.execution_guid, "env": self.env, "start_time": self.start_time, "runtime": self.runtime, "retry_count": self.retry_count, "stack_trace": self.stack_trace, "message": self.message, "log_url": self.log_url, } ================================================ FILE: seleniumbase/core/tour_helper.py ================================================ """This module contains methods for running website tours. These helper methods SHOULD NOT be called directly from tests.""" import os import re import textwrap import time from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import style_sheet from seleniumbase.fixtures import constants from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_actions EXPORTED_TOURS_FOLDER = constants.Tours.EXPORTED_TOURS_FOLDER def activate_bootstrap(driver): """Allows you to use Bootstrap Tours with SeleniumBase http://bootstraptour.com/ """ bootstrap_tour_css = constants.BootstrapTour.MIN_CSS bootstrap_tour_js = constants.BootstrapTour.MIN_JS verify_script = """// Verify Bootstrap Tour activated var tour2 = new Tour({ });""" backdrop_style = style_sheet.get_bt_backdrop_style() js_utils.add_css_style(driver, backdrop_style) js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) for x in range(4): js_utils.activate_jquery(driver) js_utils.add_css_link(driver, bootstrap_tour_css) js_utils.add_js_link(driver, bootstrap_tour_js) time.sleep(0.1) for x in range(int(settings.MINI_TIMEOUT * 2.0)): # Bootstrap needs a small amount of time to load & activate. try: driver.execute_script(verify_script) time.sleep(0.05) return except Exception: time.sleep(0.15) js_utils.raise_unable_to_load_jquery_exception(driver) def is_bootstrap_activated(driver): verify_script = """// Verify Bootstrap Tour activated var tour2 = new Tour({ });""" try: driver.execute_script(verify_script) return True except Exception: return False def activate_driverjs(driver): """Allows you to use DriverJS Tours with SeleniumBase https://kamranahmed.info/driver.js/ """ backdrop_style = style_sheet.get_dt_backdrop_style() driverjs_css = constants.DriverJS.MIN_CSS driverjs_js = constants.DriverJS.MIN_JS verify_script = """// Verify DriverJS activated var driverjs2 = Driver.name; """ js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) js_utils.add_css_style(driver, backdrop_style) for x in range(4): js_utils.activate_jquery(driver) js_utils.add_css_link(driver, driverjs_css) js_utils.add_js_link(driver, driverjs_js) time.sleep(0.1) for x in range(int(settings.MINI_TIMEOUT * 2.0)): # DriverJS needs a small amount of time to load & activate. try: driver.execute_script(verify_script) js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) time.sleep(0.05) return except Exception: time.sleep(0.15) js_utils.raise_unable_to_load_jquery_exception(driver) def is_driverjs_activated(driver): verify_script = """// Verify DriverJS activated var driverjs2 = Driver.name; """ try: driver.execute_script(verify_script) return True except Exception: return False def activate_hopscotch(driver): """Allows you to use Hopscotch Tours with SeleniumBase http://linkedin.github.io/hopscotch/ """ hopscotch_css = constants.Hopscotch.MIN_CSS hopscotch_js = constants.Hopscotch.MIN_JS backdrop_style = style_sheet.get_hops_backdrop_style() verify_script = """// Verify Hopscotch activated var hops = hopscotch.isActive; """ js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) js_utils.add_css_style(driver, backdrop_style) for x in range(4): js_utils.activate_jquery(driver) js_utils.add_css_link(driver, hopscotch_css) js_utils.add_js_link(driver, hopscotch_js) time.sleep(0.1) for x in range(int(settings.MINI_TIMEOUT * 2.0)): # Hopscotch needs a small amount of time to load & activate. try: driver.execute_script(verify_script) js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) time.sleep(0.05) return except Exception: time.sleep(0.15) js_utils.raise_unable_to_load_jquery_exception(driver) def is_hopscotch_activated(driver): verify_script = """// Verify Hopscotch activated var hops = hopscotch.isActive; """ try: driver.execute_script(verify_script) return True except Exception: return False def activate_introjs(driver): """Allows you to use IntroJS Tours with SeleniumBase https://introjs.com/ """ intro_css = constants.IntroJS.MIN_CSS intro_js = constants.IntroJS.MIN_JS theme_color = sb_config.introjs_theme_color hover_color = sb_config.introjs_hover_color backdrop_style = style_sheet.get_introjs_style() % ( theme_color, hover_color, hover_color, hover_color, theme_color, ) verify_script = """// Verify IntroJS activated var intro2 = introJs(); """ js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) js_utils.add_css_style(driver, backdrop_style) for x in range(4): js_utils.activate_jquery(driver) js_utils.add_css_link(driver, intro_css) js_utils.add_js_link(driver, intro_js) time.sleep(0.1) for x in range(int(settings.MINI_TIMEOUT * 2.0)): # IntroJS needs a small amount of time to load & activate. try: driver.execute_script(verify_script) js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) time.sleep(0.05) return except Exception: time.sleep(0.15) js_utils.raise_unable_to_load_jquery_exception(driver) def is_introjs_activated(driver): verify_script = """// Verify IntroJS activated var intro2 = introJs(); """ try: driver.execute_script(verify_script) return True except Exception: return False def activate_shepherd(driver): """Allows you to use Shepherd Tours with SeleniumBase https://cdnjs.com/libraries/shepherd/1.8.1 """ shepherd_js = constants.Shepherd.MIN_JS sh_theme_arrows_css = constants.Shepherd.THEME_ARROWS_CSS sh_theme_arrows_fix_css = constants.Shepherd.THEME_ARR_FIX_CSS sh_theme_default_css = constants.Shepherd.THEME_DEFAULT_CSS sh_theme_dark_css = constants.Shepherd.THEME_DARK_CSS sh_theme_sq_css = constants.Shepherd.THEME_SQ_CSS sh_theme_sq_dark_css = constants.Shepherd.THEME_SQ_DK_CSS tether_js = constants.Tether.MIN_JS spinner_css = constants.Messenger.SPINNER_CSS sh_style = style_sheet.get_sh_style_test() backdrop_style = style_sheet.get_sh_backdrop_style() js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) js_utils.add_css_style(driver, backdrop_style) js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) for x in range(4): js_utils.add_css_link(driver, spinner_css) js_utils.add_css_link(driver, sh_theme_arrows_css) js_utils.add_css_link(driver, sh_theme_arrows_fix_css) js_utils.add_css_link(driver, sh_theme_default_css) js_utils.add_css_link(driver, sh_theme_dark_css) js_utils.add_css_link(driver, sh_theme_sq_css) js_utils.add_css_link(driver, sh_theme_sq_dark_css) js_utils.add_js_link(driver, tether_js) js_utils.add_js_link(driver, shepherd_js) time.sleep(0.1) for x in range(int(settings.MINI_TIMEOUT * 2.0)): # Shepherd needs a small amount of time to load & activate. try: driver.execute_script(sh_style) # Verify Shepherd has loaded js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) driver.execute_script(sh_style) # Need it twice for ordering js_utils.wait_for_ready_state_complete(driver) js_utils.wait_for_angularjs(driver) time.sleep(0.05) return except Exception: time.sleep(0.15) js_utils.raise_unable_to_load_jquery_exception(driver) def is_shepherd_activated(driver): sh_style = style_sheet.get_sh_style_test() try: driver.execute_script(sh_style) # Verify Shepherd has loaded return True except Exception: return False def play_shepherd_tour(driver, tour_steps, msg_dur, name=None, interval=0): """Plays a Shepherd tour on the current website.""" instructions = "" for tour_step in tour_steps[name]: instructions += tour_step instructions += """ // Start the tour tour.start(); $tour = tour;""" extra = """ document.body.addEventListener('keyup', function (event) { if (event.key === 'PageUp' || event.key === 'ArrowLeft') { Shepherd.activeTour.back(); } if (event.key === 'PageDown' || event.key === 'ArrowRight') { Shepherd.activeTour.next(); } })""" autoplay = False if interval and interval > 0: autoplay = True interval = float(interval) if interval < 0.5: interval = 0.5 if not is_shepherd_activated(driver): instructions += extra activate_shepherd(driver) if len(tour_steps[name]) > 1: try: selector = re.search( r"[\S\s]+{element: '([\S\s]+)', on: [\S\s]+", tour_steps[name][1], ).group(1) selector = selector.replace("\\", "") page_actions.wait_for_element_present( driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT, ) except Exception: js_utils.post_messenger_error_message( driver, "Tour Error: {'%s'} was not found!" % selector, msg_dur ) raise Exception( "Tour Error: {'%s'} was not found! " "Exiting due to failure on first tour step!" "" % selector ) driver.execute_script(instructions) try: page_actions.wait_for_element_visible( driver, "a.tour-button-right", by="css selector", timeout=1.2 ) except Exception: pass try: driver.execute_script('document.activeElement.blur();') except Exception: pass tour_on = True if autoplay: start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) latest_element = None latest_text = None while tour_on: try: time.sleep(0.01) result = driver.execute_script( "return Shepherd.activeTour.currentStep.isOpen()" ) except Exception: tour_on = False result = None if result: tour_on = True if autoplay: try: element = driver.execute_script( "return Shepherd.activeTour.currentStep" ".options.attachTo.element" ) shep_text = driver.execute_script( "return Shepherd.activeTour.currentStep" ".options.text" ) except Exception: continue if element != latest_element or shep_text != latest_text: latest_element = element latest_text = shep_text start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) now_ms = time.time() * 1000.0 if now_ms >= stop_ms: if (element == latest_element) and ( shep_text == latest_text ): driver.execute_script("Shepherd.activeTour.next()") try: latest_element = driver.execute_script( "return Shepherd.activeTour.currentStep" ".options.attachTo.element" ) latest_text = driver.execute_script( "return Shepherd.activeTour.currentStep" ".options.text" ) start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) except Exception: pass continue else: try: time.sleep(0.01) selector = driver.execute_script( "return Shepherd.activeTour" ".currentStep.options.attachTo.element" ) try: js_utils.wait_for_css_query_selector( driver, selector, timeout=settings.SMALL_TIMEOUT ) except Exception: remove_script = ( "jQuery('%s').remove()" % "div.shepherd-content" ) driver.execute_script(remove_script) js_utils.post_messenger_error_message( driver, "Tour Error: {'%s'} was not found!" % selector, msg_dur, ) time.sleep(0.1) driver.execute_script("Shepherd.activeTour.next()") if autoplay: start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) tour_on = True except Exception: tour_on = False time.sleep(0.1) def play_bootstrap_tour( driver, tour_steps, browser, msg_dur, name=None, interval=0 ): """Plays a Bootstrap tour on the current website.""" instructions = "" for tour_step in tour_steps[name]: instructions += tour_step instructions += """]); // Initialize the tour tour.init(); // Start the tour tour.start(); // Fix timing issue by restarting tour immediately tour.restart(); // Save for later $tour = tour;""" if interval and interval > 0: if interval < 1: interval = 1 interval = str(float(interval) * 1000.0) instructions = instructions.replace( "duration: 0,", "duration: %s," % interval ) if not is_bootstrap_activated(driver): activate_bootstrap(driver) if len(tour_steps[name]) > 1: try: if "element: " in tour_steps[name][1]: selector = re.search( r"[\S\s]+element: '([\S\s]+)',[\S\s]+title: '", tour_steps[name][1], ).group(1) selector = selector.replace("\\", "").replace(":first", "") page_actions.wait_for_element_present( driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT, ) else: selector = "html" except Exception: js_utils.post_messenger_error_message( driver, "Tour Error: {'%s'} was not found!" % selector, msg_dur ) raise Exception( "Tour Error: {'%s'} was not found! " "Exiting due to failure on first tour step!" "" % selector ) driver.execute_script(instructions) tour_on = True try: page_actions.wait_for_element_visible( driver, ".tour-tour", by="css selector", timeout=1.2 ) except Exception: pass try: driver.execute_script('document.activeElement.blur();') except Exception: pass while tour_on: try: time.sleep(0.01) if browser != "firefox": result = driver.execute_script("return $tour.ended()") else: page_actions.wait_for_element_present( driver, ".tour-tour", by="css selector", timeout=0.48 ) result = False except Exception: tour_on = False result = None if result is False: tour_on = True time.sleep(0.05) else: try: time.sleep(0.01) if browser != "firefox": result = driver.execute_script("return $tour.ended()") else: page_actions.wait_for_element_present( driver, ".tour-tour", by="css selector", timeout=0.48 ) result = False if result is False: time.sleep(0.05) continue else: return except Exception: tour_on = False time.sleep(0.1) def play_driverjs_tour( driver, tour_steps, browser, msg_dur, name=None, interval=0 ): """Plays a DriverJS tour on the current website.""" instructions = "" for tour_step in tour_steps[name]: instructions += tour_step instructions += """] ); // Start the tour! tour.start(); $tour = tour;""" extra = """ document.body.addEventListener('keyup', function (event) { if (event.key === 'PageUp') { $tour.movePrevious(); } if (event.key === 'PageDown') { $tour.moveNext(); } })""" autoplay = False if interval and interval > 0: autoplay = True interval = float(interval) if interval < 0.5: interval = 0.5 if not is_driverjs_activated(driver): instructions += extra activate_driverjs(driver) if len(tour_steps[name]) > 1: try: if "element: " in tour_steps[name][1]: selector = re.search( r"[\S\s]+element: '([\S\s]+)',[\S\s]+popover: {", tour_steps[name][1], ).group(1) selector = selector.replace("\\", "").replace(":first", "") page_actions.wait_for_element_present( driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT, ) else: selector = "html" except Exception: js_utils.post_messenger_error_message( driver, "Tour Error: {'%s'} was not found!" % selector, msg_dur ) raise Exception( "Tour Error: {'%s'} was not found! " "Exiting due to failure on first tour step!" "" % selector ) driver.execute_script(instructions) driver.execute_script( 'document.querySelector(".driver-next-btn").focus();' ) tour_on = True if autoplay: start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) latest_step = 0 while tour_on: try: time.sleep(0.01) if browser != "firefox": result = not driver.execute_script("return $tour.isActivated") else: page_actions.wait_for_element_visible( driver, "#driver-popover-item", by="css selector", timeout=1.1, ) result = False except Exception: tour_on = False result = None if result is False: tour_on = True driver.execute_script( 'document.querySelector(".driver-next-btn").focus();' ) if autoplay: try: current_step = driver.execute_script( "return $tour.currentStep" ) except Exception: continue if current_step != latest_step: latest_step = current_step start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) now_ms = time.time() * 1000.0 if now_ms >= stop_ms: if current_step == latest_step: driver.execute_script("$tour.moveNext()") try: latest_step = driver.execute_script( "return $tour.currentStep" ) start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) except Exception: pass continue else: try: time.sleep(0.01) if browser != "firefox": result = not driver.execute_script( "return $tour.isActivated" ) else: page_actions.wait_for_element_visible( driver, "#driver-popover-item", by="css selector", timeout=1.1, ) result = False if result is False: time.sleep(0.1) continue else: return except Exception: tour_on = False time.sleep(0.1) def play_hopscotch_tour( driver, tour_steps, browser, msg_dur, name=None, interval=0 ): """Plays a Hopscotch tour on the current website.""" instructions = "" for tour_step in tour_steps[name]: instructions += tour_step instructions += """] }; // Start the tour! hopscotch.startTour(tour); $tour = hopscotch;""" extra = """ document.body.addEventListener('keyup', function (event) { if (event.key === 'PageUp' || event.key === 'ArrowLeft') { $tour.prevStep(); } if (event.key === 'PageDown' || event.key === 'ArrowRight') { $tour.nextStep(); } })""" autoplay = False if interval and interval > 0: autoplay = True interval = float(interval) if interval < 0.5: interval = 0.5 if not is_hopscotch_activated(driver): instructions += extra activate_hopscotch(driver) if len(tour_steps[name]) > 1: try: if "target: " in tour_steps[name][1]: selector = re.search( r"[\S\s]+target: '([\S\s]+)',[\S\s]+title: '", tour_steps[name][1], ).group(1) selector = selector.replace("\\", "").replace(":first", "") page_actions.wait_for_element_present( driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT, ) else: selector = "html" except Exception: js_utils.post_messenger_error_message( driver, "Tour Error: {'%s'} was not found!" % selector, msg_dur ) raise Exception( "Tour Error: {'%s'} was not found! " "Exiting due to failure on first tour step!" "" % selector ) driver.execute_script(instructions) try: page_actions.wait_for_element_visible( driver, "button.hopscotch-next", by="css selector", timeout=1.2 ) except Exception: pass try: driver.execute_script('document.activeElement.blur();') except Exception: pass tour_on = True if autoplay: start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) latest_step = 0 while tour_on: try: time.sleep(0.01) if browser != "firefox": result = not driver.execute_script("return $tour.isActive") else: page_actions.wait_for_element_present( driver, ".hopscotch-bubble", by="css selector", timeout=0.4, ) result = False except Exception: tour_on = False result = None if result is False: tour_on = True if autoplay: try: current_step = driver.execute_script( "return $tour.getCurrStepNum()" ) except Exception: continue if current_step != latest_step: latest_step = current_step start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) now_ms = time.time() * 1000.0 if now_ms >= stop_ms: if current_step == latest_step: driver.execute_script("$tour.nextStep()") try: latest_step = driver.execute_script( "return $tour.getCurrStepNum()" ) start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) except Exception: pass continue else: try: time.sleep(0.01) if browser != "firefox": result = not driver.execute_script("return $tour.isActive") else: page_actions.wait_for_element_present( driver, ".hopscotch-bubble", by="css selector", timeout=0.4, ) result = False if result is False: time.sleep(0.1) continue else: return except Exception: tour_on = False time.sleep(0.1) def play_introjs_tour( driver, tour_steps, browser, msg_dur, name=None, interval=0 ): """Plays an IntroJS tour on the current website.""" instructions = "" for tour_step in tour_steps[name]: instructions += tour_step instructions += """] }); intro.setOption("disableInteraction", true); intro.setOption("overlayOpacity", .29); intro.setOption("scrollToElement", true); intro.setOption("keyboardNavigation", true); intro.setOption("exitOnEsc", true); intro.setOption("hidePrev", true); intro.setOption("nextToDone", true); intro.setOption("exitOnOverlayClick", false); intro.setOption("showStepNumbers", false); intro.setOption("showProgress", false); intro.start(); $tour = intro; }; // Start the tour startIntro(); """ extra = """ document.body.addEventListener('keyup', function (event) { if (event.key === 'PageUp') { $tour.previousStep(); } if (event.key === 'PageDown') { $tour.nextStep(); } })""" autoplay = False if interval and interval > 0: autoplay = True interval = float(interval) if interval < 0.5: interval = 0.5 if not is_introjs_activated(driver): instructions += extra activate_introjs(driver) if len(tour_steps[name]) > 1: try: if "element: " in tour_steps[name][1]: selector = re.search( r"[\S\s]+element: '([\S\s]+)',[\S\s]+intro: '", tour_steps[name][1], ).group(1) selector = selector.replace("\\", "") page_actions.wait_for_element_present( driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT, ) else: selector = "html" except Exception: js_utils.post_messenger_error_message( driver, "Tour Error: {'%s'} was not found!" % selector, msg_dur ) raise Exception( "Tour Error: {'%s'} was not found! " "Exiting due to failure on first tour step!" "" % selector ) driver.execute_script(instructions) tour_on = True if autoplay: start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) latest_step = 0 while tour_on: try: time.sleep(0.01) if browser != "firefox": result = driver.execute_script("return $tour._currentStep") else: page_actions.wait_for_element_present( driver, ".introjs-tooltip", by="css selector", timeout=0.4 ) result = True except Exception: tour_on = False result = None if result is not None: tour_on = True if autoplay: try: current_step = driver.execute_script( "return $tour._currentStep" ) except Exception: continue if current_step != latest_step: latest_step = current_step start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) now_ms = time.time() * 1000.0 if now_ms >= stop_ms: if current_step == latest_step: try: driver.execute_script("$tour.nextStep()") except Exception: driver.execute_script("$tour.exit()") try: latest_step = driver.execute_script( "return $tour._currentStep" ) start_ms = time.time() * 1000.0 stop_ms = start_ms + (interval * 1000.0) except Exception: pass continue else: try: time.sleep(0.01) if browser != "firefox": result = driver.execute_script("return $tour._currentStep") else: page_actions.wait_for_element_present( driver, ".introjs-tooltip", by="css selector", timeout=0.4, ) result = True if result is not None: time.sleep(0.1) continue else: return except Exception: tour_on = False time.sleep(0.1) def export_tour(tour_steps, name=None, filename="my_tour.js", url=None): """Exports a tour as a JS file. It will include necessary resources as well, such as jQuery. You'll be able to copy the tour directly into the Console of any web browser to play the tour outside of SeleniumBase runs.""" if not name: name = "default" if name not in tour_steps: raise Exception("Tour {%s} does not exist!" % name) if not filename.endswith(".js"): raise Exception('Tour file must end in ".js"!') if not url: url = "data:," tour_type = None if "Bootstrap" in tour_steps[name][0]: tour_type = "bootstrap" elif "DriverJS" in tour_steps[name][0]: tour_type = "driverjs" elif "Hopscotch" in tour_steps[name][0]: tour_type = "hopscotch" elif "IntroJS" in tour_steps[name][0]: tour_type = "introjs" elif "Shepherd" in tour_steps[name][0]: tour_type = "shepherd" else: raise Exception("Unknown tour type!") instructions = ( """//////// Load Tour Start Page (if not there now) ////////\n\n""" """if (window.location.href != "%s") {\n""" """ window.location.href="%s";\n""" """}\n\n""" """//////// Resources ////////\n\n""" """function injectCSS(css_link) {""" """var head = document.getElementsByTagName("head")[0];""" """var link = document.createElement("link");""" """link.rel = "stylesheet";""" """link.type = "text/css";""" """link.href = css_link;""" """link.crossorigin = "anonymous";""" """head.appendChild(link);""" """};\n""" """function injectJS(js_link) {""" """var head = document.getElementsByTagName("head")[0];""" """var script = document.createElement("script");""" """script.src = js_link;""" """script.defer;""" """script.type="text/javascript";""" """script.crossorigin = "anonymous";""" """script.onload = function() { null };""" """head.appendChild(script);""" """};\n""" """function injectStyle(css) {""" """var head = document.getElementsByTagName("head")[0];""" """var style = document.createElement("style");""" """style.type = "text/css";""" """style.appendChild(document.createTextNode(css));""" """head.appendChild(style);""" """};\n""" % (url, url) ) if tour_type == "bootstrap": jquery_js = constants.JQuery.MIN_JS bootstrap_tour_css = constants.BootstrapTour.MIN_CSS bootstrap_tour_js = constants.BootstrapTour.MIN_JS backdrop_style = style_sheet.get_bt_backdrop_style() backdrop_style = backdrop_style.replace("\n", "") backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style) instructions += 'injectJS("%s");\n' % jquery_js instructions += "\n" instructions += "function loadResources() { " instructions += 'if ( typeof jQuery !== "undefined" ) {\n' instructions += 'injectCSS("%s");\n' % bootstrap_tour_css instructions += 'injectStyle("%s");\n' % backdrop_style instructions += 'injectJS("%s");' % bootstrap_tour_js instructions += '} else { window.setTimeout("loadResources();",100); ' instructions += "} }\n" instructions += "loadResources()" elif tour_type == "driverjs": driverjs_css = constants.DriverJS.MIN_CSS driverjs_js = constants.DriverJS.MIN_JS backdrop_style = style_sheet.get_dt_backdrop_style() backdrop_style = backdrop_style.replace("\n", "") backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style) instructions += 'injectCSS("%s");\n' % driverjs_css instructions += 'injectStyle("%s");\n' % backdrop_style instructions += 'injectJS("%s");' % driverjs_js elif tour_type == "hopscotch": hopscotch_css = constants.Hopscotch.MIN_CSS hopscotch_js = constants.Hopscotch.MIN_JS backdrop_style = style_sheet.get_hops_backdrop_style() backdrop_style = backdrop_style.replace("\n", "") backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style) instructions += 'injectCSS("%s");\n' % hopscotch_css instructions += 'injectStyle("%s");\n' % backdrop_style instructions += 'injectJS("%s");' % hopscotch_js elif tour_type == "introjs": intro_css = constants.IntroJS.MIN_CSS intro_js = constants.IntroJS.MIN_JS theme_color = sb_config.introjs_theme_color hover_color = sb_config.introjs_hover_color backdrop_style = style_sheet.get_introjs_style() % ( theme_color, hover_color, hover_color, hover_color, theme_color, ) backdrop_style = backdrop_style.replace("\n", "") backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style) instructions += 'injectCSS("%s");\n' % intro_css instructions += 'injectStyle("%s");\n' % backdrop_style instructions += 'injectJS("%s");' % intro_js elif tour_type == "shepherd": jquery_js = constants.JQuery.MIN_JS shepherd_js = constants.Shepherd.MIN_JS sh_theme_arrows_css = constants.Shepherd.THEME_ARROWS_CSS sh_theme_arrows_fix_css = constants.Shepherd.THEME_ARR_FIX_CSS sh_theme_default_css = constants.Shepherd.THEME_DEFAULT_CSS sh_theme_dark_css = constants.Shepherd.THEME_DARK_CSS sh_theme_sq_css = constants.Shepherd.THEME_SQ_CSS sh_theme_sq_dark_css = constants.Shepherd.THEME_SQ_DK_CSS tether_js = constants.Tether.MIN_JS spinner_css = constants.Messenger.SPINNER_CSS backdrop_style = style_sheet.get_sh_backdrop_style() backdrop_style = backdrop_style.replace("\n", "") backdrop_style = js_utils.escape_quotes_if_needed(backdrop_style) instructions += 'injectCSS("%s");\n' % spinner_css instructions += 'injectJS("%s");\n' % jquery_js instructions += 'injectJS("%s");\n' % tether_js instructions += "\n" instructions += "function loadResources() { " instructions += 'if ( typeof jQuery !== "undefined" ) {\n' instructions += 'injectCSS("%s");' % sh_theme_arrows_css instructions += 'injectCSS("%s");' % sh_theme_arrows_fix_css instructions += 'injectCSS("%s");' % sh_theme_default_css instructions += 'injectCSS("%s");' % sh_theme_dark_css instructions += 'injectCSS("%s");' % sh_theme_sq_css instructions += 'injectCSS("%s");\n' % sh_theme_sq_dark_css instructions += 'injectStyle("%s");\n' % backdrop_style instructions += 'injectJS("%s");\n' % shepherd_js instructions += '} else { window.setTimeout("loadResources();",100); ' instructions += "} }\n" instructions += "loadResources()" instructions += "\n\n//////// Tour Code ////////\n\n" first_instructions = instructions instructions = " " if tour_type == "bootstrap": instructions += "function loadTour() { " instructions += 'if ( typeof Tour !== "undefined" ) {\n' elif tour_type == "driverjs": instructions += "function loadTour() { " instructions += 'if ( typeof Driver !== "undefined" ) {\n' elif tour_type == "hopscotch": instructions += "function loadTour() { " instructions += 'if ( typeof hopscotch !== "undefined" ) {\n' elif tour_type == "introjs": instructions += "function loadTour() { " instructions += 'if ( typeof introJs !== "undefined" ) {\n' elif tour_type == "shepherd": instructions += "function loadTour() { " instructions += 'if ( typeof Shepherd !== "undefined" ) {\n' for tour_step in tour_steps[name]: instructions += tour_step if tour_type == "bootstrap": instructions += """]); // Initialize the tour tour.init(); // Start the tour tour.start(); $tour = tour; $tour.restart();\n""" elif tour_type == "driverjs": instructions += """] ); // Start the tour! tour.start(); $tour = tour;\n""" elif tour_type == "hopscotch": instructions += """] }; // Start the tour! hopscotch.startTour(tour); $tour = hopscotch;\n""" elif tour_type == "introjs": instructions += """] }); intro.setOption("disableInteraction", true); intro.setOption("overlayOpacity", .29); intro.setOption("scrollToElement", true); intro.setOption("keyboardNavigation", true); intro.setOption("exitOnEsc", true); intro.setOption("hidePrev", true); intro.setOption("nextToDone", true); intro.setOption("exitOnOverlayClick", false); intro.setOption("showStepNumbers", false); intro.setOption("showProgress", false); intro.start(); $tour = intro; }; startIntro();\n""" elif tour_type == "shepherd": instructions += """ tour.start(); $tour = tour;\n""" else: pass instructions = textwrap.dedent(instructions) instructions = first_instructions + instructions instructions += '\n} else { window.setTimeout("loadTour();",100); } ' instructions += "}\n" instructions += "loadTour()\n" exported_tours_folder = EXPORTED_TOURS_FOLDER if exported_tours_folder.endswith("/"): exported_tours_folder = exported_tours_folder[:-1] if not os.path.exists(exported_tours_folder): try: os.makedirs(exported_tours_folder) except Exception: pass file_path = exported_tours_folder + "/" + filename out_file = open(file_path, mode="w+", encoding="utf-8") out_file.writelines(instructions) out_file.close() print("\n>>> [%s] was saved!\n" % file_path) ================================================ FILE: seleniumbase/core/visual_helper.py ================================================ import os from seleniumbase.core import log_helper from seleniumbase.fixtures import constants VISUAL_BASELINE_DIR = constants.VisualBaseline.STORAGE_FOLDER abs_path = os.path.abspath(".") visual_baseline_path = os.path.join(abs_path, VISUAL_BASELINE_DIR) def get_visual_baseline_folder(): return visual_baseline_path def visual_baseline_folder_setup(): """Handle Logging""" if not os.path.exists(visual_baseline_path): try: os.makedirs(visual_baseline_path) except Exception: pass # Should only be reachable during multi-threaded runs def get_sbs_head(): # Uses caching to prevent extra method calls SIDE_BY_SIDE_PNG = constants.SideBySide.get_favicon() head = ( '' '' '' "Visual Comparison" "" % (SIDE_BY_SIDE_PNG) ) return head def get_sbs_table_row(baseline="baseline.png", diff="baseline_diff.png"): row = ( '' '' '' '' "" "" % (baseline, diff) ) return row def get_sbs_table_html(baseline="baseline.png", diff="baseline_diff.png"): table_html = ( '' '' "" '' '' "" ) row = get_sbs_table_row(baseline, diff) table_html += row table_html += "
      Baseline ScreenshotVisual Diff Failure Screenshot
      " return table_html def get_sbs_gen_by(): gen_by = ( '

      Generated by: ' "SeleniumBase

      " ) return gen_by def get_sbs_header_text(): header_text = "SeleniumBase Visual Comparison" return header_text def get_sbs_header(): header_text = get_sbs_header_text() header = '

      %s

      ' % header_text return header def get_sbs_footer(): footer = "
      Last updated: " timestamp, the_date, the_time = log_helper.get_master_time() last_updated = "%s at %s" % (the_date, the_time) footer = footer + "%s" % last_updated gen_by = get_sbs_gen_by() footer = footer + gen_by return footer def get_sbs_html(baseline="baseline.png", diff="baseline_diff.png"): head = get_sbs_head() header = get_sbs_header() table_html = get_sbs_table_html(baseline, diff) footer = get_sbs_footer() the_html = ( '' + head + '' + header + table_html + footer + "" ) return the_html ================================================ FILE: seleniumbase/drivers/ReadMe.md ================================================ ## SeleniumBase driver storage To run web automation, you'll need webdrivers for each browser you plan on using. With SeleniumBase, drivers are downloaded automatically as needed into the SeleniumBase `drivers` folder. 🎛️ You can also download drivers manually with these commands: ```zsh seleniumbase get chromedriver seleniumbase get geckodriver seleniumbase get edgedriver ``` After running the commands above, web drivers will get downloaded into the `seleniumbase/drivers/` folder. SeleniumBase uses those drivers during tests. (The drivers don't come with SeleniumBase by default.) If the necessary driver is not found in this location while running tests, SeleniumBase will instead look for the driver on the System PATH. If the necessary driver is not on the System PATH either, SeleniumBase will automatically attempt to download the required driver. 🎛️ You can also download specific versions of drivers. Examples: ```zsh sbase get chromedriver 114 sbase get chromedriver 114.0.5735.90 sbase get chromedriver stable sbase get chromedriver beta sbase get chromedriver dev sbase get chromedriver canary sbase get chromedriver previous # One major version before the stable version sbase get chromedriver mlatest # Milestone latest version for detected browser sbase get edgedriver 115.0.1901.183 ``` (NOTE: `sbase` is a shortcut for `seleniumbase`) -------- **Browser Binaries**: 🎛️ Use the `sbase get` command to download the `Chrome for Testing` and `Chrome-Headless-Shell` browser binaries. Example: ```zsh sbase get chromium # (For base `Chromium`) sbase get cft # (For `Chrome for Testing`) sbase get chs # (For `Chrome-Headless-Shell`) ``` Those commands download those binaries into the `seleniumbase/drivers` folder. (There are subfolders, such as `cft_drivers`, `chs_drivers`, and `chromium_drivers`.) To use the base `Chromium` binary in SeleniumBase scripts, add `--use-chromium` on the command-line, or set `use_chromium=True` from within scripts. To use the `cft` or `chs` binaries in SeleniumBase scripts, set the `binary_location` to `cft` or `chs`, use `--cft` / `--chs` or set `cft=True` / `chs=True`. (Source: https://googlechromelabs.github.io/chrome-for-testing/) -------- [](https://github.com/seleniumbase/SeleniumBase) ================================================ FILE: seleniumbase/drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/atlas_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/brave_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/cft_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/chromium_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/chs_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/comet_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/drivers/opera_drivers/__init__.py ================================================ ================================================ FILE: seleniumbase/extensions/ReadMe.md ================================================ [](https://github.com/seleniumbase/SeleniumBase)

      SeleniumBase browser extension storage

      The List: * ad_block.zip => This extension blocks certain types of iframe ads from loading. * disable_csp.zip => This extension disables a website's Content-Security-Policy. * recorder.zip => Save browser actions to sessionStorage with good CSS selectors. * sbase_ext.zip => A Chromium extension that does nothing. (Used for testing purposes) * firefox_addon.xpi => A Firefox add-on that does nothing. (Used for testing purposes) ================================================ FILE: seleniumbase/extensions/__init__.py ================================================ ================================================ FILE: seleniumbase/fixtures/__init__.py ================================================ ================================================ FILE: seleniumbase/fixtures/base_case.py ================================================ r"""-----------------------------------------------------------------> | ______ __ _ ____ | | / ____/__ / /__ ____ (_)_ ______ ___ / _ \____ ________ | | \__ \/ _ \/ / _ \/ __ \/ / / / / __ `__ \ / /_) / __ \/ ___/ _ \ | | ___/ / __/ / __/ / / / / /_/ / / / / / // /_) / (_/ /__ / __/ | | /____/\___/_/\___/_/ /_/_/\__,_/_/ /_/ /_//_____/\__,_/____/\___/ | | | ---------------------------------------------------------------------> The BaseCase class is the main gateway for using The SeleniumBase Framework. It inherits Python's unittest.TestCase class and runs with pytest or pynose. All tests using BaseCase automatically launch WebDriver browsers for tests. Example Test: # -------------------------------------------------------------- from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_anything(self): # Write your code here. Example: self.open("https://github.com/") self.click('span[data-target*="inputButtonText"]') self.type("input#query-builder-test", "SeleniumBase\n") self.click('a[href="/seleniumbase/SeleniumBase"]') self.assert_element("div.repository-content") self.assert_text("SeleniumBase", "strong a") # -------------------------------------------------------------- SeleniumBase methods expand and improve on existing WebDriver commands. Improvements include making WebDriver more robust, reliable, and flexible. Page elements are given enough time to load before WebDriver acts on them. Code becomes greatly simplified and easier to maintain.""" import colorama import fasteners import json import logging import math import os import re import shutil import sys import textwrap import time import unittest import urllib3 from contextlib import contextmanager, suppress from selenium.common.exceptions import ( ElementClickInterceptedException as ECI_Exception, ElementNotInteractableException as ENI_Exception, InvalidArgumentException, MoveTargetOutOfBoundsException, NoSuchElementException, NoSuchWindowException, StaleElementReferenceException as Stale_Exception, TimeoutException, WebDriverException, ) from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.remote.remote_connection import LOGGER from selenium.webdriver.remote.webelement import WebElement from seleniumbase import config as sb_config from seleniumbase.__version__ import __version__ from seleniumbase.common import decorators from seleniumbase.common.exceptions import ( NotConnectedException, NotUsingChromeException, NotUsingChromiumException, ProxyConnectionException, OutOfScopeException, VisualException, ) from seleniumbase.config import settings from seleniumbase.core import browser_launcher from seleniumbase.core import download_helper from seleniumbase.core import log_helper from seleniumbase.core import session_helper from seleniumbase.core import visual_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import css_to_xpath from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_actions from seleniumbase.fixtures import page_utils from seleniumbase.fixtures import shared_utils from seleniumbase.fixtures import unittest_helper from seleniumbase.fixtures import xpath_to_css __all__ = ["BaseCase"] logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("urllib3").setLevel(logging.ERROR) logging.getLogger("websocket").setLevel(logging.CRITICAL) urllib3.disable_warnings() LOGGER.setLevel(logging.WARNING) is_linux = shared_utils.is_linux() is_windows = shared_utils.is_windows() python3_11_or_newer = False if sys.version_info >= (3, 11): python3_11_or_newer = True py311_patch2 = constants.PatchPy311.PATCH class BaseCase(unittest.TestCase): """""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__initialize_variables() def __initialize_variables(self): self.driver = None self.environment = None self.env = None # Add a shortened version of self.environment self.version_list = shared_utils.make_version_list(__version__) self.version_tuple = tuple(self.version_list) self.version_info = self.version_tuple self.time = time.time self.__page_sources = [] self.__extra_actions = [] self.__js_start_time = 0 self.__set_c_from_switch = False self.__frame_switch_layer = 0 # Used by Recorder-Mode self.__frame_switch_multi = False # Used by Recorder-Mode self.__last_saved_url = None # Used by Recorder-Mode self.__uc_frame_layer = 0 self.__called_setup = False self.__called_teardown = False self.__start_time_ms = int(time.time() * 1000.0) self.__requests_timeout = None self.__page_source_count = 0 self.__screenshot_count = 0 self.__saved_pdf_count = 0 self.__logs_data_count = 0 self.__last_data_file = None self.__level_0_visual_f = False self.__will_be_skipped = False self.__passed_then_skipped = False self.__visual_baseline_copies = [] self.__last_url_of_deferred_assert = "about:blank" self.__last_page_load_url = "about:blank" self.__last_page_screenshot = None self.__last_page_screenshot_png = None self.__last_page_url = None self.__last_page_source = None self.__skip_reason = None self.__origins_to_save = [] self.__actions_to_save = [] self.__dont_record_open = False self.__dont_record_js_click = False self.__new_window_on_rec_open = True self.__overrided_default_timeouts = False self.__added_pytest_html_extra = None self.__deferred_assert_count = 0 self.__deferred_assert_failures = [] self.__device_width = None self.__device_height = None self.__device_pixel_ratio = None self.__changed_jqc_theme = False self.__jqc_default_theme = None self.__jqc_default_color = None self.__jqc_default_width = None self.__saved_id = None # Requires self._* instead of self.__* for external class use self._language = "English" self._presentation_slides = {} self._presentation_transition = {} self._output_file_saves = True # For Presenter / ChartMaker self._rec_overrides_switch = True # Recorder-Mode uses set_c vs switch self._sb_test_identifier = None self._html_report_extra = [] # (Used by pytest_plugin.py) self._last_page_screenshot = None self._last_page_url = None self._final_debug = None self._default_driver = None self._drivers_list = [] self._drivers_browser_map = {} self._was_skipped = False self._chart_data = {} self._chart_count = 0 self._chart_label = {} self._chart_xcount = 0 self._chart_first_series = {} self._chart_series_count = {} self._tour_steps = {} self._xvfb_display = None self._xvfb_width = None self._xvfb_height = None @classmethod def main(self, name, file, *args): """Run pytest if file was called with "python". Usage example: from seleniumbase import BaseCase BaseCase.main(__name__, __file__) class MyTestClass(BaseCase): def test_example(self): pass The run command: python my_test.py # (Instead of "pytest my_test.py") This is useful when sharing code with people who may not be aware that SeleniumBase tests are run with "pytest" instead of "python". Now, if they accidentally type "python", the tests will still run. Eg. "python my_test.py" instead of "pytest my_test.py".""" if name == "__main__": # Test called with "python" import subprocess all_args = [] for arg in args: all_args.append(arg) for arg in sys.argv[1:]: all_args.append(arg) # See: https://stackoverflow.com/a/54666289/7058266 # from pytest import main as pytest_main # pytest_main([file, "-s", *all_args]) subprocess.call( [sys.executable, "-m", "pytest", file, "-s", *all_args] ) def open(self, url, **kwargs): """Navigates the current browser window to the specified page.""" self.__check_scope() if self.__is_cdp_swap_needed(): self.cdp.open(url, **kwargs) return elif ( getattr(self.driver, "_is_using_uc", None) # and getattr(self.driver, "_is_using_auth", None) and not getattr(self.driver, "_is_using_cdp", None) ): # Auth in UC Mode requires CDP Mode # (and now we're always forcing it) logging.info("open() in UC Mode now always activates CDP Mode.") self.activate_cdp_mode(url, **kwargs) return elif ( getattr(self.driver, "_is_using_uc", None) and getattr(self.driver, "_is_using_cdp", None) ): self.disconnect() self.cdp.open(url, **kwargs) return self._check_browser() if self.__needs_minimum_wait(): time.sleep(0.04) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url url = str(url).strip() # Remove leading and trailing whitespace if not self.__looks_like_a_page_url(url): # url should start with one of the following: # "http:", "https:", "://", "data:", "file:", # "about:", "chrome:", or "edge:". if page_utils.is_valid_url("https://" + url): url = "https://" + url else: raise Exception('Invalid URL: "%s"!' % url) self.__last_page_load_url = None js_utils.clear_out_console_logs(self.driver) if url.startswith("://"): # Convert URLs such as "://google.com" into "https://google.com" url = "https" + url if self.recorder_mode and not self.__dont_record_open: time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() action = ["o_url", origin, url, str(int(time_stamp) - 1)] self.__extra_actions.append(action) action = ["_url_", origin, url, time_stamp] self.__extra_actions.append(action) if self.recorder_mode and self.__new_window_on_rec_open: c_url = self.driver.current_url if ("http:") in c_url or ("https:") in c_url or ("file:") in c_url: if self.get_domain_url(url) != self.get_domain_url(c_url): self.open_new_window(switch_to=True) try: self.driver.get(url) except Exception as e: if not hasattr(e, "msg") and hasattr(self.driver, "default_get"): try: self._check_browser() time.sleep(0.4) except Exception: logging.debug("Browser crashed! Will open new browser!") self.driver = self.get_new_driver() self.driver.default_get(url) elif ( "ERR_CONNECTION_TIMED_OUT" in e.msg or "ERR_CONNECTION_CLOSED" in e.msg or "ERR_CONNECTION_RESET" in e.msg or "ERR_NAME_NOT_RESOLVED" in e.msg ): shared_utils.check_if_time_limit_exceeded() self._check_browser() time.sleep(0.8) self.driver.get(url) elif ( "ERR_INTERNET_DISCONNECTED" in e.msg or "neterror?e=dnsNotFound" in e.msg or "ERR_PROXY_CONNECTION_FAILED" in e.msg ): shared_utils.check_if_time_limit_exceeded() self._check_browser() time.sleep(1.05) try: self.driver.get(url) except Exception as e2: if ( "ERR_INTERNET_DISCONNECTED" in e2.msg or "neterror?e=dnsNotFound" in e2.msg ): message = "ERR_INTERNET_DISCONNECTED: " message += "Internet unreachable!" raise NotConnectedException(message) elif "ERR_PROXY_CONNECTION_FAILED" in e2.msg: message = "ERR_PROXY_CONNECTION_FAILED: " message += "Internet unreachable and/or invalid proxy!" raise ProxyConnectionException(message) else: raise elif "Timed out receiving message from renderer" in e.msg: page_load_timeout = self.driver.timeouts.page_load logging.info( "The page load timed out after %s seconds! Will retry..." % page_load_timeout ) try: self.driver.get(url) except Exception as e: if "Timed out receiving message from renderer" in e.msg: raise Exception( "Retry of page load timed out after %s seconds!" % page_load_timeout ) else: raise elif ( "cannot determine loading status" in e.msg or "unexpected command response" in e.msg ): if self.__needs_minimum_wait(): time.sleep(0.2) self.driver.get(url) else: pass # Odd issue where the open did happen. Continue. elif "invalid session id" in e.msg: logging.debug("Invalid session id. Will open new browser.") self.driver = self.get_new_driver() self.driver.get(url) else: raise try: if ( self.driver.current_url == pre_action_url and pre_action_url != url ): time.sleep(0.1) # Make sure load happens except Exception: time.sleep(0.1) # First see if waiting helps try: self._check_browser() if not self.driver.current_url: raise Exception("No current URL!") except Exception: # Spin up a new driver with the URL self.driver = self.get_new_driver() self.driver.get(url) self._check_browser() if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: if not self.undetectable: self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.08) # Force a minimum wait, even if skipping waits. if self.undetectable: self.__uc_frame_layer = 0 if self.demo_mode: if self.driver.current_url.startswith(("http", "file", "data")): if not js_utils.is_jquery_activated(self.driver): with suppress(Exception): js_utils.add_js_link( self.driver, constants.JQuery.MIN_JS ) self.__demo_mode_pause_if_active() def get(self, url): """If "url" looks like a page URL, open the URL in the web browser. Otherwise, return self.get_element(URL_AS_A_SELECTOR) Examples: self.get("https://seleniumbase.io") # Navigates to the URL self.get("input.class") # Finds and returns the WebElement """ self.__check_scope() if self.__looks_like_a_page_url(url): self.open(url) else: return self.get_element(url) # url is treated like a selector def click( self, selector, by="css selector", timeout=None, delay=0, scroll=True ): self.__check_scope() self.__skip_if_esc() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) original_selector = selector original_by = by selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.click(selector, timeout=timeout) return if delay and (type(delay) in [int, float]) and delay > 0: time.sleep(delay) if page_utils.is_link_text_selector(selector) or by == By.LINK_TEXT: if not self.is_link_text_visible(selector): # Handle a special case of links hidden in dropdowns self.click_link_text(selector, timeout=timeout) return if ( page_utils.is_partial_link_text_selector(selector) or by == By.PARTIAL_LINK_TEXT ): if not self.is_partial_link_text_visible(selector): # Handle a special case of partial links hidden in dropdowns self.click_partial_link_text(selector, timeout=timeout) return if self.__is_shadow_selector(selector): self.__shadow_click(selector, timeout) return if self.__needs_minimum_wait() or self.browser == "safari": time.sleep(0.05) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) self.__demo_mode_highlight_if_active(original_selector, original_by) if scroll and not self.demo_mode and not self.slow_mode: self.__scroll_to_element(element, selector, by) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) try: if ( by == By.LINK_TEXT and (self.browser == "ie" or self.browser == "safari") ): self.__jquery_click(selector, by=by) elif self.browser == "safari": self.execute_script("arguments[0].click();", element) else: href = None new_tab = False onclick = None with suppress(Exception): if self.headless and element.tag_name.lower() == "a": # Handle a special case of opening a new tab (headless) href = element.get_attribute("href").strip() onclick = element.get_attribute("onclick") target = element.get_attribute("target") if target == "_blank": new_tab = True if new_tab and self.__looks_like_a_page_url(href): if onclick: with suppress(Exception): self.execute_script(onclick) current_window = self.driver.current_window_handle self.open_new_window() with suppress(Exception): self.open(href) self.switch_to_window(current_window) return # Normal click self.__element_click(element) except Stale_Exception: self.wait_for_ready_state_complete() time.sleep(0.16) element = page_actions.wait_for_element_clickable( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) with suppress(Exception): self.__scroll_to_element(element, selector, by) if self.browser == "safari" and by == By.LINK_TEXT: self.__jquery_click(selector, by=by) elif self.browser == "safari": self.execute_script("arguments[0].click();", element) else: self.__element_click(element) except ENI_Exception as e: with suppress(Exception): if ( "element has zero size" in e.msg and element.tag_name.lower() == "a" ): if "contains(" not in selector: self.js_click(selector, by=by) else: self.jquery_click(selector, by=by) if self.__needs_minimum_wait(): time.sleep(0.04) return self.wait_for_ready_state_complete() time.sleep(0.1) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) if not page_actions.is_element_clickable( self.driver, selector, by ): with suppress(Exception): self.wait_for_element_clickable( selector, by, timeout=1.8 ) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) href = None new_tab = False onclick = None with suppress(Exception): if element.tag_name.lower() == "a": # Handle a special case of opening a new tab (non-headless) href = element.get_attribute("href").strip() onclick = element.get_attribute("onclick") target = element.get_attribute("target") if target == "_blank": new_tab = True if new_tab and self.__looks_like_a_page_url(href): if onclick: with suppress(Exception): self.execute_script(onclick) current_window = self.driver.current_window_handle self.open_new_window() with suppress(Exception): self.open(href) self.switch_to_window(current_window) return if scroll and not self.demo_mode and not self.slow_mode: self.__scroll_to_element(element, selector, by) if self.browser == "firefox" or self.browser == "safari": if by == By.LINK_TEXT or "contains(" in selector: self.__jquery_click(selector, by=by) else: self.__js_click(selector, by=by) else: try: self.__element_click(element) except Exception: self.wait_for_ready_state_complete() element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) self.__element_click(element) except MoveTargetOutOfBoundsException: self.wait_for_ready_state_complete() try: self.__js_click(selector, by=by) except Exception: try: self.__jquery_click(selector, by=by) except Exception: # One more attempt to click on the element element = page_actions.wait_for_element_clickable( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) self.__element_click(element) except WebDriverException as e: if ( "cannot determine loading status" in e.msg or "unexpected command response" in e.msg ): pass # Odd issue where the click did happen. Continue. else: self.wait_for_ready_state_complete() try: self.__js_click(selector, by=by) except Exception: try: self.__jquery_click(selector, by=by) except Exception: # One more attempt to click on the element element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) self.__element_click(element) latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() elif ( latest_window_count == pre_window_count - 1 and latest_window_count > 0 ): # If a click closes the active window, # switch to the last one if it exists. self.switch_to_window(-1) if settings.WAIT_FOR_RSC_ON_CLICKS: if not self.undetectable: with suppress(Exception): self.wait_for_ready_state_complete() if self.__needs_minimum_wait() or self.browser == "safari": time.sleep(0.05) else: time.sleep(0.08) else: if not self.undetectable: # A smaller subset of self.wait_for_ready_state_complete() with suppress(Exception): self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT) if self.__needs_minimum_wait() or self.browser == "safari": time.sleep(0.045) try: if self.driver.current_url != pre_action_url: self.__ad_block_as_needed() self.__disable_beforeunload_as_needed() if self.__needs_minimum_wait(): time.sleep(0.075) except Exception: with suppress(Exception): self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.05) else: time.sleep(0.085) if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() self.__set_esc_skip() def slow_click(self, selector, by="css selector", timeout=None): """Similar to click(), but pauses for a brief moment before clicking. When used in combination with setting the user-agent, you can often bypass bot-detection by tricking websites into thinking that you're not a bot. (Useful on websites that block web automation tools.) To set the user-agent, use: ``--agent=AGENT``. Here's an example message from GitHub's bot-blocker: ``You have triggered an abuse detection mechanism...`` """ self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) if not self.demo_mode and not self.slow_mode: self.click(selector, by=by, timeout=timeout, delay=1.05) elif self.slow_mode: self.click(selector, by=by, timeout=timeout, delay=0.65) else: # Demo Mode already includes a small delay self.click(selector, by=by, timeout=timeout, delay=0.25) def double_click(self, selector, by="css selector", timeout=None): self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) original_selector = selector original_by = by selector, by = self.__recalculate_selector(selector, by) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) self.__demo_mode_highlight_if_active(original_selector, original_by) if not self.demo_mode and not self.slow_mode: self.__scroll_to_element(element, selector, by) self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.02) # Find the element one more time in case scrolling hid it element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url try: if self.browser == "safari": # Jump to the "except" block where the other script should work raise Exception("This Exception will be caught.") actions = ActionChains(self.driver) actions.double_click(element).perform() except Exception: css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) double_click_script = ( """var targetElement1 = document.querySelector('%s'); var clickEvent1 = document.createEvent('MouseEvents'); clickEvent1.initEvent('dblclick', true, true); targetElement1.dispatchEvent(clickEvent1);""" % css_selector ) if ":contains\\(" not in css_selector: self.execute_script(double_click_script) else: double_click_script = ( """var targetElement1 = arguments[0]; var clickEvent1 = document.createEvent('MouseEvents'); clickEvent1.initEvent('dblclick', true, true); targetElement1.dispatchEvent(clickEvent1);""" ) self.execute_script(double_click_script, element) if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() else: # A smaller subset of self.wait_for_ready_state_complete() self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT) if self.driver.current_url != pre_action_url: self.__ad_block_as_needed() self.__disable_beforeunload_as_needed() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() elif self.__needs_minimum_wait(): time.sleep(0.02) def context_click(self, selector, by="css selector", timeout=None): """(A context click is a right-click that opens a context menu.)""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) original_selector = selector original_by = by selector, by = self.__recalculate_selector(selector, by) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) self.__demo_mode_highlight_if_active(original_selector, original_by) if not self.demo_mode and not self.slow_mode: self.__scroll_to_element(element, selector, by) self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.02) # Find the element one more time in case scrolling hid it element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout=timeout, original_selector=original_selector, ) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url try: if self.browser == "safari": # Jump to the "except" block where the other script should work raise Exception("This Exception will be caught.") actions = ActionChains(self.driver) actions.context_click(element).perform() except Exception: css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) right_click_script = ( """var targetElement1 = document.querySelector('%s'); var clickEvent1 = document.createEvent('MouseEvents'); clickEvent1.initEvent('contextmenu', true, true); targetElement1.dispatchEvent(clickEvent1);""" % css_selector ) if ":contains\\(" not in css_selector: self.execute_script(right_click_script) else: right_click_script = ( """var targetElement1 = arguments[0]; var clickEvent1 = document.createEvent('MouseEvents'); clickEvent1.initEvent('contextmenu', true, true); targetElement1.dispatchEvent(clickEvent1);""" ) self.execute_script(right_click_script, element) if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() else: # A smaller subset of self.wait_for_ready_state_complete() self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT) if self.driver.current_url != pre_action_url: self.__ad_block_as_needed() self.__disable_beforeunload_as_needed() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() elif self.__needs_minimum_wait(): time.sleep(0.02) if self.recorder_mode and self.__current_url_is_recordable(): if self.get_session_storage_item("pause_recorder") == "no": if by == By.XPATH: selector = original_selector time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() action = ["r_clk", selector, origin, time_stamp] self.__extra_actions.append(action) def click_chain( self, selectors_list, by="css selector", timeout=None, spacing=0 ): """This method clicks on a list of elements in succession. @Params selectors_list - The list of selectors to click on. by - The type of selector to search by (Default: "css selector"). timeout - How long to wait for the selector to be visible. spacing - The amount of time to wait between clicks (in seconds).""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) for selector in selectors_list: self.click(selector, by=by, timeout=timeout) if spacing > 0: time.sleep(spacing) def update_text( self, selector, text, by="css selector", timeout=None, retry=False ): """This method updates an element's text field with new text. Has multiple parts: * Waits for the element to be visible. * Waits for the element to be interactive. * Clears the text field. * Types in the new text. * Hits Enter/Submit (if the text ends in "\n"). @Params selector - The selector of the text field. text - The new text to type into the text field. by - The type of selector to search by. (Default: "css selector") timeout - How long to wait for the selector to be visible. retry - If True, use JS if the Selenium text update fails.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.type(selector, text, timeout=timeout) return if self.__is_shadow_selector(selector): self.__shadow_type(selector, text, timeout) return element = self.wait_for_element_clickable( selector, by=by, timeout=timeout ) self.__demo_mode_highlight_if_active(selector, by) if not self.demo_mode and not self.slow_mode: self.__scroll_to_element(element, selector, by) if self.__needs_minimum_wait(): time.sleep(0.04) try: element.clear() # May need https://stackoverflow.com/a/50691625 backspaces = Keys.BACK_SPACE * 42 # Is the answer to everything element.send_keys(backspaces) # In case autocomplete keeps text except (Stale_Exception, ENI_Exception): self.wait_for_ready_state_complete() time.sleep(0.16) element = self.wait_for_element_clickable( selector, by=by, timeout=timeout ) with suppress(Exception): element.clear() except Exception: pass # Clearing the text field first might not be necessary self.__demo_mode_pause_if_active(tiny=True) pre_action_url = None if self.demo_mode: with suppress(Exception): pre_action_url = self.driver.current_url text = self.__get_type_checked_text(text) try: if not text.endswith("\n"): element.send_keys(text) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() else: element.send_keys(text[:-1]) if self.slow_mode or self.demo_mode: self.__demo_mode_pause_if_active(tiny=True) else: time.sleep(0.0135) try: element.send_keys(Keys.RETURN) except WebDriverException as e: if ( "cannot determine loading status" in e.msg or "unexpected command response" in e.msg ): pass # Odd issue where the click did happen. Continue. else: raise if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.04) except Exception: self.wait_for_ready_state_complete() time.sleep(0.14) element = self.wait_for_element_visible( selector, by=by, timeout=timeout ) time.sleep(0.04) if not text.endswith("\n"): element.send_keys(text) else: element.send_keys(text[:-1]) if self.slow_mode or self.demo_mode: self.__demo_mode_pause_if_active(tiny=True) else: time.sleep(0.0135) try: element.send_keys(Keys.RETURN) except WebDriverException as e: if ( "cannot determine loading status" in e.msg or "unexpected command response" in e.msg ): pass # Odd issue where the click did happen. Continue. else: raise if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.03) if self.undetectable: time.sleep(0.025) if ( retry and element.get_attribute("value") != text and not text.endswith("\n") ): logging.debug("update_text() is falling back to JavaScript!") self.set_value(selector, text, by=by) if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() def add_text(self, selector, text, by="css selector", timeout=None): """The more-reliable version of driver.send_keys() Similar to update_text(), but won't clear the text field first.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.send_keys(selector, text) return if self.__is_shadow_selector(selector): self.__shadow_type(selector, text, timeout, clear_first=False) return if selector == "html" and text in ["\n", Keys.ENTER, Keys.RETURN]: # This is a shortcut for calling self.click_active_element(). # Use after "\t" or Keys.TAB to cycle through elements first. self.click_active_element() return element = self.wait_for_element_present( selector, by=by, timeout=timeout ) if ( selector == "html" and text.count("\t") >= 1 and text.count("\n") == 1 and text.endswith("\n") and text.replace("\t", "").replace("\n", "").replace(" ", "") == "" ): # Shortcut to send multiple tabs followed by click_active_element() self.wait_for_ready_state_complete() element.send_keys(Keys.TAB * text.count("\t")) self.click_active_element() return self.__demo_mode_highlight_if_active(selector, by) if not self.demo_mode and not self.slow_mode: self.__scroll_to_element(element, selector, by) pre_action_url = None if self.demo_mode: with suppress(Exception): pre_action_url = self.driver.current_url text = self.__get_type_checked_text(text) try: if not text.endswith("\n"): element.send_keys(text) else: element.send_keys(text[:-1]) if self.slow_mode or self.demo_mode: self.__demo_mode_pause_if_active(tiny=True) else: time.sleep(0.0135) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() except (Stale_Exception, ENI_Exception): self.wait_for_ready_state_complete() time.sleep(0.16) element = self.wait_for_element_visible( selector, by=by, timeout=timeout ) if not text.endswith("\n"): element.send_keys(text) else: element.send_keys(text[:-1]) if self.slow_mode or self.demo_mode: self.__demo_mode_pause_if_active(tiny=True) else: time.sleep(0.0135) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() def type( self, selector, text, by="css selector", timeout=None, retry=False ): """Same as self.update_text() This method updates an element's text field with new text. Has multiple parts: * Waits for the element to be visible. * Waits for the element to be interactive. * Clears the text field. * Types in the new text. * Hits Enter/Submit (if the text ends in "\n"). @Params selector - The selector of the text field. text - The new text to type into the text field. by - The type of selector to search by. (Default: "css selector") timeout - How long to wait for the selector to be visible. retry - If True, use JS if the Selenium text update fails. DO NOT confuse self.type() with Python type()! They are different! """ self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) self.update_text(selector, text, by=by, timeout=timeout, retry=retry) def send_keys(self, selector, text, by="css selector", timeout=None): """Same as self.add_text() Similar to update_text(), but won't clear the text field first.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) self.add_text(selector, text, by=by, timeout=timeout) def press_keys(self, selector, text, by="css selector", timeout=None): """Use send_keys() to press one key at a time.""" if self.__is_cdp_swap_needed(): self.cdp.press_keys(selector, text, timeout=timeout) return self.wait_for_ready_state_complete() element = self.wait_for_element_present( selector, by=by, timeout=timeout ) if self.demo_mode: selector, by = self.__recalculate_selector(selector, by) css_selector = self.convert_to_css_selector(selector, by=by) self.__demo_mode_highlight_if_active(css_selector, By.CSS_SELECTOR) if self.recorder_mode and self.__current_url_is_recordable(): if self.get_session_storage_item("pause_recorder") == "no": css_selector = self.convert_to_css_selector(selector, by=by) time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() sel_tex = [css_selector, text] action = ["pkeys", sel_tex, origin, time_stamp] self.__extra_actions.append(action) press_enter = False if text.endswith("\n"): text = text[:-1] press_enter = True for key in text: element.send_keys(key) if press_enter: if self.slow_mode or self.demo_mode: self.__demo_mode_pause_if_active(tiny=True) else: time.sleep(0.0135) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: if not self.undetectable: self.wait_for_ready_state_complete() else: time.sleep(0.15) if self.demo_mode: if press_enter: self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() elif self.__needs_minimum_wait(): time.sleep(0.05) if self.undetectable: time.sleep(0.02) def submit(self, selector, by="css selector"): """Alternative to self.driver.find_element_by_*(SELECTOR).submit()""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.submit(selector) return element = self.wait_for_element_clickable( selector, by=by, timeout=settings.SMALL_TIMEOUT ) element.submit() self.__demo_mode_pause_if_active() def clear(self, selector, by="css selector", timeout=None): """This method clears an element's text field. A clear() is already included with most methods that type text, such as self.type(), self.update_text(), etc. Does not use Demo Mode highlights, mainly because we expect that some users will be calling an unnecessary clear() before calling a method that already includes clear() as part of it. In case websites trigger an autofill after clearing a field, add backspaces to make sure autofill doesn't undo the clear. @Params selector - The selector of the text field. by - The type of selector to search by. (Default: "css selector") timeout - How long to wait for the selector to be visible.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.clear_input(selector) return if self.__is_shadow_selector(selector): self.__shadow_clear(selector, timeout) return element = self.wait_for_element_visible( selector, by=by, timeout=timeout ) self.scroll_to(selector, by=by, timeout=timeout) try: element.clear() backspaces = Keys.BACK_SPACE * 42 # Autofill Defense element.send_keys(backspaces) except (Stale_Exception, ENI_Exception): self.wait_for_ready_state_complete() time.sleep(0.16) element = self.wait_for_element_visible( selector, by=by, timeout=timeout ) element.clear() with suppress(Exception): backspaces = Keys.BACK_SPACE * 42 # Autofill Defense element.send_keys(backspaces) except Exception: element.clear() def focus(self, selector, by="css selector", timeout=None): """Make the current page focus on an interactable element. If the element is not interactable, only scrolls to it. The "tab" key is another way of setting the page focus.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.focus(selector) return element = self.wait_for_element_present( selector, by=by, timeout=timeout ) if not element.is_displayed(): css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) script = """document.querySelector('%s').focus();""" % css_selector self.execute_script(script) self.__demo_mode_pause_if_active() return self.scroll_to(selector, by=by, timeout=timeout) try: element.send_keys(Keys.NULL) except (Stale_Exception, ENI_Exception): self.wait_for_ready_state_complete() time.sleep(0.12) element = self.wait_for_element_visible( selector, by=by, timeout=timeout ) try: element.send_keys(Keys.NULL) except ENI_Exception: # Non-interactable element. Skip focus and continue. pass self.__demo_mode_pause_if_active() def refresh_page(self): self.__check_scope() self.__last_page_load_url = None if self.__is_cdp_swap_needed(): self.cdp.reload() return js_utils.clear_out_console_logs(self.driver) self.driver.refresh() self.wait_for_ready_state_complete() def refresh(self): """The shorter version of self.refresh_page()""" self.refresh_page() def get_current_url(self): self.__check_scope() current_url = None if self.__is_cdp_swap_needed(): current_url = self.cdp.get_current_url() else: current_url = self.driver.current_url if "%" in current_url: try: from urllib.parse import unquote current_url = unquote(current_url, errors="strict") except Exception: pass return current_url def get_origin(self): self.__check_scope() return self.execute_script("return window.location.origin;") def get_html(self, *args, **kwargs): return self.get_page_source(*args, **kwargs) def get_page_source(self, *args, **kwargs): if self.__is_cdp_swap_needed(*args, **kwargs): return self.cdp.get_page_source() self.wait_for_ready_state_complete() if self.__needs_minimum_wait: time.sleep(0.025) return self.driver.page_source def get_page_title(self): if self.__is_cdp_swap_needed(): return self.cdp.get_title() self.wait_for_ready_state_complete() with suppress(Exception): self.wait_for_element_present( "title", by="css selector", timeout=settings.MINI_TIMEOUT ) time.sleep(0.025) return self.driver.title def get_title(self): """The shorter version of self.get_page_title()""" return self.get_page_title() def get_user_agent(self): if self.__is_cdp_swap_needed(): return self.cdp.get_user_agent() return self.execute_script("return navigator.userAgent;") def get_locale_code(self): if self.__is_cdp_swap_needed(): return self.cdp.get_locale_code() return self.execute_script( "return navigator.language || navigator.languages[0];" ) def go_back(self): self.__check_scope() if self.__is_cdp_swap_needed(): self.cdp.go_back() return if getattr(self, "recorder_mode", None): self.save_recorded_actions() pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url self.__last_page_load_url = None self.driver.back() with suppress(Exception): if pre_action_url == self.driver.current_url: self.driver.back() # Again because the page was redirected if self.recorder_mode: time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() action = ["go_bk", "", origin, time_stamp] self.__extra_actions.append(action) if self.browser == "safari": self.wait_for_ready_state_complete() time.sleep(0.02) self.driver.refresh() time.sleep(0.02) self.wait_for_ready_state_complete() self.__demo_mode_pause_if_active() def go_forward(self): self.__check_scope() if self.__is_cdp_swap_needed(): self.cdp.go_forward() return if getattr(self, "recorder_mode", None): self.save_recorded_actions() self.__last_page_load_url = None self.driver.forward() if self.recorder_mode: time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() action = ["go_fw", "", origin, time_stamp] self.__extra_actions.append(action) self.wait_for_ready_state_complete() self.__demo_mode_pause_if_active() def open_start_page(self): """Navigates the current browser window to the start_page. You can set the start_page on the command-line in three ways: '--start_page=URL', '--start-page=URL', or '--url=URL'. If the start_page is not set, then "about:blank" will be used.""" self.__check_scope() start_page = self.start_page if isinstance(start_page, str): start_page = start_page.strip() # Remove extra whitespace if start_page and len(start_page) >= 4: if page_utils.is_valid_url(start_page): self.open(start_page) else: new_start_page = "https://" + start_page if page_utils.is_valid_url(new_start_page): self.__dont_record_open = True self.open(new_start_page) self.__dont_record_open = False else: logging.info('Invalid URL: "%s"!' % start_page) if self.get_current_url() != "about:blank": self.open("about:blank") else: if self.get_current_url() != "about:blank": self.open("about:blank") def open_if_not_url(self, url): """Opens the url in the browser if it's not the current url (*). Parameters tagged on by search engines are ignored for this method. Eg. If the current url is: * https://www.bing.com/search?q=SeleniumBase&source=hp And the url privided by this method call is: * https://www.bing.com/search?q=SeleniumBase Then the urls will be considered the same, and no open() action will be performed. This method is primarily used by Recorder Mode script generation, where both clicks and opens are recorded. So if a click() action leads to an open() action, then the script generator will attempt to convert the open() action into open_if_not_url() so that the same page isn't opened again if the user is already on the page.""" self.__check_scope() current_url = self.get_current_url() if current_url != url: if ( "?q=" not in current_url or "&" not in current_url or current_url.find("?q=") >= current_url.find("&") or current_url.split("&")[0] != url ): self.open(url) def is_element_present(self, selector, by="css selector"): """Returns whether the element exists in the HTML.""" if self.__is_cdp_swap_needed(): return self.cdp.is_element_present(selector) self.wait_for_ready_state_complete() selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_element_present(selector) return page_actions.is_element_present(self.driver, selector, by) def is_element_visible(self, selector, by="css selector"): """Returns whether the element is visible on the page.""" if self.__is_cdp_swap_needed(): return self.cdp.is_element_visible(selector) self.wait_for_ready_state_complete() selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_element_visible(selector) return page_actions.is_element_visible(self.driver, selector, by) def is_element_clickable(self, selector, by="css selector"): self.wait_for_ready_state_complete() selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_element_clickable(selector) return page_actions.is_element_clickable(self.driver, selector, by) def is_element_enabled(self, selector, by="css selector"): self.wait_for_ready_state_complete() selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_element_enabled(selector) return page_actions.is_element_enabled(self.driver, selector, by) def is_text_visible(self, text, selector="body", by="css selector"): """Returns whether the text substring is visible in the element.""" if self.__is_cdp_swap_needed(): return self.cdp.is_text_visible(text, selector) self.wait_for_ready_state_complete() time.sleep(0.01) selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_text_visible(text, selector) return page_actions.is_text_visible(self.driver, text, selector, by) def is_exact_text_visible(self, text, selector="body", by="css selector"): """Returns whether the text is exactly equal to the element text. (Leading and trailing whitespace is ignored in the verification.)""" if self.__is_cdp_swap_needed(): return self.cdp.is_exact_text_visible(text, selector) self.wait_for_ready_state_complete() time.sleep(0.01) selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_exact_text_visible(text, selector) return page_actions.is_exact_text_visible( self.driver, text, selector, by ) def is_non_empty_text_visible(self, selector="body", by="css selector"): """Returns whether the element has any non-empty text visible. Whitespace-only text is considered empty text.""" self.wait_for_ready_state_complete() time.sleep(0.01) selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_non_empty_text_visible(selector) return page_actions.is_non_empty_text_visible( self.driver, selector, by=by ) def is_attribute_present( self, selector, attribute, value=None, by="css selector" ): """Returns True if the element attribute/value is found. If the value is not specified, the attribute only needs to exist.""" if self.__is_cdp_swap_needed(): return self.cdp.is_attribute_present( selector, attribute, value=value ) self.wait_for_ready_state_complete() time.sleep(0.01) selector, by = self.__recalculate_selector(selector, by) if self.__is_shadow_selector(selector): return self.__is_shadow_attribute_present( selector, attribute, value ) return page_actions.is_attribute_present( self.driver, selector, attribute, value, by ) def is_link_text_visible(self, link_text): """Returns whether there's an exact match for the link text.""" self.wait_for_ready_state_complete() time.sleep(0.01) return page_actions.is_element_visible( self.driver, link_text, by="link text" ) def is_partial_link_text_visible(self, partial_link_text): """Returns whether there's a substring match for the link text.""" self.wait_for_ready_state_complete() time.sleep(0.01) return page_actions.is_element_visible( self.driver, partial_link_text, by="partial link text" ) def is_link_text_present(self, link_text): """Returns True if the link text appears in the HTML of the page. The element doesn't need to be visible, such as elements hidden inside a dropdown selection.""" self.wait_for_ready_state_complete() soup = self.get_beautiful_soup() html_links = soup.find_all("a") for html_link in html_links: if html_link.text.strip() == link_text.strip(): return True return False def is_partial_link_text_present(self, link_text): """Returns True if the partial link appears in the HTML of the page. The element doesn't need to be visible, such as elements hidden inside a dropdown selection.""" self.wait_for_ready_state_complete() soup = self.get_beautiful_soup() html_links = soup.find_all("a") for html_link in html_links: if link_text.strip() in html_link.text.strip(): return True return False def get_link_attribute(self, link_text, attribute, hard_fail=True): """Finds a link by link text and then returns the attribute's value. If the link text or attribute cannot be found, an exception will get raised if hard_fail is True (otherwise None is returned).""" self.wait_for_ready_state_complete() soup = self.get_beautiful_soup() html_links = soup.find_all("a") for html_link in html_links: if html_link.text.strip() == link_text.strip(): if html_link.has_attr(attribute): attribute_value = html_link.get(attribute) return attribute_value if hard_fail: raise Exception( "Unable to find attribute {%s} from link text {%s}!" % (attribute, link_text) ) else: return None if hard_fail: raise Exception("Link text {%s} was not found!" % link_text) else: return None def get_link_text_attribute(self, link_text, attribute, hard_fail=True): """Same as self.get_link_attribute() Finds a link by link text and then returns the attribute's value. If the link text or attribute cannot be found, an exception will get raised if hard_fail is True (otherwise None is returned).""" return self.get_link_attribute(link_text, attribute, hard_fail) def get_partial_link_text_attribute( self, link_text, attribute, hard_fail=True ): """Finds a link by partial link text and then returns the attribute's value. If the partial link text or attribute cannot be found, an exception will get raised if hard_fail is True (otherwise None is returned).""" self.wait_for_ready_state_complete() soup = self.get_beautiful_soup() html_links = soup.find_all("a") for html_link in html_links: if link_text.strip() in html_link.text.strip(): if html_link.has_attr(attribute): attribute_value = html_link.get(attribute) return attribute_value if hard_fail: raise Exception( "Unable to find attribute {%s} from " "partial link text {%s}!" % (attribute, link_text) ) else: return None if hard_fail: msg = "Partial Link text {%s} was not found!" % link_text page_actions.timeout_exception("LinkTextNotFoundException", msg) else: return None def click_link_text(self, link_text, timeout=None): """This method clicks link text on a page.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) if self.__is_cdp_swap_needed(): self.cdp.find_element(link_text, timeout=timeout).click() return self.__skip_if_esc() link_text = self.__get_type_checked_text(link_text) if self.__is_cdp_swap_needed(): self.cdp.click_link(link_text) return if self.browser == "safari": if self.demo_mode: self.wait_for_link_text_present(link_text, timeout=timeout) try: self.__jquery_slow_scroll_to(link_text, by="link text") except Exception: element = self.wait_for_link_text_visible( link_text, timeout=timeout ) self.__slow_scroll_to_element(element) o_bs = "" # original_box_shadow loops = settings.HIGHLIGHTS selector = self.convert_to_css_selector( link_text, by="link text" ) selector = self.__make_css_match_first_element_only(selector) try: selector = re.escape(selector) selector = self.__escape_quotes_if_needed(selector) self.__highlight_with_jquery(selector, loops, o_bs) except Exception: pass # JQuery probably couldn't load. Skip highlighting. self.__jquery_click(link_text, by="link text") return if not self.is_link_text_present(link_text): self.wait_for_link_text_present(link_text, timeout=timeout) if not self.demo_mode and not self.slow_mode: if self.__needs_minimum_wait(): time.sleep(0.04) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) try: element = self.wait_for_link_text_visible(link_text, timeout=0.2) self.__demo_mode_highlight_if_active(link_text, by="link text") try: self.__element_click(element) except (Stale_Exception, ENI_Exception, ECI_Exception): self.wait_for_ready_state_complete() time.sleep(0.16) element = self.wait_for_link_text_visible( link_text, timeout=timeout ) self.__element_click(element) except Exception: found_css = False text_id = self.get_link_attribute(link_text, "id", False) if text_id: link_css = '[id="%s"]' % link_text found_css = True if not found_css: href = self.__get_href_from_link_text(link_text, False) if href: if href.startswith("/") or page_utils.is_valid_url(href): link_css = '[href="%s"]' % href found_css = True if not found_css: ngclick = self.get_link_attribute(link_text, "ng-click", False) if ngclick: link_css = '[ng-click="%s"]' % ngclick found_css = True if not found_css: onclick = self.get_link_attribute(link_text, "onclick", False) if onclick: link_css = '[onclick="%s"]' % onclick found_css = True success = False if found_css: if self.is_element_visible(link_css): self.click(link_css) success = True else: # The link text might be hidden under a dropdown menu success = self.__click_dropdown_link_text( link_text, link_css ) if not success: element = self.wait_for_link_text_visible( link_text, timeout=settings.MINI_TIMEOUT ) self.__element_click(element) latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() elif ( latest_window_count == pre_window_count - 1 and latest_window_count > 0 ): # If a click closes the active window, # switch to the last one if it exists. self.switch_to_window(-1) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: with suppress(Exception): self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() elif self.__needs_minimum_wait(): time.sleep(0.04) def click_partial_link_text(self, partial_link_text, timeout=None): """This method clicks the partial link text on a page.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) partial_link_text = self.__get_type_checked_text(partial_link_text) if self.__is_cdp_swap_needed(): self.cdp.find_element(partial_link_text, timeout=timeout).click() return if not self.is_partial_link_text_present(partial_link_text): self.wait_for_partial_link_text_present( partial_link_text, timeout=timeout ) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) try: element = self.wait_for_partial_link_text( partial_link_text, timeout=0.2 ) self.__demo_mode_highlight_if_active( partial_link_text, by="link text" ) try: self.__element_click(element) except (Stale_Exception, ENI_Exception, ECI_Exception): self.wait_for_ready_state_complete() time.sleep(0.16) element = self.wait_for_partial_link_text( partial_link_text, timeout=timeout ) self.__element_click(element) except Exception: found_css = False text_id = self.get_partial_link_text_attribute( partial_link_text, "id", False ) if text_id: link_css = '[id="%s"]' % partial_link_text found_css = True if not found_css: href = self.__get_href_from_partial_link_text( partial_link_text, False ) if href: if href.startswith("/") or page_utils.is_valid_url(href): link_css = '[href="%s"]' % href found_css = True if not found_css: ngclick = self.get_partial_link_text_attribute( partial_link_text, "ng-click", False ) if ngclick: link_css = '[ng-click="%s"]' % ngclick found_css = True if not found_css: onclick = self.get_partial_link_text_attribute( partial_link_text, "onclick", False ) if onclick: link_css = '[onclick="%s"]' % onclick found_css = True success = False if found_css: if self.is_element_visible(link_css): self.click(link_css) success = True else: # The link text might be hidden under a dropdown menu success = self.__click_dropdown_partial_link_text( partial_link_text, link_css ) if not success: element = self.wait_for_partial_link_text( partial_link_text, timeout=settings.MINI_TIMEOUT ) self.__element_click(element) latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() elif ( latest_window_count == pre_window_count - 1 and latest_window_count > 0 ): # If a click closes the active window, # switch to the last one if it exists. self.switch_to_window(-1) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: with suppress(Exception): self.wait_for_ready_state_complete() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() def get_text(self, selector="body", by="css selector", timeout=None): self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): return self.cdp.get_text(selector) if self.__is_shadow_selector(selector): return self.__get_shadow_text(selector, timeout) self.wait_for_ready_state_complete() time.sleep(0.01) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout ) try: element_text = element.text if self.browser == "safari": if element.tag_name.lower() in ["input", "textarea"]: element_text = element.get_attribute("value") else: element_text = element.get_attribute("innerText") elif element.tag_name.lower() in ["input", "textarea"]: element_text = element.get_property("value") except (Stale_Exception, ENI_Exception, TimeoutException): self.wait_for_ready_state_complete() time.sleep(0.14) element = page_actions.wait_for_element_visible( self.driver, selector, by, timeout ) element_text = element.text if self.browser == "safari": if element.tag_name.lower() in ["input", "textarea"]: element_text = element.get_attribute("value") else: element_text = element.get_attribute("innerText") elif element.tag_name.lower() in ["input", "textarea"]: element_text = element.get_property("value") return element_text def get_attribute( self, selector, attribute, by="css selector", timeout=None, hard_fail=True, ): """This method uses JavaScript to get the value of an attribute. If the attribute doesn't exist or isn't found, an exception will get raised if hard_fail is True (otherwise None is returned).""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): if hard_fail: return self.cdp.get_element_attribute(selector, attribute) else: return self.cdp.get_attribute(selector, attribute) self.wait_for_ready_state_complete() time.sleep(0.01) if self.__is_shadow_selector(selector): return self.__get_shadow_attribute( selector, attribute, timeout=timeout ) element = page_actions.wait_for_element_present( self.driver, selector, by, timeout ) try: attribute_value = element.get_attribute(attribute) except (Stale_Exception, ENI_Exception, TimeoutException): self.wait_for_ready_state_complete() time.sleep(0.14) element = page_actions.wait_for_element_present( self.driver, selector, by, timeout ) attribute_value = element.get_attribute(attribute) if attribute_value is not None: return attribute_value else: if hard_fail: raise Exception( "Element {%s} has no attribute {%s}!" % (selector, attribute) ) else: return None def set_attribute( self, selector, attribute, value, by="css selector", timeout=None, scroll=False, ): """This method uses JavaScript to set/update an attribute. Only the first matching selector from querySelector() is used.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) original_attribute = attribute original_value = value if scroll and self.is_element_visible(selector, by=by): with suppress(Exception): self.scroll_to(selector, by=by, timeout=timeout) attribute = re.escape(attribute) attribute = self.__escape_quotes_if_needed(attribute) value = re.escape(value) value = self.__escape_quotes_if_needed(value) css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) script = ( """document.querySelector('%s').setAttribute('%s','%s');""" % (css_selector, attribute, value) ) self.execute_script(script) if self.recorder_mode and self.__current_url_is_recordable(): if self.get_session_storage_item("pause_recorder") == "no": time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() sele_attr_val = [selector, original_attribute, original_value] action = ["s_at_", sele_attr_val, origin, time_stamp] self.__extra_actions.append(action) def set_attributes(self, selector, attribute, value, by="css selector"): """This method uses JavaScript to set/update a common attribute. All matching selectors from querySelectorAll() are used. Example => (Make all links on a website redirect to Google): self.set_attributes("a", "href", "https://google.com")""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.set_attributes(selector, attribute, value) return original_attribute = attribute original_value = value attribute = re.escape(attribute) attribute = self.__escape_quotes_if_needed(attribute) value = re.escape(value) value = self.__escape_quotes_if_needed(value) css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) script = """var $elements = document.querySelectorAll('%s'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].setAttribute('%s','%s');}""" % ( css_selector, attribute, value, ) with suppress(Exception): self.execute_script(script) if self.recorder_mode and self.__current_url_is_recordable(): if self.get_session_storage_item("pause_recorder") == "no": time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() sele_attr_val = [selector, original_attribute, original_value] action = ["s_ats", sele_attr_val, origin, time_stamp] self.__extra_actions.append(action) def set_attribute_all(self, selector, attribute, value, by="css selector"): """Same as set_attributes(), but using querySelectorAll naming scheme. This method uses JavaScript to set/update a common attribute. All matching selectors from querySelectorAll() are used. Example => (Make all links on a website redirect to Google): self.set_attribute_all("a", "href", "https://google.com")""" self.set_attributes(selector, attribute, value, by=by) def remove_attribute( self, selector, attribute, by="css selector", timeout=None ): """This method uses JavaScript to remove an attribute. Only the first matching selector from querySelector() is used.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.is_element_visible(selector, by=by): with suppress(Exception): self.scroll_to(selector, by=by, timeout=timeout) attribute = re.escape(attribute) attribute = self.__escape_quotes_if_needed(attribute) css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) script = """document.querySelector('%s').removeAttribute('%s');""" % ( css_selector, attribute, ) self.execute_script(script) def remove_attributes(self, selector, attribute, by="css selector"): """This method uses JavaScript to remove a common attribute. All matching selectors from querySelectorAll() are used.""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) attribute = re.escape(attribute) attribute = self.__escape_quotes_if_needed(attribute) css_selector = self.convert_to_css_selector(selector, by=by) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) script = """var $elements = document.querySelectorAll('%s'); var index = 0, length = $elements.length; for(; index < length; index++){ $elements[index].removeAttribute('%s');}""" % ( css_selector, attribute, ) with suppress(Exception): self.execute_script(script) def internalize_links(self): """All `target="_blank"` links become `target="_self"`. This prevents those links from opening in a new tab.""" if self.__is_cdp_swap_needed(): self.cdp.internalize_links() return self.set_attributes('[target="_blank"]', "target", "_self") def get_parent(self, element, by="css selector", timeout=None): """Returns the parent element. If element is a string, then finds element first via selector.""" if self.__is_cdp_swap_needed(): return self.cdp.get_parent(element) if isinstance(element, str): if not timeout: timeout = settings.LARGE_TIMEOUT element = self.wait_for_element_present( element, by=by, timeout=timeout ) return element.find_element(by="xpath", value="..") def get_property( self, selector, property, by="css selector", timeout=None ): """Returns the property value of an element. This is not the same as self.get_property_value(), which returns the value of an element's computed style using a different algorithm. If no result is found, an empty string (instead of None) is returned. Example: html_text = self.get_property(SELECTOR, "textContent") """ self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) self.wait_for_ready_state_complete() time.sleep(0.01) element = page_actions.wait_for_element_present( self.driver, selector, by, timeout ) try: property_value = element.get_property(property) except (Stale_Exception, ENI_Exception, TimeoutException): self.wait_for_ready_state_complete() time.sleep(0.14) element = page_actions.wait_for_element_present( self.driver, selector, by, timeout ) property_value = element.get_property(property) if not property_value: return "" return property_value def get_text_content( self, selector="body", by="css selector", timeout=None ): """Returns the text that appears in the HTML for an element. This is different from "self.get_text(selector, by="css selector")" because that only returns the visible text on a page for an element, rather than the HTML text that's being returned from this method.""" self.__check_scope() return self.get_property( selector, property="textContent", by=by, timeout=timeout ) def get_property_value( self, selector, property, by="css selector", timeout=None ): """Returns the property value of a page element's computed style. Example: opacity = self.get_property_value("html body a", "opacity") self.assertTrue(float(opacity) > 0, "Element not visible!") """ self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) self.wait_for_ready_state_complete() element = page_actions.wait_for_element_present( self.driver, selector, by, timeout ) try: selector = self.convert_to_css_selector(selector, by=by) except Exception: # If can't convert to CSS_Selector for JS, use element directly script = ( """var $elm = arguments[0]; $val = window.getComputedStyle($elm).getPropertyValue('%s'); return $val;""" % property ) value = self.execute_script(script, element) if value is not None: return value else: return "" selector = re.escape(selector) selector = self.__escape_quotes_if_needed(selector) script = """var $elm = document.querySelector('%s'); $val = window.getComputedStyle($elm).getPropertyValue('%s'); return $val;""" % (selector, property) value = self.execute_script(script) if value is not None: return value else: return "" # Return an empty string if the property doesn't exist def get_image_url(self, selector, by="css selector", timeout=None): """Extracts the URL from an image element on the page.""" self.__check_scope() if not timeout: timeout = settings.LARGE_TIMEOUT if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: timeout = self.__get_new_timeout(timeout) return self.get_attribute( selector, attribute="src", by=by, timeout=timeout ) def find_elements(self, selector, by="css selector", limit=0): """Returns a list of matching WebElements. Elements could be either hidden or visible on the page. If "limit" is set and > 0, will only return that many elements.""" selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): elements = self.cdp.select_all(selector) if limit and limit > 0 and len(elements) > limit: elements = elements[:limit] return elements self.wait_for_ready_state_complete() time.sleep(0.05) elements = self.driver.find_elements(by=by, value=selector) if limit and limit > 0 and len(elements) > limit: elements = elements[:limit] return elements def find_visible_elements(self, selector, by="css selector", limit=0): """Returns a list of matching WebElements that are visible. If "limit" is set and > 0, will only return that many elements.""" selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): elements = self.cdp.find_visible_elements(selector) if limit and limit > 0 and len(elements) > limit: elements = elements[:limit] return elements self.wait_for_ready_state_complete() time.sleep(0.05) return page_actions.find_visible_elements( self.driver, selector, by, limit ) def click_visible_elements( self, selector, by="css selector", limit=0, timeout=None ): """Finds all matching page elements and clicks visible ones in order. If a click reloads or opens a new page, the clicking will stop. If no matching elements appear, an Exception will be raised. If "limit" is set and > 0, will only click that many elements. Also clicks elements that become visible from previous clicks. Works best for actions such as clicking all checkboxes on a page. Example: self.click_visible_elements('input[type="checkbox"]')""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.click_visible_elements(selector, limit) return self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.12) if self.undetectable: time.sleep(0.06) element = self.wait_for_element_present( selector, by=by, timeout=timeout ) with suppress(Exception): # If the first element isn't visible, wait a little. if not element.is_displayed(): time.sleep(0.16) if self.undetectable: time.sleep(0.06) elements = self.find_elements(selector, by=by) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) click_count = 0 for element in elements: if limit and limit > 0 and click_count >= limit: return try: if element.is_displayed(): self.__scroll_to_element(element) if self.browser == "safari": self.execute_script("arguments[0].click();", element) else: element.click() click_count += 1 self.wait_for_ready_state_complete() except ECI_Exception: continue # (Overlay likely) except (Stale_Exception, ENI_Exception): self.wait_for_ready_state_complete() time.sleep(0.12) try: if element.is_displayed(): self.__scroll_to_element(element) if self.browser == "safari": self.execute_script( "arguments[0].click();", element ) else: element.click() click_count += 1 self.wait_for_ready_state_complete() except (Stale_Exception, ENI_Exception): latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() return # Probably on new page / Elements are all stale latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() def click_nth_visible_element( self, selector, number, by="css selector", timeout=None ): """Finds all matching page elements and clicks the nth visible one. Example: self.click_nth_visible_element('[type="checkbox"]', 5) (Clicks the 5th visible checkbox on the page.)""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.click_nth_visible_element(selector, number) return self.wait_for_ready_state_complete() self.wait_for_element_present(selector, by=by, timeout=timeout) elements = self.find_visible_elements(selector, by=by) if len(elements) < number: raise Exception( "Not enough matching {%s} elements of type {%s} to " "click number %s!" % (selector, by, number) ) number = number - 1 if number < 0: number = 0 element = elements[number] pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) try: self.__scroll_to_element(element) self.__element_click(element) except (Stale_Exception, ENI_Exception, ECI_Exception): time.sleep(0.12) self.wait_for_ready_state_complete() self.wait_for_element_present(selector, by=by, timeout=timeout) elements = self.find_visible_elements(selector, by=by) if len(elements) < number: raise Exception( "Not enough matching {%s} elements of type {%s} to " "click number %s!" % (selector, by, number) ) number = number - 1 if number < 0: number = 0 element = elements[number] self.__element_click(element) latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() def click_if_visible(self, selector, by="css selector", timeout=0): """If the page selector exists and is visible, clicks on the element. This method only clicks on the first matching element found. Use click_visible_elements() to click all matching elements. If a "timeout" is provided, waits that long for the element to appear before giving up and returning without a click().""" if self.__is_cdp_swap_needed(): self.cdp.click_if_visible(selector, timeout=timeout) return self.wait_for_ready_state_complete() if self.is_element_visible(selector, by=by): self.click(selector, by=by) elif timeout > 0: with suppress(Exception): self.wait_for_element_visible( selector, by=by, timeout=timeout ) self.sleep(0.2) if self.is_element_visible(selector, by=by): self.click(selector, by=by) def click_active_element(self): if self.__is_cdp_swap_needed(): self.cdp.click_active_element() return self.wait_for_ready_state_complete() pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) if self.recorder_mode: selector = js_utils.get_active_element_css(self.driver) self.click(selector) return else: self.execute_script("document.activeElement.click();") latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() if settings.WAIT_FOR_RSC_ON_CLICKS: self.wait_for_ready_state_complete() else: # A smaller subset of self.wait_for_ready_state_complete() self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT) if self.driver.current_url != pre_action_url: self.__ad_block_as_needed() self.__disable_beforeunload_as_needed() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() def click_with_offset( self, selector, x, y, by="css selector", mark=None, timeout=None, center=None, ): """Click an element at an {X,Y}-offset location. {0,0} is the top-left corner of the element. If center==True, {0,0} becomes the center of the element. If mark==True, will draw a dot at location. (Useful for debugging) In Demo Mode, mark becomes True unless set to False. (Default: None)""" self.__check_scope() self.__click_with_offset( selector, x, y, by=by, double=False, mark=mark, timeout=timeout, center=center, ) def double_click_with_offset( self, selector, x, y, by="css selector", mark=None, timeout=None, center=None, ): """Double click an element at an {X,Y}-offset location. {0,0} is the top-left corner of the element. If center==True, {0,0} becomes the center of the element. If mark==True, will draw a dot at location. (Useful for debugging) In Demo Mode, mark becomes True unless set to False. (Default: None)""" self.__check_scope() self.__click_with_offset( selector, x, y, by=by, double=True, mark=mark, timeout=timeout, center=center, ) def is_checked(self, selector, by="css selector", timeout=None): """Determines if a checkbox or a radio button element is checked. Returns True if the element is checked. Returns False if the element is not checked. If the element is not present on the page, raises an exception. If the element is not a checkbox or radio, raises an exception.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): return self.cdp.is_checked(selector) kind = self.get_attribute(selector, "type", by=by, timeout=timeout) if kind != "checkbox" and kind != "radio": raise Exception("Expecting a checkbox or a radio button element!") return bool( self.get_attribute( selector, "checked", by=by, timeout=timeout, hard_fail=False ) ) def is_selected(self, selector, by="css selector", timeout=None): """Same as is_checked()""" return self.is_checked(selector, by=by, timeout=timeout) def check_if_unchecked(self, selector, by="css selector"): """If a checkbox or radio button is not checked, will check it.""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.check_if_unchecked(selector) return if not self.is_checked(selector, by=by): if self.is_element_visible(selector, by=by): self.click(selector, by=by) else: element = self.wait_for_element_present(selector, by=by) opacity = self.execute_script( 'return arguments[0].style.opacity;', element ) # Handle switches that sit on checkboxes with zero opacity: # Change the opacity a bit to allow the click to succeed. with suppress(Exception): self.execute_script( 'arguments[0].style.opacity="0.001";', element ) if self.is_element_visible(selector, by=by): self.click(selector, by=by) else: selector = self.convert_to_css_selector(selector, by=by) self.__dont_record_js_click = True self.js_click(selector, by="css selector") self.__dont_record_js_click = False with suppress(Exception): self.execute_script( 'arguments[0].style.opacity="arguments[1]";', element, opacity, ) def select_if_unselected(self, selector, by="css selector"): """Same as check_if_unchecked()""" self.check_if_unchecked(selector, by=by) def uncheck_if_checked(self, selector, by="css selector"): """If a checkbox is checked, will uncheck it.""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.uncheck_if_checked(selector) return if self.is_checked(selector, by=by): if self.is_element_visible(selector, by=by): self.click(selector, by=by) else: element = self.wait_for_element_present(selector, by=by) opacity = self.execute_script( 'return arguments[0].style.opacity;', element ) # Handle switches that sit on checkboxes with zero opacity: # Change the opacity a bit to allow the click to succeed. with suppress(Exception): self.execute_script( 'arguments[0].style.opacity="0.001";', element ) if self.is_element_visible(selector, by=by): self.click(selector, by=by) else: selector = self.convert_to_css_selector(selector, by=by) self.__dont_record_js_click = True self.js_click(selector, by="css selector") self.__dont_record_js_click = False with suppress(Exception): self.execute_script( 'arguments[0].style.opacity="arguments[1]";', element, opacity, ) def unselect_if_selected(self, selector, by="css selector"): """Same as uncheck_if_checked()""" self.uncheck_if_checked(selector, by=by) def is_element_in_an_iframe(self, selector, by="css selector"): """Returns True if the selector's element is located in an iframe. Otherwise returns False.""" self.__check_scope() selector, by = self.__recalculate_selector(selector, by) if self.is_element_present(selector, by=by): return False soup = self.get_beautiful_soup() iframe_list = soup.select("iframe") for iframe in iframe_list: iframe_identifier = None if iframe.has_attr("name") and len(iframe["name"]) > 0: iframe_identifier = iframe["name"] elif iframe.has_attr("id") and len(iframe["id"]) > 0: iframe_identifier = iframe["id"] elif iframe.has_attr("class") and len(iframe["class"]) > 0: iframe_class = " ".join(iframe["class"]) iframe_identifier = '[class="%s"]' % iframe_class else: continue self.switch_to_frame(iframe_identifier) if self.is_element_present(selector, by=by): self.switch_to_default_content() return True self.switch_to_default_content() return False def switch_to_frame_of_element(self, selector, by="css selector"): """Set driver control to the iframe containing element (assuming the element is in a single-nested iframe) and returns the iframe name. If element is not in an iframe, returns None, and nothing happens. May not work if multiple iframes are nested within each other.""" self.wait_for_ready_state_complete() if self.__needs_minimum_wait(): time.sleep(0.04) if self.undetectable: time.sleep(0.04) selector, by = self.__recalculate_selector(selector, by) if self.is_element_present(selector, by=by): return None soup = self.get_beautiful_soup() iframe_list = soup.select("iframe") for iframe in iframe_list: iframe_identifier = None if iframe.has_attr("name") and len(iframe["name"]) > 0: iframe_identifier = iframe["name"] elif iframe.has_attr("id") and len(iframe["id"]) > 0: iframe_identifier = iframe["id"] elif iframe.has_attr("class") and len(iframe["class"]) > 0: iframe_class = " ".join(iframe["class"]) iframe_identifier = '[class="%s"]' % iframe_class else: continue with suppress(Exception): self.switch_to_frame(iframe_identifier, timeout=1) if self.__needs_minimum_wait(): time.sleep(0.02) if self.is_element_present(selector, by=by): return iframe_identifier self.switch_to_default_content() if self.__needs_minimum_wait(): time.sleep(0.02) try: self.switch_to_frame(selector, timeout=1) if self.undetectable: self.__uc_frame_layer += 1 return selector except Exception: if self.is_element_present(selector, by=by): return "" raise Exception( "Could not switch to iframe containing " "element {%s}!" % selector ) def hover(self, selector, by="css selector", timeout=None): self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) original_selector = selector original_by = by selector, by = self.__recalculate_selector(selector, by) if self.__is_cdp_swap_needed(): self.cdp.hover_element(selector) return self.wait_for_element_visible( original_selector, by=original_by, timeout=timeout ) self.__demo_mode_highlight_if_active(original_selector, original_by) self.scroll_to(selector, by=by) time.sleep(0.05) # Settle down from scrolling before hovering element = page_actions.hover_on_element(self.driver, selector, by) if self.recorder_mode and self.__current_url_is_recordable(): if self.get_session_storage_item("pause_recorder") == "no": if by == By.XPATH: selector = original_selector time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() action = ["hover", selector, origin, time_stamp] self.__extra_actions.append(action) return element def hover_and_click( self, hover_selector, click_selector, hover_by="css selector", click_by="css selector", timeout=None, js_click=False, ): """When you want to hover over an element or dropdown menu, and then click an element that appears after that.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) original_selector = hover_selector original_by = hover_by hover_selector, hover_by = self.__recalculate_selector( hover_selector, hover_by ) original_click_selector = click_selector click_selector, click_by = self.__recalculate_selector( click_selector, click_by ) if self.__is_cdp_swap_needed(): self.cdp.hover_and_click(hover_selector, click_selector) return dropdown_element = self.wait_for_element_visible( original_selector, by=original_by, timeout=timeout ) self.__demo_mode_highlight_if_active(original_selector, original_by) self.scroll_to(hover_selector, by=hover_by) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) if self.recorder_mode and self.__current_url_is_recordable(): if self.get_session_storage_item("pause_recorder") == "no": if hover_by == By.XPATH: hover_selector = original_selector if click_by == By.XPATH: click_selector = original_click_selector time_stamp = self.execute_script("return Date.now();") origin = self.get_origin() the_selectors = [hover_selector, click_selector] action = ["ho_cl", the_selectors, origin, time_stamp] self.__extra_actions.append(action) outdated_driver = False element = None try: if self.mobile_emulator: # On mobile, click to hover the element dropdown_element.click() else: page_actions.hover_element(self.driver, dropdown_element) except Exception: outdated_driver = True element = self.wait_for_element_present( click_selector, click_by, timeout ) if click_by == By.LINK_TEXT: self.open(self.__get_href_from_link_text(click_selector)) elif click_by == By.PARTIAL_LINK_TEXT: self.open( self.__get_href_from_partial_link_text(click_selector) ) else: self.__dont_record_js_click = True self.js_click(click_selector, by=click_by) self.__dont_record_js_click = False if outdated_driver: pass # Already did the click workaround elif self.mobile_emulator: self.click(click_selector, by=click_by) elif not outdated_driver: element = page_actions.hover_and_click( self.driver, hover_selector, click_selector, hover_by, click_by, timeout, js_click, ) latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() elif self.browser == "safari": # Release the hover by hovering elsewhere with suppress(Exception): page_actions.hover_on_element(self.driver, "body") if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() return element def hover_and_js_click( self, hover_selector, click_selector, hover_by="css selector", click_by="css selector", timeout=None, ): self.hover_and_click( hover_selector=hover_selector, click_selector=click_selector, hover_by=hover_by, click_by=click_by, timeout=timeout, js_click=True, ) def hover_and_double_click( self, hover_selector, click_selector, hover_by="css selector", click_by="css selector", timeout=None, ): """When you want to hover over an element or dropdown menu, and then double-click an element that appears after that.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) original_selector = hover_selector original_by = hover_by hover_selector, hover_by = self.__recalculate_selector( hover_selector, hover_by ) hover_selector = self.convert_to_css_selector(hover_selector, hover_by) hover_by = By.CSS_SELECTOR click_selector, click_by = self.__recalculate_selector( click_selector, click_by ) dropdown_element = self.wait_for_element_visible( original_selector, by=original_by, timeout=timeout ) self.__demo_mode_highlight_if_active(original_selector, original_by) self.scroll_to(hover_selector, by=hover_by) pre_action_url = None with suppress(Exception): pre_action_url = self.driver.current_url pre_window_count = len(self.driver.window_handles) outdated_driver = False element = None try: page_actions.hover_element(self.driver, dropdown_element) except Exception: outdated_driver = True element = self.wait_for_element_present( click_selector, click_by, timeout ) if click_by == By.LINK_TEXT: self.open(self.__get_href_from_link_text(click_selector)) elif click_by == By.PARTIAL_LINK_TEXT: self.open( self.__get_href_from_partial_link_text(click_selector) ) else: self.__dont_record_js_click = True self.js_click(click_selector, click_by) self.__dont_record_js_click = False if not outdated_driver: element = page_actions.hover_element_and_double_click( self.driver, dropdown_element, click_selector, click_by="css selector", timeout=timeout, ) latest_window_count = len(self.driver.window_handles) if ( latest_window_count > pre_window_count and ( self.recorder_mode or ( settings.SWITCH_TO_NEW_TABS_ON_CLICK and self.driver.current_url == pre_action_url ) ) ): self.__switch_to_newest_window_if_not_blank() if self.demo_mode: if self.driver.current_url != pre_action_url: if not js_utils.is_jquery_activated(self.driver): js_utils.add_js_link(self.driver, constants.JQuery.MIN_JS) self.__demo_mode_pause_if_active() else: self.__demo_mode_pause_if_active(tiny=True) elif self.slow_mode: self.__slow_mode_pause_if_active() return element def drag_and_drop( self, drag_selector, drop_selector, drag_by="css selector", drop_by="css selector", timeout=None, jquery=False, ): """Drag-and-drop an element from one selector to another.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) drag_selector, drag_by = self.__recalculate_selector( drag_selector, drag_by ) drop_selector, drop_by = self.__recalculate_selector( drop_selector, drop_by ) if self.__is_cdp_swap_needed(): self.cdp.gui_drag_and_drop(drag_selector, drop_selector) return drag_element = self.wait_for_element_clickable( drag_selector, by=drag_by, timeout=timeout ) self.__demo_mode_highlight_if_active(drag_selector, drag_by) drop_element = self.wait_for_element_visible( drop_selector, by=drop_by, timeout=timeout ) self.__demo_mode_highlight_if_active(drop_selector, drop_by) self.scroll_to(drop_selector, by=drop_by) drag_selector = self.convert_to_css_selector(drag_selector, drag_by) drop_selector = self.convert_to_css_selector(drop_selector, drop_by) if not jquery: drag_and_drop_script = js_utils.get_js_drag_and_drop_script() self.execute_script( drag_and_drop_script, drag_element, drop_element, 0, 0, 1, None ) else: drag_and_drop_script = js_utils.get_drag_and_drop_script() self.safe_execute_script( drag_and_drop_script + ( "$('%s').simulateDragDrop(" "{dropTarget: " "'%s'});" % (drag_selector, drop_selector) ) ) if self.demo_mode: self.__demo_mode_pause_if_active() elif self.slow_mode: self.__slow_mode_pause_if_active() return drag_element def drag_and_drop_with_offset( self, selector, x, y, by="css selector", timeout=None ): """Drag-and-drop an element to an {X,Y}-offset location.""" self.__check_scope() if not timeout: timeout = settings.SMALL_TIMEOUT if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: timeout = self.__get_new_timeout(timeout) selector, by = self.__recalculate_selector(selector, by) css_selector = self.convert_to_css_selector(selector, by=by) element = self.wait_for_element_visible(css_selector, timeout=timeout) self.__demo_mode_highlight_if_active(css_selector, By.CSS_SELECTOR) css_selector = re.escape(css_selector) # Add "\\" to special chars css_selector = self.__escape_quotes_if_needed(css_selector) script = js_utils.get_drag_and_drop_with_offset_script( css_selector, x, y ) self.execute_script(script) if self.demo_mode: self.__demo_mode_pause_if_active() elif self.slow_mode: self.__slow_mode_pause_if_active() return element def __element_click(self, element): self.__check_scope() if ( not self.undetectable or self.uc_cdp_events or self.__uc_frame_layer > 0 or not hasattr(element, "uc_click") or element.tag_name.lower() != "a" ): element.click() else: try: href = element.get_attribute("href") target = element.get_attribute("target") if len(href) > 0 and target != "_blank": element.uc_click() else: element.click() time.sleep(0.012) except Exception: element.click() def __select_option( self, dropdown_selector, option, dropdown_by="css selector", option_by="text", timeout=None, ): """Selects an HTML option by option text. @Params dropdown_selector - the option by option index. @Params dropdown_selector - the option by option value. @Params dropdown_selector - the ' => '//*[@id="user[login]"]' # Need to tell apart string-brackets from regular brackets new_xpath = "" chunks = xpath.split('"') len_chunks = len(chunks) for chunk_num in range(len_chunks): if chunk_num % 2 != 0: chunks[chunk_num] = chunks[chunk_num].replace( "[", "_STR_L_bracket_" ) chunks[chunk_num] = chunks[chunk_num].replace( "]", "_STR_R_bracket_" ) new_xpath += chunks[chunk_num] if chunk_num != len_chunks - 1: new_xpath += '"' return new_xpath def _filter_xpath_grouping(xpath, original): """ This method removes the outer parentheses for xpath grouping. The xpath converter will break otherwise. Example: "(//button[@type='submit'])[1]" becomes "//button[@type='submit'][1]" """ # First remove the first open parentheses xpath = xpath[1:] # Next remove the last closed parentheses index = xpath.rfind(")") index_p1 = index + 1 # Make "flake8" and "black" agree if index == -1: raise XpathException( "\nInvalid or unsupported XPath:\n%s\n" "(Unable to convert XPath Selector to CSS Selector)" "" % original ) xpath = xpath[:index] + xpath[index_p1:] return xpath def _get_raw_css_from_xpath(xpath, original): css = "" attr = "" position = 0 while position < len(xpath): node = prog.match(xpath[position:]) if node is None: raise XpathException( "\nInvalid or unsupported XPath:\n%s\n" "(Unable to convert XPath Selector to CSS Selector)" "" % original ) match = node.groupdict() if position != 0: nav = " " if match["nav"] == "//" else " > " else: nav = "" tag = "" if match["tag"] == "*" else match["tag"] or "" if match["idvalue"]: attr = "#%s" % match["idvalue"].replace(" ", "#") elif match["matched"]: if match["mattr"] == "@id": attr = "#%s" % match["mvalue"].replace(" ", "#") elif match["mattr"] == "@class": attr = ".%s" % match["mvalue"].replace(" ", ".") elif match["mattr"] in ["text()", "."]: attr = ":contains('%s')" % match["mvalue"] elif match["mattr"]: attr = '[%s="%s"]' % ( match["mattr"].replace("@", ""), match["mvalue"], ) elif match["contained"]: if match["cattr"].startswith("@"): attr = '[%s*="%s"]' % ( match["cattr"].replace("@", ""), match["cvalue"], ) elif match["cattr"] == "text()": attr = ':contains("%s")' % match["cvalue"] elif match["cattr"] == ".": attr = ':contains("%s")' % match["cvalue"] else: attr = "" if match["nth"]: nth = ":nth-of-type(%s)" % match["nth"] else: nth = "" node_css = nav + tag + attr + nth css += node_css position += node.end() else: css = css.strip() return css def convert_xpath_to_css(xpath): original = xpath xpath = xpath.replace(" = '", "='") # **** Start of handling special xpath edge cases instantly **** # Handle a special edge case that converts to: 'tag.class:contains("TEXT")' c3 = "@class and contains(concat(' ', normalize-space(@class), ' '), ' " if c3 in xpath and xpath.count(c3) == 1 and xpath.count("[@") == 1: p2 = " ') and (contains(., '" if ( xpath.count(p2) == 1 and xpath.endswith("'))]") and xpath.count("//") == 1 and xpath.count(" ') and (") == 1 ): s_contains = xpath.split(p2)[1].split("'))]")[0] s_tag = xpath.split("//")[1].split("[@class")[0] s_class = xpath.split(c3)[1].split(" ') and (")[0] return '%s.%s:contains("%s")' % (s_tag, s_class, s_contains) # Find instance of: //tag[@attribute='value' and (contains(., 'TEXT'))] data = re.match( r"""^\s*//(\S+)\[@(\S+)='(\S+)'\s+and\s+""" r"""\(contains\(\.,\s'(\S+)'\)\)\]""", xpath, ) if data: s_tag = data.group(1) s_atr = data.group(2) s_val = data.group(3) s_contains = data.group(4) return '%s[%s="%s"]:contains("%s")' % (s_tag, s_atr, s_val, s_contains) # Find instance of: //tag[@attribute1='value1' and (@attribute2='value2')] data = re.match( r"""^\s*//(\S+)\[@(\S+)='(\S+)'\s+and\s+""" r"""\(@(\S+)='(\S+)'\)\]""", xpath, ) if data: s_tag = data.group(1) s_atr1 = data.group(2) s_val1 = data.group(3) s_atr2 = data.group(4) s_val2 = data.group(5) return '%s[%s="%s"][%s="%s"]' % (s_tag, s_atr1, s_val1, s_atr2, s_val2) # **** End of handling special xpath edge cases instantly **** if xpath[0] != '"' and xpath[-1] != '"' and xpath.count('"') % 2 == 0: xpath = _handle_brackets_in_strings(xpath) xpath = xpath.replace("descendant-or-self::*/", "descORself/") if len(xpath) > 3: xpath = xpath[0:3] + xpath[3:].replace("//", "/descORself/") if " and contains(@" in xpath and xpath.count(" and contains(@") == 1: spot1 = xpath.find(" and contains(@") spot1 = spot1 + len(" and contains(@") spot2 = xpath.find(",", spot1) attr = xpath[spot1:spot2] swap = " and contains(@%s, " % attr if swap in xpath: swap_spot = xpath.find(swap) close_paren = xpath.find("]", swap_spot) - 1 close_paren_p1 = close_paren + 1 # Make "flake8" and "black" agree if close_paren > 1: xpath = xpath[:close_paren] + xpath[close_paren_p1:] xpath = xpath.replace(swap, "_STAR_=") if xpath.startswith("("): xpath = _filter_xpath_grouping(xpath, original) css = "" if "/descORself/" in xpath and ("@id" in xpath or "@class" in xpath): css_sections = [] xpath_sections = xpath.split("/descORself/") for xpath_section in xpath_sections: if not xpath_section.startswith("//"): xpath_section = "//" + xpath_section css_sections.append( _get_raw_css_from_xpath(xpath_section, original) ) css = "/descORself/".join(css_sections) else: css = _get_raw_css_from_xpath(xpath, original) attribute_defs = re.findall(r"(\[\w+\=\S+\])", css) for attr_def in attribute_defs: if ( attr_def.count("[") == 1 and attr_def.count("]") == 1 and attr_def.count("=") == 1 and attr_def.count('"') == 0 and attr_def.count("'") == 0 and attr_def.count(" ") == 0 ): # Now safe to manipulate q1 = attr_def.find("=") + 1 q2 = attr_def.find("]") new_attr_def = attr_def[:q1] + "'" + attr_def[q1:q2] + "']" css = css.replace(attr_def, new_attr_def) # Replace the string-brackets with escaped ones css = css.replace("_STR_L_bracket_", "\\[") css = css.replace("_STR_R_bracket_", "\\]") # Handle a lot of edge cases with conversion css = css.replace(" > descORself > ", " ") css = css.replace(" descORself > ", " ") css = css.replace("/descORself/*", " ") css = css.replace("/descORself/", " ") css = css.replace("descORself > ", "") css = css.replace("descORself/", " ") css = css.replace("descORself", " ") css = css.replace("_STAR_=", "*=") css = css.replace("]/", "] ") css = css.replace("] *[", "] > [") css = css.replace("'", '"') css = css.replace("[@", "[") return css ================================================ FILE: seleniumbase/js_code/__init__.py ================================================ ================================================ FILE: seleniumbase/js_code/active_css_js.py ================================================ ############################################################################### # active_css_js - Return the Best CSS Selector of the Currently Active Element. ############################################################################### get_active_element_css = r""" var cssPathById = function(el) { if (!(el instanceof Element)) return; var path = []; while (el != null && el.nodeType === Node.ELEMENT_NODE) { var selector = el.nodeName.toLowerCase(); if (el.id) { elid = el.id; if (/\s/.test(elid) || elid.includes(',') || elid.includes('.') || elid.includes('(') || elid.includes(':') || hasDigit(elid[0])) return cssPathByAttribute(el, 'id'); selector += '#' + elid; path.unshift(selector); break; } else { var sib = el, nth = 1; while (sib = sib.previousElementSibling) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ':nth-of-type('+nth+')'; } path.unshift(selector); el = el.parentNode; } return path.join(' > '); }; var cssPathByAttribute = function(el, attr) { if (!(el instanceof Element)) return; var path = []; while (el !== null && el.nodeType === Node.ELEMENT_NODE) { var selector = el.nodeName.toLowerCase(); if (el.hasAttribute(attr) && el.getAttribute(attr).length > 0 && !el.getAttribute(attr).includes('\n')) { the_attr = el.getAttribute(attr); the_attr = the_attr.replaceAll('"', '\\"'); the_attr = the_attr.replaceAll("'", "\\'"); selector += '[' + attr + '="' + the_attr + '"]'; path.unshift(selector); break; } else { var sib = el, nth = 1; while (sib = sib.previousElementSibling) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ':nth-of-type('+nth+')'; } path.unshift(selector); el = el.parentNode; } return path.join(' > '); }; var cssPathByClass = function(el) { if (!(el instanceof Element)) return; var path = []; while (el !== null && el.nodeType === Node.ELEMENT_NODE) { var selector = el.nodeName.toLowerCase(); if (el.hasAttribute('class') && el.getAttribute('class').length > 0 && !el.getAttribute('class').includes(' ') && (el.getAttribute('class').includes('-')) && document.querySelectorAll( selector + '.' + el.getAttribute('class')).length == 1) { selector += '.' + el.getAttribute('class'); path.unshift(selector); break; } else { var sib = el, nth = 1; while (sib = sib.previousElementSibling) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ':nth-of-type('+nth+')'; } path.unshift(selector); el = el.parentNode; } return path.join(' > '); }; var ssOccurrences = function(string, subString, allowOverlapping) { if (subString.length <= 0) return (string.length + 1); var n = 0; var pos = 0; var step = allowOverlapping ? 1 : subString.length; while (true) { pos = string.indexOf(subString, pos); if (pos >= 0) { ++n; pos += step; } else break; } return n; }; function hasDigit(str) { return /\d/.test(str); }; function isGen(str) { return /[_-]\d/.test(str) || /\d[a-z]/.test(str); }; function tagName(el) { return el.tagName.toLowerCase(); }; function turnIntoParentAsNeeded(el) { if (tagName(el) == 'span' || tagName(el) == 'i') { if (tagName(el.parentElement) == 'button') { el = el.parentElement; } else if (tagName(el.parentElement.parentElement) == 'button') { el = el.parentElement.parentElement; } } return el; } var getBestSelector = function(el) { if (!(el instanceof Element)) return; el = turnIntoParentAsNeeded(el); sel_by_id = cssPathById(el); if (!sel_by_id.includes(' > ') && !isGen(sel_by_id)) return sel_by_id; child_count_by_id = ssOccurrences(sel_by_id, ' > '); selector_by_class = cssPathByClass(el); tag_name = tagName(el); non_id_attributes = []; non_id_attributes.push('name'); non_id_attributes.push('data-qa'); non_id_attributes.push('data-tid'); non_id_attributes.push('data-el'); non_id_attributes.push('data-se'); non_id_attributes.push('data-name'); non_id_attributes.push('data-auto'); non_id_attributes.push('data-text'); non_id_attributes.push('data-test'); non_id_attributes.push('data-testid'); non_id_attributes.push('data-test-id'); non_id_attributes.push('data-test-selector'); non_id_attributes.push('data-nav'); non_id_attributes.push('data-sb'); non_id_attributes.push('data-cy'); non_id_attributes.push('data-action'); non_id_attributes.push('data-target'); non_id_attributes.push('data-tooltip'); non_id_attributes.push('alt'); non_id_attributes.push('title'); non_id_attributes.push('heading'); non_id_attributes.push('translate'); non_id_attributes.push('aria-label'); non_id_attributes.push('aria-describedby'); non_id_attributes.push('rel'); non_id_attributes.push('ng-model'); non_id_attributes.push('ng-href'); non_id_attributes.push('href'); non_id_attributes.push('label'); non_id_attributes.push('data-content'); non_id_attributes.push('data-tip'); non_id_attributes.push('data-for'); non_id_attributes.push('class'); non_id_attributes.push('for'); non_id_attributes.push('placeholder'); non_id_attributes.push('value'); non_id_attributes.push('ng-click'); non_id_attributes.push('ng-if'); non_id_attributes.push('src'); selector_by_attr = []; all_by_attr = []; num_by_attr = []; child_count_by_attr = []; for (var i = 0; i < non_id_attributes.length; i++) { n_i_attr = non_id_attributes[i]; selector_by_attr[i] = null; if (n_i_attr == 'class') selector_by_attr[i] = selector_by_class; else selector_by_attr[i] = cssPathByAttribute(el, n_i_attr); all_by_attr[i] = document.querySelectorAll(selector_by_attr[i]); num_by_attr[i] = all_by_attr[i].length; if (!selector_by_attr[i].includes(' > ') && ((num_by_attr[i] == 1) || (el == all_by_attr[i][0]))) { if (n_i_attr.startsWith('aria') || n_i_attr == 'for') if (hasDigit(selector_by_attr[i])) continue; return selector_by_attr[i]; } child_count_by_attr[i] = ssOccurrences(selector_by_attr[i], ' > '); } basic_tags = []; basic_tags.push('h1'); basic_tags.push('h2'); basic_tags.push('h3'); basic_tags.push('canvas'); basic_tags.push('center'); basic_tags.push('input'); basic_tags.push('textarea'); for (var i = 0; i < basic_tags.length; i++) { d_qsa = document.querySelectorAll(basic_tags[i]); if (tag_name == basic_tags[i] && d_qsa.length == 1 && el == d_qsa[0]) return basic_tags[i]; } contains_tags = []; contains_tags.push('a'); contains_tags.push('b'); contains_tags.push('h1'); contains_tags.push('h2'); contains_tags.push('h3'); contains_tags.push('h4'); contains_tags.push('h5'); contains_tags.push('code'); contains_tags.push('mark'); contains_tags.push('button'); contains_tags.push('label'); contains_tags.push('legend'); contains_tags.push('li'); contains_tags.push('td'); contains_tags.push('th'); contains_tags.push('i'); contains_tags.push('small'); contains_tags.push('strong'); contains_tags.push('summary'); contains_tags.push('span'); all_by_tag = []; text_content = ''; if (el.textContent) text_content = el.textContent.trim(); for (var i = 0; i < contains_tags.length; i++) { if (tag_name == contains_tags[i] && text_content.length >= 2 && text_content.length <= 64) { t_count = 0; all_by_tag[i] = document.querySelectorAll(contains_tags[i]); for (var j = 0; j < all_by_tag[i].length; j++) { if (all_by_tag[i][j].textContent.includes(text_content)) t_count += 1; } if (t_count === 1 && !text_content.includes('\n')) { text_content = text_content.replaceAll("'", "\\'"); text_content = text_content.replaceAll('"', '\\"'); return tag_name += ':contains("'+text_content+'")'; } } } best_selector = sel_by_id; lowest_child_count = child_count_by_id; child_count_by_class = ssOccurrences(selector_by_class, ' > '); if (child_count_by_class < lowest_child_count) { best_selector = selector_by_class; lowest_child_count = child_count_by_class; } for (var i = 0; i < non_id_attributes.length; i++) { if (child_count_by_attr[i] < lowest_child_count && ((num_by_attr[i] == 1) || (el == all_by_attr[i][0]))) { best_selector = selector_by_attr[i]; lowest_child_count = child_count_by_attr[i]; } } best_selector = best_selector.replaceAll('html > body', 'body'); selector = best_selector.replaceAll(' > ', ' '); selector = selector.replaceAll(' div ', ' '); if (document.querySelector(selector) == el) best_selector = selector; return best_selector; }; return getBestSelector(document.activeElement); """ ================================================ FILE: seleniumbase/js_code/live_js.py ================================================ ############################################################################### # live_js - Reload web pages when changes are detected on the server side. ############################################################################### live_js = r""" (function () { var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 }, resources = {}, pendingRequests = {}, currentLinkElements = {}, oldLinkElements = {}, interval = 1000, loaded = false, active = { "html": 1, "css": 1, "js": 1 }; var Live = { heartbeat: function () { if (document.body) { if (!loaded) Live.loadresources(); Live.checkForChanges(); } setTimeout(Live.heartbeat, interval); }, loadresources: function () { function isLocal(url) { var loc = document.location, reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host); return url.match(reg); } var scripts = document.getElementsByTagName("script"), links = document.getElementsByTagName("link"), uris = []; for (var i = 0; i < scripts.length; i++) { var script = scripts[i], src = script.getAttribute("src"); if (src && isLocal(src)) uris.push(src); if (src && src.match(/\blive.js#/)) { for (var type in active) active[type] = src.match("[#,|]" + type) != null if (src.match("notify")) alert("Live.js is loaded."); } } if (!active.js) uris = []; if (active.html) uris.push(document.location.href); for (var i = 0; i < links.length && active.css; i++) { var link = links[i], rel = link.getAttribute("rel" ),href = link.getAttribute("href", 2); if (href && rel && rel.match(new RegExp("stylesheet", "i") ) && isLocal(href)) { uris.push(href); currentLinkElements[href] = link; } } for (var i = 0; i < uris.length; i++) { var url = uris[i]; Live.getHead(url, function (url, info) { resources[url] = info; }); } var head = document.getElementsByTagName("head")[0], style = document.createElement("style"), rule = "transition: all .3s ease-out;" css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join(''); style.setAttribute("type", "text/css"); head.appendChild(style); style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css)); loaded = true; }, checkForChanges: function () { for (var url in resources) { if (pendingRequests[url]) continue; Live.getHead(url, function (url, newInfo) { var oldInfo = resources[url], hasChanged = false; resources[url] = newInfo; for (var header in oldInfo) { var oldValue = oldInfo[header], newValue = newInfo[header], contentType = newInfo["Content-Type"]; switch (header.toLowerCase()) { case "etag": if (!newValue) break; default: hasChanged = oldValue != newValue; break; } if (hasChanged) { Live.refreshResource(url, contentType); break; } } }); } }, refreshResource: function (url, type) { switch (type.toLowerCase()) { case "text/css": var link = currentLinkElements[url], html = document.body.parentNode, head = link.parentNode, next = link.nextSibling, newLink = document.createElement("link"); html.className = html.className.replace(/\s*livejs\-loading/gi, '' ) + ' livejs-loading'; newLink.setAttribute("type", "text/css"); newLink.setAttribute("rel", "stylesheet"); newLink.setAttribute("href", url + "?now=" + new Date() * 1); next ? head.insertBefore(newLink, next) : head.appendChild( newLink); currentLinkElements[url] = newLink; oldLinkElements[url] = link; Live.removeoldLinkElements(); break; case "text/html": if (url != document.location.href) return; case "text/javascript": case "application/javascript": case "application/x-javascript": document.location.reload(); } }, removeoldLinkElements: function () { var pending = 0; for (var url in oldLinkElements) { try { var link = currentLinkElements[url], oldLink = oldLinkElements[url], html = document.body.parentNode, sheet = link.sheet || link.styleSheet, rules = sheet.rules || sheet.cssRules; if (rules.length >= 0) { oldLink.parentNode.removeChild(oldLink); delete oldLinkElements[url]; setTimeout(function () { html.className = html.className.replace( /\s*livejs\-loading/gi, ''); }, 100); } } catch (e) { pending++; } if (pending) setTimeout(Live.removeoldLinkElements, 50); } }, getHead: function (url, callback) { pendingRequests[url] = true; var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp"); xhr.open("HEAD", url, true); xhr.onreadystatechange = function () { delete pendingRequests[url]; if (xhr.readyState == 4 && xhr.status != 304) { xhr.getAllResponseHeaders(); var info = {}; for (var h in headers) { var value = xhr.getResponseHeader(h); if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, ''); if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1"); info[h] = value; } callback(url, info); } } xhr.send(); } }; if (document.location.protocol != "file:") { if (!window.liveJsLoaded) Live.heartbeat(); window.liveJsLoaded = true; } })(); """ ================================================ FILE: seleniumbase/js_code/recorder_js.py ================================================ ############################################################################### # recorder_js - Save browser actions to sessionStorage with good CSS selectors. ############################################################################### recorder_js = r""" var cssPathById = function(el) { if (!(el instanceof Element)) return; var path = []; while (el != null && el.nodeType === Node.ELEMENT_NODE) { var selector = el.nodeName.toLowerCase(); if (el.id) { elid = el.id; if (/\s/.test(elid) || elid.includes(',') || elid.includes('.') || elid.includes('(') || elid.includes(':') || hasDigit(elid[0])) return cssPathByAttribute(el, 'id'); selector += '#' + elid; path.unshift(selector); break; } else { var sib = el, nth = 1; while (sib = sib.previousElementSibling) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ':nth-of-type('+nth+')'; } path.unshift(selector); el = el.parentNode; } return path.join(' > '); }; var cssPathByAttribute = function(el, attr) { if (!(el instanceof Element)) return; var path = []; while (el !== null && el.nodeType === Node.ELEMENT_NODE) { var selector = el.nodeName.toLowerCase(); if (el.hasAttribute(attr) && el.getAttribute(attr).length > 0 && !el.getAttribute(attr).includes('\n')) { the_attr = el.getAttribute(attr); the_attr = the_attr.replaceAll('"', '\\"'); the_attr = the_attr.replaceAll("'", "\\'"); selector += '[' + attr + '="' + the_attr + '"]'; path.unshift(selector); break; } else { var sib = el, nth = 1; while (sib = sib.previousElementSibling) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ':nth-of-type('+nth+')'; } path.unshift(selector); el = el.parentNode; } return path.join(' > '); }; var cssPathByClass = function(el) { if (!(el instanceof Element)) return; var path = []; while (el !== null && el.nodeType === Node.ELEMENT_NODE) { var selector = el.nodeName.toLowerCase(); if (el.hasAttribute('class') && el.getAttribute('class').length > 0 && !el.getAttribute('class').includes(' ') && (el.getAttribute('class').includes('-')) && document.querySelectorAll( selector + '.' + el.getAttribute('class')).length == 1) { selector += '.' + el.getAttribute('class'); path.unshift(selector); break; } else { var sib = el, nth = 1; while (sib = sib.previousElementSibling) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ':nth-of-type('+nth+')'; } path.unshift(selector); el = el.parentNode; } return path.join(' > '); }; var ssOccurrences = function(string, subString, allowOverlapping) { if (subString.length <= 0) return (string.length + 1); var n = 0; var pos = 0; var step = allowOverlapping ? 1 : subString.length; while (true) { pos = string.indexOf(subString, pos); if (pos >= 0) { ++n; pos += step; } else break; } return n; }; function hasDigit(str) { return /\d/.test(str); }; function isGen(str) { return /[_-]\d/.test(str) || /\d[a-z]/.test(str); }; function tagName(el) { return el.tagName.toLowerCase(); }; function turnIntoParentAsNeeded(el) { if (tagName(el) == 'span' || tagName(el) == 'i') { if (tagName(el.parentElement) == 'button') { el = el.parentElement; } else if (tagName(el.parentElement.parentElement) == 'button') { el = el.parentElement.parentElement; } } return el; } var getBestSelector = function(el) { if (!(el instanceof Element)) return; el = turnIntoParentAsNeeded(el); sel_by_id = cssPathById(el); if (!sel_by_id.includes(' > ') && !isGen(sel_by_id)) return sel_by_id; child_count_by_id = ssOccurrences(sel_by_id, ' > '); selector_by_class = cssPathByClass(el); tag_name = tagName(el); non_id_attributes = []; non_id_attributes.push('name'); non_id_attributes.push('data-qa'); non_id_attributes.push('data-tid'); non_id_attributes.push('data-el'); non_id_attributes.push('data-se'); non_id_attributes.push('data-name'); non_id_attributes.push('data-auto'); non_id_attributes.push('data-text'); non_id_attributes.push('data-test'); non_id_attributes.push('data-testid'); non_id_attributes.push('data-test-id'); non_id_attributes.push('data-test-selector'); non_id_attributes.push('data-nav'); non_id_attributes.push('data-sb'); non_id_attributes.push('data-cy'); non_id_attributes.push('data-action'); non_id_attributes.push('data-target'); non_id_attributes.push('data-tooltip'); non_id_attributes.push('alt'); non_id_attributes.push('title'); non_id_attributes.push('heading'); non_id_attributes.push('translate'); non_id_attributes.push('aria-label'); non_id_attributes.push('aria-describedby'); non_id_attributes.push('rel'); non_id_attributes.push('ng-model'); non_id_attributes.push('ng-href'); non_id_attributes.push('href'); non_id_attributes.push('label'); non_id_attributes.push('data-content'); non_id_attributes.push('data-tip'); non_id_attributes.push('data-for'); non_id_attributes.push('class'); non_id_attributes.push('for'); non_id_attributes.push('placeholder'); non_id_attributes.push('value'); non_id_attributes.push('ng-click'); non_id_attributes.push('ng-if'); non_id_attributes.push('src'); selector_by_attr = []; all_by_attr = []; num_by_attr = []; child_count_by_attr = []; for (var i = 0; i < non_id_attributes.length; i++) { n_i_attr = non_id_attributes[i]; selector_by_attr[i] = null; if (n_i_attr == 'class') selector_by_attr[i] = selector_by_class; else selector_by_attr[i] = cssPathByAttribute(el, n_i_attr); all_by_attr[i] = document.querySelectorAll(selector_by_attr[i]); num_by_attr[i] = all_by_attr[i].length; if (!selector_by_attr[i].includes(' > ') && ((num_by_attr[i] == 1) || (el == all_by_attr[i][0]))) { if (n_i_attr.startsWith('aria') || n_i_attr == 'for') if (hasDigit(selector_by_attr[i])) continue; return selector_by_attr[i]; } child_count_by_attr[i] = ssOccurrences(selector_by_attr[i], ' > '); } basic_tags = []; basic_tags.push('h1'); basic_tags.push('h2'); basic_tags.push('h3'); basic_tags.push('canvas'); basic_tags.push('center'); basic_tags.push('input'); basic_tags.push('textarea'); for (var i = 0; i < basic_tags.length; i++) { d_qsa = document.querySelectorAll(basic_tags[i]); if (tag_name == basic_tags[i] && d_qsa.length == 1 && el == d_qsa[0]) return basic_tags[i]; } contains_tags = []; contains_tags.push('a'); contains_tags.push('b'); contains_tags.push('h1'); contains_tags.push('h2'); contains_tags.push('h3'); contains_tags.push('h4'); contains_tags.push('h5'); contains_tags.push('code'); contains_tags.push('mark'); contains_tags.push('button'); contains_tags.push('label'); contains_tags.push('legend'); contains_tags.push('li'); contains_tags.push('td'); contains_tags.push('th'); contains_tags.push('i'); contains_tags.push('small'); contains_tags.push('strong'); contains_tags.push('summary'); contains_tags.push('span'); all_by_tag = []; text_content = ''; if (el.textContent) text_content = el.textContent.trim(); for (var i = 0; i < contains_tags.length; i++) { if (tag_name == contains_tags[i] && text_content.length >= 2 && text_content.length <= 64) { t_count = 0; all_by_tag[i] = document.querySelectorAll(contains_tags[i]); for (var j = 0; j < all_by_tag[i].length; j++) { if (all_by_tag[i][j].textContent.includes(text_content)) t_count += 1; } if (t_count === 1 && !text_content.includes('\n')) { text_content = text_content.replaceAll("'", "\\'"); text_content = text_content.replaceAll('"', '\\"'); return tag_name += ':contains("'+text_content+'")'; } } } best_selector = sel_by_id; lowest_child_count = child_count_by_id; child_count_by_class = ssOccurrences(selector_by_class, ' > '); if (child_count_by_class < lowest_child_count) { best_selector = selector_by_class; lowest_child_count = child_count_by_class; } for (var i = 0; i < non_id_attributes.length; i++) { if (child_count_by_attr[i] < lowest_child_count && ((num_by_attr[i] == 1) || (el == all_by_attr[i][0]))) { best_selector = selector_by_attr[i]; lowest_child_count = child_count_by_attr[i]; } } best_selector = best_selector.replaceAll('html > body', 'body'); selector = best_selector.replaceAll(' > ', ' '); selector = selector.replaceAll(' div ', ' '); if (document.querySelector(selector) == el) best_selector = selector; return best_selector; }; function useHref(tag_name, el) { return (tag_name === 'a' && el.hasAttribute('href') && el.getAttribute('href').length > 0 && el.origin != 'null'); }; function saveRecordedActions() { json_rec_act = JSON.stringify(document.recorded_actions); sessionStorage.setItem('recorded_actions', json_rec_act); }; function new_tab_on_new_origin() { var AllAnchorTags = document.getElementsByTagName('a'); for (var i = 0; i < AllAnchorTags.length; i++) { if (!AllAnchorTags[i].sbset) { AllAnchorTags[i].sbset = true; AllAnchorTags[i].addEventListener('click', function (event) { rec_mode = sessionStorage.getItem('recorder_mode'); if (rec_mode !== '2' && rec_mode !== '3') { if (this.origin && this.origin != 'null' && this.origin != document.location.origin && this.hasAttribute('href')) { event.preventDefault(); window.open(this.href, '_blank').focus(); } } else { event.preventDefault(); event.stopPropagation(); } }, false); } } }; new_tab_on_new_origin(); var AllInputTags = document.getElementsByTagName('input'); var AllButtonTags = document.getElementsByTagName('button'); var All_IB_Tags = []; All_IB_Tags.push(...AllInputTags, ...AllButtonTags); for (var i = 0; i < All_IB_Tags.length; i++) { All_IB_Tags[i].addEventListener('click', function (event) { rec_mode = sessionStorage.getItem('recorder_mode'); if (rec_mode === '2' || rec_mode === '3') { event.preventDefault(); event.stopPropagation(); } }, false); } var SearchInputs = document.querySelectorAll('input[type="search"]'); for (var i = 0; i < SearchInputs.length; i++) { SearchInputs[i].addEventListener('change', function (event) { new_tab_on_new_origin(); }, false); } var AwayForms = document.querySelectorAll('form[action^="//"]'); for (var i = 0; i < AwayForms.length; i++) { AwayForms[i].target = '_blank'; } var reset_recorder_state = function() { document.recorded_actions = []; sessionStorage.setItem('pause_recorder', 'no'); sessionStorage.setItem('recorder_mode', '1'); sessionStorage.setItem('recorder_title', document.title); const d_now = Date.now(); document.recorder_last_mouseup = d_now; w_orig = window.location.origin; w_href = window.location.href; if (sessionStorage.getItem('recorder_activated') === 'yes') { ss_ra = JSON.parse(sessionStorage.getItem('recorded_actions')); document.recorded_actions = ss_ra; document.recorded_actions.push(['_url_', w_orig, w_href, d_now]); } else { sessionStorage.setItem('recorder_activated', 'yes'); document.recorded_actions.push(['begin', w_orig, w_href, d_now]); } saveRecordedActions(); return; }; reset_recorder_state(); var reset_if_recorder_undefined = function() { if (typeof document.recorded_actions === 'undefined') reset_recorder_state(); }; var set_border = function(color) { document.querySelector('body').style.border = '5px solid ' + color; document.querySelector('body').style.borderRadius = '10px'; }; document.body.addEventListener('mouseover', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const el = event.target; const selector = getBestSelector(el); if (!selector.startsWith('body') && !selector.includes(' div')) { document.title = selector; } }); document.body.addEventListener('mouseout', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; document.title = sessionStorage.getItem('recorder_title'); }); window.addEventListener('blur', () => { setTimeout(() => { reset_if_recorder_undefined(); rec_mode = sessionStorage.getItem('recorder_mode'); if (rec_mode === '2' || rec_mode === '3') return; const el = document.activeElement; const d_now = Date.now(); const d_now2 = d_now + 1; const doc_t = document.title; skip_open = false; if (tagName(el) === 'iframe' && doc_t.startsWith('iframe') && Date.now() - document.recorder_last_mouseup > 32) { const selector = getBestSelector(el); const el_cw = el.contentWindow; origin = window.location.origin; if (el.hasAttribute('src') && el.getAttribute('src').length > 0) { if (el.src.startsWith('data:')) return; skip_open = true; window.open(el.src, "_blank"); } else document.body.innerHTML = el_cw.document.body.innerHTML; window.focus(); document.recorded_actions.push(['sw_fr', selector, origin, d_now]); if (skip_open) document.recorded_actions.push(['sk_fo', '', origin, d_now2]); saveRecordedActions(); } }); }, { once: false }); document.body.addEventListener('click', function (event) { // do nothing }); document.body.addEventListener('dblclick', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; document.recorded_actions.push(['dbclk', '', '', Date.now()+1]); saveRecordedActions(); }); document.body.addEventListener('submit', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); ra_len = document.recorded_actions.length; if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'input' && !document.recorded_actions[ra_len-1][2].endsWith('\n')) { selector = document.recorded_actions[ra_len-1][1]; text = document.recorded_actions[ra_len-1][2] + '\n'; document.recorded_actions.pop(); document.recorded_actions.push(['input', selector, text, d_now]); saveRecordedActions(); } }); document.body.addEventListener('formdata', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); ra_len = document.recorded_actions.length; if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'input' && !document.recorded_actions[ra_len-1][2].endsWith('\n')) { selector = document.recorded_actions[ra_len-1][1]; text = document.querySelector(selector).value + '\n'; document.recorded_actions.pop(); document.recorded_actions.push(['input', selector, text, d_now]); saveRecordedActions(); } }); document.body.addEventListener('dragstart', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); const el = event.target; const selector = getBestSelector(el); ra_len = document.recorded_actions.length; rec_mode = sessionStorage.getItem('recorder_mode'); if (rec_mode === '2' || rec_mode === '3') return; if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'mo_dn' && document.recorded_actions[ra_len-1][1] === selector) { document.recorded_actions.pop(); } if (el.draggable === true) { document.recorded_actions.push(['drags', selector, '', d_now]); } saveRecordedActions(); }); document.body.addEventListener('dragend', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; ra_len = document.recorded_actions.length; if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'drags') { document.recorded_actions.pop(); saveRecordedActions(); } }); document.body.addEventListener('drop', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); const el = event.target; const selector = getBestSelector(el); ra_len = document.recorded_actions.length; if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'drags') { drg_s = document.recorded_actions[ra_len-1][1]; document.recorded_actions.pop(); document.recorded_actions.push(['ddrop', drg_s, selector, d_now]); saveRecordedActions(); } }); document.body.addEventListener('change', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); const el = event.target; const selector = getBestSelector(el); ra_len = document.recorded_actions.length; tag_name = tagName(el); e_type = el.type; if (tag_name === 'select') { el_computed = document.querySelector(selector); optxt = el_computed.options[el_computed.selectedIndex].text; document.recorded_actions.push(['s_opt', selector, optxt, d_now]); } else if (tag_name === 'input' && e_type === 'range') { if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector) { document.recorded_actions.pop(); ra_len = document.recorded_actions.length; } if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector) { document.recorded_actions.pop(); ra_len = document.recorded_actions.length; } value = el.value; document.recorded_actions.push(['set_v', selector, value, d_now]); } else if (tag_name === 'input' && e_type === 'file') { if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector) { document.recorded_actions.pop(); ra_len = document.recorded_actions.length; } value = el.value; document.recorded_actions.push(['cho_f', selector, value, d_now]); } else if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector && tag_name === 'input' && e_type === 'checkbox') { document.recorded_actions.pop(); ra_len = document.recorded_actions.length; if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector) document.recorded_actions.pop(); } if (tag_name === 'input' && e_type === 'checkbox' && el.checked) document.recorded_actions.push(['c_box', selector, 'yes', d_now]); else if (tag_name === 'input' && e_type === 'checkbox' && !el.checked) document.recorded_actions.push(['c_box', selector, 'no', d_now]); saveRecordedActions(); }); document.body.addEventListener('mousedown', function (event) { reset_if_recorder_undefined(); new_tab_on_new_origin(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); el = event.target; const selector = getBestSelector(el); ra_len = document.recorded_actions.length; rec_mode = sessionStorage.getItem('recorder_mode'); tag_name = tagName(el); if (rec_mode === '2' || rec_mode === '3') { el = turnIntoParentAsNeeded(el); text = el.innerText; t_con = el.textContent; origin = window.location.origin; sel_has_contains = selector.includes(':contains('); if (!text) { text = ''; } text = text.trim(); if (el.tagName.toLowerCase() == "input") text = el.value.trim(); if (!t_con) { t_con = ''; } if (rec_mode === '2' || ( rec_mode === '3' && sel_has_contains && text === t_con.trim())) { document.recorded_actions.push(['as_el', selector, origin, d_now]); saveRecordedActions(); return; } else if (rec_mode === '3') { action = 'as_et'; var match = /\r|\n/.exec(text); if (match) { lines = text.split(/\r\n|\r|\n/g); text = ''; for (var i = 0; i < lines.length; i++) { if (lines[i].length > 0) { action = 'as_te'; text = lines[i]; break; } } } tex_sel = [text, selector]; document.recorded_actions.push([action, tex_sel, origin, d_now]); saveRecordedActions(); return; } } if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'mo_dn') document.recorded_actions.pop(); if (tag_name === 'select') { // do nothing ('change' action) } else document.recorded_actions.push(['mo_dn', selector, '', d_now]); saveRecordedActions(); }); document.body.addEventListener('mouseup', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); document.recorder_last_mouseup = d_now; const el = event.target; selector = getBestSelector(el); ra_len = document.recorded_actions.length; tag_name = tagName(el); parent_el = el.parentElement; parent_tag_name = tagName(parent_el); grand_el = ""; grand_tag_name = ""; origin = ""; rec_mode = sessionStorage.getItem('recorder_mode'); if (rec_mode === '2' || rec_mode === '3') return; if (parent_el.parentElement != null) { grand_el = parent_el.parentElement; grand_tag_name = tagName(grand_el); } if (ra_len > 0 && document.recorded_actions[ra_len-1][1] === selector && (document.recorded_actions[ra_len-1][0] === 'mo_dn' || tag_name === 'a' || parent_tag_name === 'a') && tag_name !== 'select') { href = ''; if (useHref(tag_name, el)) { href = el.href; origin = el.origin; } else if (useHref(parent_tag_name, parent_el)) { href = parent_el.href; origin = parent_el.origin; } else if (useHref(grand_tag_name, grand_el)) { href = grand_el.href; origin = grand_el.origin; } document.recorded_actions.pop(); child_count = ssOccurrences(selector, ' > '); if ((tag_name === "a" && !el.hasAttribute('onclick') && child_count > 0 && href.length > 0) || (parent_tag_name === "a" && href.length > 0 && child_count > 1 && !parent_el.hasAttribute('onclick')) || (grand_tag_name === "a" && href.length > 0 && child_count > 2 && !grand_el.hasAttribute('onclick'))) { w_orig = window.location.origin; if (origin === w_orig) document.recorded_actions.push(['_url_', origin, href, d_now]); else document.recorded_actions.push(['begin', origin, href, d_now]); } else document.recorded_actions.push(['click', selector, href, d_now]); // hover+click if (el.parentElement.classList.contains('dropdown-content') && el.parentElement.parentElement.classList.contains('dropdown')) { ch_s = selector; pa_el = el.parentElement.parentElement; pa_s = getBestSelector(pa_el); if (pa_el.childElementCount >= 2 && !pa_el.firstElementChild.classList.contains('dropdown-content')) { pa_el = pa_el.firstElementChild; pa_s = getBestSelector(pa_el); } document.recorded_actions.pop(); document.recorded_actions.push(['h_clk', pa_s, ch_s, d_now]); } else if (tag_name === 'canvas') { rect = el.getBoundingClientRect(); p_x = event.clientX - rect.left; p_y = event.clientY - rect.top; c_offset = [selector, p_x, p_y]; document.recorded_actions.pop(); document.recorded_actions.push(['canva', c_offset, href, d_now]); } } else if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'mo_dn' && document.recorded_actions[ra_len-1][1] === selector && tag_name === 'select') { document.recorded_actions.pop(); } else if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'mo_dn') { // accidental drag&drop document.recorded_actions.pop(); } saveRecordedActions(); }); document.body.addEventListener('contextmenu', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; const el = event.target; const selector = getBestSelector(el); ra_len = document.recorded_actions.length; if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'mo_dn' && document.recorded_actions[ra_len-1][1] === selector) { document.recorded_actions.pop(); saveRecordedActions(); } }); document.body.addEventListener('keydown', function (event) { reset_if_recorder_undefined(); if (sessionStorage.getItem('pause_recorder') === 'yes') return; if (document.recorded_actions.length == 0) return; const el = event.target; const selector = getBestSelector(el); const d_now = Date.now(); const tag_name = tagName(el); const l_key = event.key.toLowerCase(); if (l_key === 'enter' && (tag_name === 'button' || tag_name === 'a')) { href = ''; if (useHref(tag_name, el)) href = el.href; document.recorded_actions.push(['click', selector, href, d_now]); saveRecordedActions(); } }); document.body.addEventListener('keyup', function (event) { reset_if_recorder_undefined(); // pause+resume controls pause_rec = sessionStorage.getItem('pause_recorder'); rec_mode = sessionStorage.getItem('recorder_mode'); l_key = event.key.toLowerCase(); no_border = 'none'; if (l_key === 'escape' && pause_rec === 'no' && rec_mode === '1') { sessionStorage.setItem('pause_recorder', 'yes'); pause_rec = 'yes'; console.log('SeleniumBase Recorder paused'); document.querySelector('body').style.border = no_border; document.title = sessionStorage.getItem('recorder_title'); } else if ((event.key === '`' || event.key === '~') && pause_rec === 'yes') { sessionStorage.setItem('pause_recorder', 'no'); pause_rec = 'no'; console.log('SeleniumBase Recorder resumed'); set_border('#F43344'); } else if (event.key === '^' && pause_rec === 'no') { sessionStorage.setItem('recorder_mode', '2'); set_border('#EF5BE9'); } else if (event.key === '&' && pause_rec === 'no') { sessionStorage.setItem('recorder_mode', '3'); set_border('#30C6C6'); } else if (pause_rec === 'no' && l_key !== 'shift' && l_key !== 'backspace') { sessionStorage.setItem('recorder_mode', '1'); set_border('#F43344'); } // after switching modes if (sessionStorage.getItem('pause_recorder') === 'yes') return; const d_now = Date.now(); const el = event.target; const selector = getBestSelector(el); skip_input = false; if ((tagName(el) === 'input' && el.type !== 'checkbox' && el.type !== 'range') || tagName(el) === 'textarea') { ra_len = document.recorded_actions.length; if (ra_len > 0 && l_key === 'enter' && document.recorded_actions[ra_len-1][0] === 'input' && document.recorded_actions[ra_len-1][1] === selector && !document.recorded_actions[ra_len-1][2].endsWith('\n')) { s_text = document.recorded_actions[ra_len-1][2] + '\n'; document.recorded_actions.pop(); document.recorded_actions.push(['input', selector, s_text, d_now]); document.recorded_actions.push(['submi', selector, s_text, d_now]); skip_input = true; } else if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'click' && document.recorded_actions[ra_len-1][1] === selector) { document.recorded_actions.pop(); } else if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'input' && document.recorded_actions[ra_len-1][1] === selector && !document.recorded_actions[ra_len-1][2].endsWith('\n') && l_key !== 'tab') { document.recorded_actions.pop(); } else if (ra_len > 0 && document.recorded_actions[ra_len-1][0] === 'input' && document.recorded_actions[ra_len-1][1] === selector && document.recorded_actions[ra_len-1][2].endsWith('\n')) { skip_input = true; } if (!skip_input && !el.hasAttribute('readonly') && l_key !== 'tab') { document.recorded_actions.push( ['input', selector, el.value, d_now]); } } saveRecordedActions(); }); set_border('#F43344'); """ ================================================ FILE: seleniumbase/masterqa/ReadMe.md ================================================ ![](https://seleniumbase.github.io/cdn/img/masterqa_logo.png "MasterQA")

      MasterQA combines automation with manual verification steps.

      ![](https://seleniumbase.github.io/cdn/gif/masterqa6.gif "MasterQA") Here's code from [basic_masterqa_test_0.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/basic_masterqa_test_0.py): ```python from seleniumbase import MasterQA class MasterQATests(MasterQA): def test_masterqa(self): self.open("https://xkcd.com/1700/") self.verify("Do you see a webcomic?") self.open("https://seleniumbase.io/demo_page") self.highlight('table') self.verify("Do you see elements in a table?") self.open("https://seleniumbase.io/devices/") self.highlight("div.mockup-wrapper") self.verify("Do you see 4 computer devices?") ``` After each automation checkpoint, a pop-up window will ask the user questions for each verification command. When the test run completes, as seen from [this longer example](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/masterqa_test_1.py), you'll reach the results page that appears after answering all the verification questions. (Failed verifications generate links to screenshots and log files.) ![](https://seleniumbase.github.io/cdn/img/mqa_hybrid.png "MasterQA") You may have noticed the ``Incomplete Test Runs`` row on the results page. If the value for that is not zero, it means that one of the automated steps failed. This could happen if you tell your script to perform an action on an element that doesn't exist. Now that we're mixing automation with manual QA, it's good to tell apart the failures from each. The results_table CSV file contains a spreadsheet with the details of each failure (if any) for both manual and automated steps. **How to run the example tests from scratch:** ```zsh git clone https://github.com/seleniumbase/SeleniumBase.git cd SeleniumBase pip install . cd examples/master_qa pytest basic_masterqa_test_0.py pytest masterqa_test_1.py ``` At the end of your test run, you'll receive a report with results, screenshots, and log files. Close the Results Page window when you're done. **Check out [masterqa_test_1.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/master_qa/masterqa_test_1.py) to learn how to write your own MasterQA tests:** You'll notice that tests are written the same way as regular [SeleniumBase](https://seleniumbase.com) tests, with the key difference being a different import: ``from seleniumbase import MasterQA`` rather than ``from seleniumbase import BaseCase``. Now your Python test class will import ``MasterQA`` instead of ``BaseCase``. To add a manual verification step, use ``self.verify()`` in the code after each part of your test that needs a manual verification step. If you want to include a custom question, add text inside that call (in quotes). Example: ```python self.verify() self.verify("Can you find the moon?") ``` -------- MasterQA is powered by [SeleniumBase](https://seleniumbase.com), the most advanced open-source automation framework on the [Planet](https://en.wikipedia.org/wiki/Earth). ================================================ FILE: seleniumbase/masterqa/__init__.py ================================================ ================================================ FILE: seleniumbase/masterqa/master_qa.py ================================================ """Manually verify pages quickly while assisted by automation.""" import os import shutil import sys import time from selenium.common.exceptions import NoAlertPresentException from selenium.common.exceptions import WebDriverException from seleniumbase import BaseCase from seleniumbase.core.style_sheet import get_report_style from seleniumbase.config import settings from seleniumbase.fixtures import js_utils python3_11_or_newer = False if sys.version_info >= (3, 11): python3_11_or_newer = True class MasterQA(BaseCase): def setUp(self): self.check_count = 0 self.auto_close_results_page = False super().setUp(masterqa_mode=True) self.LATEST_REPORT_DIR = settings.LATEST_REPORT_DIR self.ARCHIVE_DIR = settings.REPORT_ARCHIVE_DIR self.RESULTS_PAGE = settings.HTML_REPORT self.BAD_PAGE_LOG = settings.RESULTS_TABLE self.DEFAULT_VALIDATION_TITLE = "Manual Check" self.DEFAULT_VALIDATION_MESSAGE = ( settings.MASTERQA_DEFAULT_VALIDATION_MESSAGE ) self.WAIT_TIME_BEFORE_VERIFY = ( settings.MASTERQA_WAIT_TIME_BEFORE_VERIFY ) self.START_IN_FULL_SCREEN_MODE = ( settings.MASTERQA_START_IN_FULL_SCREEN_MODE ) self.MAX_IDLE_TIME_BEFORE_QUIT = ( settings.MASTERQA_MAX_IDLE_TIME_BEFORE_QUIT ) self.__manual_check_setup() if self.headless: self.auto_close_results_page = True if self.START_IN_FULL_SCREEN_MODE: self.maximize_window() def verify(self, *args): warn_msg = "\nWARNING: MasterQA skips manual checks in headless mode!" self.check_count += 1 if self.headless: if self.check_count == 1: print(warn_msg) return # This is where the magic happens self.__manual_page_check(*args) def auto_close_results(self): """If this method is called, the results page will automatically close at the end of the test run, rather than waiting on the user to close the results page manually. """ self.auto_close_results_page = True def tearDown(self): if self.headless and self.check_count > 0: print( "WARNING: %s manual checks were skipped! (MasterQA)" % self.check_count ) if self.__has_exception(): self.__add_failure(sys.exc_info()[1]) self.__process_manual_check_results(self.auto_close_results_page) super().tearDown() #################### def __get_timestamp(self): return str(int(time.time() * 1000)) def __manual_check_setup(self): self.manual_check_count = 0 self.manual_check_successes = 0 self.incomplete_runs = 0 self.page_results_list = [] self.__clear_out_old_logs(archive_past_runs=False) def __clear_out_old_logs( self, archive_past_runs=True, get_log_folder=False ): abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, self.LATEST_REPORT_DIR) if not os.path.exists(file_path): os.makedirs(file_path) if archive_past_runs: archive_timestamp = int(time.time()) archive_dir_root = os.path.join(file_path, "..", self.ARCHIVE_DIR) if not os.path.exists(archive_dir_root): os.makedirs(archive_dir_root) archive_dir = os.path.join( archive_dir_root, "log_%s" % archive_timestamp ) shutil.move(file_path, archive_dir) os.makedirs(file_path) if get_log_folder: return archive_dir else: latest_report_local = os.path.join(".", self.LATEST_REPORT_DIR) # Just delete bad pages to make room for the latest run. filelist = [ f for f in os.listdir(latest_report_local) if (f.startswith("failed_")) or (f == self.RESULTS_PAGE) or (f.startswith("automation_failure")) or (f == self.BAD_PAGE_LOG) ] for f in filelist: os.remove(os.path.join(file_path, f)) def __jq_confirm_dialog(self, question): count = self.manual_check_count + 1 title = self.DEFAULT_VALIDATION_TITLE title_content = ( '
      %s #%s:' '

      %s' "" % (title, count, question) ) title_content = js_utils.escape_quotes_if_needed(title_content) jqcd = ( """jconfirm({ boxWidth: '32.5%%', useBootstrap: false, containerFluid: false, animationBounce: 1, type: 'default', theme: 'bootstrap', typeAnimated: true, animation: 'scale', draggable: true, dragWindowGap: 1, container: 'body', title: '%s', content: '', buttons: { pass_button: { btnClass: 'btn-green', text: 'YES / PASS', keys: ['y', 'p', '1'], action: function(){ $jqc_status = "Success!"; jconfirm.lastButtonText = "Success!"; } }, fail_button: { btnClass: 'btn-red', text: 'NO / FAIL', keys: ['n', 'f', '2'], action: function(){ $jqc_status = "Failure!"; jconfirm.lastButtonText = "Failure!"; } } } });""" % title_content ) self.execute_script(jqcd) def __manual_page_check(self, *args): if not args: instructions = self.DEFAULT_VALIDATION_MESSAGE # self.verify() else: instructions = str(args[0]) if len(args) > 1: pass question = "Approve?" # self.verify("") if instructions and "?" not in instructions: question = instructions + " <> Approve?" elif instructions and "?" in instructions: question = instructions wait_time_before_verify = self.WAIT_TIME_BEFORE_VERIFY if self.verify_delay: wait_time_before_verify = float(self.verify_delay) # Allow a moment to see the full page before the dialog box pops up time.sleep(wait_time_before_verify) use_jqc = False self.wait_for_ready_state_complete() if js_utils.is_jquery_confirm_activated(self.driver): use_jqc = True else: js_utils.activate_jquery_confirm(self.driver) get_jqc = None try: get_jqc = self.execute_script("return jconfirm") if get_jqc is None: raise Exception("jconfirm did not load") use_jqc = True except Exception: use_jqc = False if use_jqc: # Use the jquery_confirm library for manual page checks self.__jq_confirm_dialog(question) time.sleep(0.02) waiting_for_response = True while waiting_for_response: time.sleep(0.05) jqc_open = self.execute_script( "return jconfirm.instances.length" ) if str(jqc_open) == "0": break time.sleep(0.1) status = None try: status = self.execute_script("return $jqc_status") except Exception: status = self.execute_script("return jconfirm.lastButtonText") else: # Fallback to plain js confirm dialogs if can't load jquery_confirm if self.browser == "ie": text = self.execute_script( """if(confirm("%s")){return "Success!"} else{return "Failure!"}""" % question ) elif self.browser == "chrome": self.execute_script( """if(confirm("%s")) {window.master_qa_result="Success!"} else{window.master_qa_result="Failure!"}""" % question ) time.sleep(0.05) self.__wait_for_special_alert_absent() text = self.execute_script("return window.master_qa_result") else: try: self.execute_script( """if(confirm("%s")) {window.master_qa_result="Success!"} else{window.master_qa_result="Failure!"}""" % question ) except WebDriverException: # Fix for https://github.com/mozilla/geckodriver/issues/431 pass time.sleep(0.05) self.__wait_for_special_alert_absent() text = self.execute_script("return window.master_qa_result") status = text self.manual_check_count += 1 try: current_url = self.driver.current_url except Exception: current_url = self.execute_script("return document.URL") if "Success!" in str(status): self.manual_check_successes += 1 self.page_results_list.append( '"%s","%s","%s","%s","%s","%s","%s","%s"' % ( self.manual_check_count, "Success", "-", current_url, self.browser, self.__get_timestamp()[:-3], instructions, "*", ) ) return 1 else: bad_page_name = "failed_check_%s.png" % self.manual_check_count self.save_screenshot(bad_page_name, folder=self.LATEST_REPORT_DIR) self.page_results_list.append( '"%s","%s","%s","%s","%s","%s","%s","%s"' % ( self.manual_check_count, "FAILED!", bad_page_name, current_url, self.browser, self.__get_timestamp()[:-3], instructions, "*", ) ) return 0 def __wait_for_special_alert_absent(self): timeout = self.MAX_IDLE_TIME_BEFORE_QUIT for x in range(int(timeout * 20)): try: alert = self.driver.switch_to.alert dummy_variable = alert.text # Raises exception if no alert if "?" not in dummy_variable: return time.sleep(0.05) except NoAlertPresentException: return self.driver.quit() raise Exception( "%s seconds passed without human action! Stopping..." % timeout ) def __has_exception(self): has_exception = False if hasattr(sys, "last_traceback") and sys.last_traceback is not None: has_exception = True elif hasattr(self, "_outcome"): if hasattr(self._outcome, "errors"): if python3_11_or_newer: if ( self._outcome.errors and self._outcome.errors[-1] and self._outcome.errors[-1][1] ): has_exception = True else: if self._outcome.errors: has_exception = True else: has_exception = sys.exc_info()[1] is not None return has_exception def __add_failure(self, exception=None): exc_info = None if exception: if hasattr(exception, "msg"): exc_info = exception.msg elif hasattr(exception, "message"): exc_info = exception.message else: exc_info = "(Unknown Exception)" self.incomplete_runs += 1 error_page = "automation_failure_%s.png" % self.incomplete_runs self.save_screenshot(error_page, folder=self.LATEST_REPORT_DIR) self.page_results_list.append( '"%s","%s","%s","%s","%s","%s","%s","%s"' % ( "ERR", "ERROR!", error_page, self.driver.current_url, self.browser, self.__get_timestamp()[:-3], "-", exc_info, ) ) try: # Return to the original window if another was opened self.driver.switch_to_window(self.driver.window_handles[1]) self.driver.close() self.driver.switch_to_window(self.driver.window_handles[0]) except Exception: pass def __add_bad_page_log_file(self): abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, self.LATEST_REPORT_DIR) log_file = os.path.join(file_path, self.BAD_PAGE_LOG) f = open(log_file, "w") h_p1 = """"Num","Result","Screenshot","URL","Browser","Epoch Time",""" h_p2 = """"Verification Instructions","Additional Info"\n""" page_header = h_p1 + h_p2 f.write(page_header) for line in self.page_results_list: f.write("%s\n" % line) f.close() def __add_results_page(self, html): abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, self.LATEST_REPORT_DIR) results_file_name = self.RESULTS_PAGE results_file = os.path.join(file_path, results_file_name) f = open(results_file, "w") f.write(html) f.close() return results_file def __process_manual_check_results(self, auto_close_results_page=False): perfection = True failures_count = self.manual_check_count - self.manual_check_successes if not self.headless: print("") print("\n*** MasterQA Manual Test Results: ***") if self.manual_check_successes == self.manual_check_count: pass else: print("WARNING: Not all tests passed manual inspection!") perfection = False if self.incomplete_runs > 0: print("WARNING: Not all tests finished running!") perfection = False if perfection: if self.manual_check_count > 0: print("SUCCESS: Everything checks out OKAY!") else: print("WARNING: No manual checks were performed!") else: pass self.__add_bad_page_log_file() # Includes successful results log_string = self.__clear_out_old_logs(get_log_folder=True) log_folder = log_string.split(os.sep)[-1] abs_path = os.path.abspath(".") file_path = os.path.join(abs_path, self.ARCHIVE_DIR) log_path = os.path.join(file_path, log_folder) web_log_path = "file://%s" % log_path tf_color = "#11BB11" if failures_count > 0: tf_color = "#EE3A3A" ir_color = "#11BB11" if self.incomplete_runs > 0: ir_color = "#EE3A3A" summary_table = """
      TESTING SUMMARY         
      CHECKS PASSED: %s
      CHECKS FAILED: %s
      TOTAL VERIFICATIONS: %s
      INCOMPLETE TEST RUNS: %s
      """ % ( self.manual_check_successes, tf_color, failures_count, self.manual_check_count, ir_color, self.incomplete_runs, ) summary_table = ( """

      %s

      """ % summary_table ) log_link_shown = os.path.join( "..", "%s%s" % ( self.ARCHIVE_DIR, web_log_path.split(self.ARCHIVE_DIR)[1] ) ) csv_link = os.path.join(web_log_path, self.BAD_PAGE_LOG) csv_link_shown = "%s" % self.BAD_PAGE_LOG log_table = """

      LOG FILES LINK:  %s
      RESULTS TABLE:  %s

      """ % ( web_log_path, log_link_shown, csv_link, csv_link_shown, ) failure_table = "

      " any_screenshots = False for line in self.page_results_list: line = line.split(",") if line[1] == '"FAILED!"' or line[1] == '"ERROR!"': if not any_screenshots: any_screenshots = True failure_table += """""" display_url = line[3] if len(display_url) > 60: display_url = display_url[0:58] + "..." line = ( '%s' % ("file://" + log_path + "/" + line[2], line[2]) + """     \n" % line failure_table += "
      SCREENSHOT FILE     LOCATION OF FAILURE
      """ + '%s' % (line[3], display_url) ) line = line.replace('"', "") failure_table += "
      %s
      " table_view = "%s%s%s" % (summary_table, log_table, failure_table) report_html = "%s%s" % ( get_report_style(), table_view, ) results_file = self.__add_results_page(report_html) archived_results_file = os.path.join(log_path, self.RESULTS_PAGE) shutil.copyfile(results_file, os.path.realpath(archived_results_file)) if self.manual_check_count > 0: print( "\n*** The manual test report is located at:\n" + results_file ) self.open("file://%s" % archived_results_file) if auto_close_results_page: # Long enough to notice the results before closing the page time.sleep(1.0) else: # The user can decide when to close the results page print("\n*** Close the html report window to continue ***") try: while len(self.driver.window_handles): time.sleep(0.1) except Exception: pass ================================================ FILE: seleniumbase/plugins/__init__.py ================================================ ================================================ FILE: seleniumbase/plugins/base_plugin.py ================================================ """Base Plugin for SeleniumBase tests that run with pynose / nosetests""" import ast import sys import time from contextlib import suppress from nose.plugins import Plugin from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import download_helper from seleniumbase.core import log_helper from seleniumbase.core import report_helper from seleniumbase.fixtures import constants python3_11_or_newer = False if sys.version_info >= (3, 11): python3_11_or_newer = True py311_patch2 = constants.PatchPy311.PATCH = True class Base(Plugin): """This plugin adds the following command-line options to pynose: --env=ENV (Set the test env. Access with "self.env" in tests.) --account=STR (Set account. Access with "self.account" in tests.) --data=STRING (Extra test data. Access with "self.data" in tests.) --var1=STRING (Extra test data. Access with "self.var1" in tests.) --var2=STRING (Extra test data. Access with "self.var2" in tests.) --var3=STRING (Extra test data. Access with "self.var3" in tests.) --variables=DICT (Extra test data. Access with "self.variables".) --settings-file=FILE (Override default SeleniumBase settings.) --ftrace | --final-trace (Enter Debug Mode after any test ends.) --archive-logs (Archive old log files instead of deleting them.) --archive-downloads (Archive old downloads instead of deleting.) --report (Create a fancy nosetests report after tests complete.) --show-report If self.report is turned on, then the report will display immediately after tests complete their run. Only use this when running tests locally, as this will pause the test run until the report window is closed. """ name = "testing_base" # Usage: --with-testing_base (Enabled by default) def options(self, parser, env): super().options(parser, env=env) parser.addoption = parser.add_option # Reuse name from pytest parser parser.addoption( "--env", action="store", dest="environment", choices=( constants.Environment.QA, constants.Environment.RC, constants.Environment.STAGING, constants.Environment.DEVELOP, constants.Environment.PRODUCTION, constants.Environment.PERFORMANCE, constants.Environment.REPLICA, constants.Environment.FEDRAMP, constants.Environment.OFFLINE, constants.Environment.ONLINE, constants.Environment.MASTER, constants.Environment.REMOTE, constants.Environment.LEGACY, constants.Environment.LOCAL, constants.Environment.ALPHA, constants.Environment.BETA, constants.Environment.DEMO, constants.Environment.GDPR, constants.Environment.MAIN, constants.Environment.TEST, constants.Environment.GOV, constants.Environment.NEW, constants.Environment.OLD, constants.Environment.UAT, ), default=constants.Environment.TEST, help="""This option sets a test env from a list of choices. Access using "self.env" or "self.environment".""", ) parser.addoption( "--account", dest="account", default=None, help="""This option sets a test account string. In tests, use "self.account" to get the value.""", ) parser.addoption( "--data", dest="data", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--var1", dest="var1", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--var2", dest="var2", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--var3", dest="var3", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--variables", dest="variables", default=None, help="""A var dict to pass to tests from the command line. Example usage: ---------------------------------------------- Option: --variables='{"special":123}' Access: self.variables["special"] ---------------------------------------------- Option: --variables='{"color":"red","num":42}' Access: self.variables["color"] Access: self.variables["num"] ----------------------------------------------""", ) parser.addoption( "--settings_file", "--settings-file", "--settings", action="store", dest="settings_file", default=None, help="""The file that stores key/value pairs for overriding values in the SeleniumBase settings.py file.""", ) parser.addoption( "--final-debug", "--final-trace", "--fdebug", "--ftrace", action="store_true", dest="final_debug", default=False, help="""Enter Debug Mode at the end of each test. To enter Debug Mode only on failures, use "--pdb". If using both "--final-debug" and "--pdb" together, then Debug Mode will activate twice on failures.""", ) parser.addoption( "--log_path", "--log-path", dest="log_path", default=constants.Logs.LATEST + "/", help="""(DEPRECATED) - This field is NOT EDITABLE anymore. Log files are saved to the "latest_logs/" folder.""", ) parser.addoption( "--archive_logs", "--archive-logs", action="store_true", dest="archive_logs", default=False, help="Archive old log files instead of deleting them.", ) parser.addoption( "--archive_downloads", "--archive-downloads", action="store_true", dest="archive_downloads", default=False, help="Archive old downloads instead of deleting them.", ) parser.addoption( "--report", action="store_true", dest="report", default=False, help="Create a fancy report at the end of the test suite.", ) parser.addoption( "--show_report", "--show-report", action="store_true", dest="show_report", default=False, help="If true when using report, will display it after tests run.", ) found_processes_arg = False for arg in sys.argv: if "--processes=" in arg or "--processes" in arg: found_processes_arg = True if found_processes_arg: print("* WARNING: Don't use multi-threading with nosetests! *") parser.addoption( "--processes", dest="processes", default=0, help="WARNING: Don't use multi-threading with nosetests!", ) def configure(self, options, conf): super().configure(options, conf) self.enabled = True # Used if test class inherits BaseCase self.options = options self.report_on = options.report self.show_report = options.show_report self.successes = [] self.failures = [] self.start_time = float(0) self.duration = float(0) self.page_results_list = [] self.test_count = 0 log_path = constants.Logs.LATEST + "/" archive_logs = options.archive_logs log_helper.log_folder_setup(log_path, archive_logs) download_helper.reset_downloads_folder() sb_config.is_nosetest = True if self.report_on: report_helper.clear_out_old_report_logs(archive_past_runs=False) def beforeTest(self, test): sb_config._context_of_runner = False # Context Manager Compatibility variables = self.options.variables if variables and isinstance(variables, str) and len(variables) > 0: bad_input = False if not variables.startswith("{") or not variables.endswith("}"): bad_input = True else: try: variables = ast.literal_eval(variables) if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True if bad_input: raise Exception( '\nExpecting a Python dictionary for "variables"!' "\nEg. --variables=\"{'KEY1':'VALUE', 'KEY2':123}\"" ) else: variables = {} test.test.test_id = test.id() test.test.is_nosetest = True test.test.environment = self.options.environment sb_config.environment = self.options.environment test.test.env = self.options.environment # Add a shortened version test.test.account = self.options.account sb_config.account = self.options.account test.test.data = self.options.data sb_config.data = self.options.data test.test.var1 = self.options.var1 sb_config.var1 = self.options.var1 test.test.var2 = self.options.var2 sb_config.var2 = self.options.var2 test.test.var3 = self.options.var3 sb_config.var3 = self.options.var3 test.test.variables = variables # Already verified is a dictionary sb_config.variables = variables test.test.settings_file = self.options.settings_file sb_config.settings_file = self.options.settings_file test.test._final_debug = self.options.final_debug test.test.log_path = self.options.log_path if self.options.archive_downloads: settings.ARCHIVE_EXISTING_DOWNLOADS = True test.test.args = self.options test.test.report_on = self.report_on self.test_count += 1 self.start_time = float(time.time()) def finalize(self, result): log_helper.archive_logs_if_set( self.options.log_path, self.options.archive_logs ) log_helper.clear_empty_logs() if self.report_on: report_helper.add_bad_page_log_file(self.page_results_list) report_log_path = report_helper.archive_new_report_logs() report_helper.build_report( report_log_path, self.page_results_list, self.successes, self.failures, self.options.browser, self.show_report, ) def addSuccess(self, test, capt): if self.report_on: self.duration = str( "%.2fs" % (float(time.time()) - float(self.start_time)) ) self.successes.append(test.id()) self.page_results_list.append( report_helper.process_successes( test, self.test_count, self.duration ) ) def add_fails_or_errors(self, test, err): if self.report_on: self.duration = str( "%.2fs" % (float(time.time()) - float(self.start_time)) ) if test.id() == "nose.failure.Failure.runTest": return self.failures.append(test.id()) self.page_results_list.append( report_helper.process_failures( test, self.test_count, self.duration ) ) if python3_11_or_newer and py311_patch2: # Handle a bug on Python 3.11 where exceptions aren't seen sb_config._browser_version = None with suppress(Exception): test._BaseCase__set_last_page_screenshot() test._BaseCase__set_last_page_url() test._BaseCase__set_last_page_source() sb_config._browser_version = test._get_browser_version() test._log_fail_data() sb_config._excinfo_tb = err log_path = None source = None if hasattr(sb_config, "_test_logpath"): log_path = sb_config._test_logpath if hasattr(sb_config, "_last_page_source"): source = sb_config._last_page_source if log_path and source: log_helper.log_page_source(log_path, None, source) last_page_screenshot_png = None if hasattr(sb_config, "_last_page_screenshot_png"): last_page_screenshot_png = sb_config._last_page_screenshot_png if log_path and last_page_screenshot_png: log_helper.log_screenshot( log_path, None, last_page_screenshot_png ) def addFailure(self, test, err, capt=None, tbinfo=None): self.add_fails_or_errors(test, err) def addError(self, test, err, capt=None): """Since Skip, Blocked, and Deprecated are all technically errors, but not error states, we want to make sure that they don't show up in the nose output as errors.""" from seleniumbase.fixtures import errors if ( err[0] == errors.BlockedTest or (err[0] == errors.SkipTest) or (err[0] == errors.DeprecatedTest) ): print( err[1] .__str__() .split( """-------------------- >> """ """begin captured logging""" """ << --------------------""", 1, )[0] ) else: pass self.add_fails_or_errors(test, err) def handleError(self, test, err, capt=None): """After each test error, record testcase run information. "Error" also encompasses any states other than Pass or Fail.""" from nose.exc import SkipTest from seleniumbase.fixtures import errors if not hasattr(test.test, "testcase_guid"): if err[0] == errors.BlockedTest: raise SkipTest(err[1]) elif err[0] == errors.DeprecatedTest: raise SkipTest(err[1]) elif err[0] == errors.SkipTest: raise SkipTest(err[1]) ================================================ FILE: seleniumbase/plugins/basic_test_info.py ================================================ """Test Info Plugin for SeleniumBase tests that run with pynose / nosetests""" import os import time import traceback from nose.plugins import Plugin from seleniumbase.config import settings class BasicTestInfo(Plugin): """This plugin captures basic info when a test fails or raises an error.""" name = "basic_test_info" # Usage: --with-basic_test_info logfile_name = settings.BASIC_INFO_NAME def options(self, parser, env): super().options(parser, env=env) def configure(self, options, conf): super().configure(options, conf) if not self.enabled: return self.options = options def addError(self, test, err, capt=None): test_logpath = self.options.log_path + "/" + test.id() if not os.path.exists(test_logpath): os.makedirs(test_logpath) file_name = "%s/%s" % (test_logpath, self.logfile_name) basic_info_file = open(file_name, mode="w+", encoding="utf-8") self.__log_test_error_data(basic_info_file, test, err, "Error") basic_info_file.close() def addFailure(self, test, err, capt=None, tbinfo=None): test_logpath = self.options.log_path + "/" + test.id() if not os.path.exists(test_logpath): os.makedirs(test_logpath) file_name = "%s/%s" % (test_logpath, self.logfile_name) basic_info_file = open(file_name, mode="w+", encoding="utf-8") self.__log_test_error_data(basic_info_file, test, err, "Error") basic_info_file.close() def __log_test_error_data(self, log_file, test, err, type): data_to_save = [] data_to_save.append("Last Page: %s" % test.driver.current_url) data_to_save.append(" Browser: %s" % self.options.browser) data_to_save.append("Timestamp: %s" % int(time.time())) data_to_save.append("Server: %s " % self.options.servername) data_to_save.append("%s: %s" % (type, err[0])) data_to_save.append( "Traceback: " + "".join(traceback.format_exception(*err)) ) log_file.writelines("\r\n".join(data_to_save)) ================================================ FILE: seleniumbase/plugins/db_reporting_plugin.py ================================================ """DB Reporting Plugin for SeleniumBase tests that use pynose / nosetests""" import time import uuid from nose.plugins import Plugin from seleniumbase.fixtures import constants class DBReporting(Plugin): """This plugin records test results in the Testcase Database.""" name = "db_reporting" # Usage: --with-db_reporting def __init__(self): Plugin.__init__(self) self.execution_guid = str(uuid.uuid4()) self.testcase_guid = None self.execution_start_time = 0 self.case_start_time = 0 self.testcase_manager = None self._result_set = False self._test = None def options(self, parser, env): super().options(parser, env=env) parser.add_option( "--database_env", "--database-env", action="store", dest="database_env", choices=( constants.Environment.QA, constants.Environment.RC, constants.Environment.STAGING, constants.Environment.DEVELOP, constants.Environment.PRODUCTION, constants.Environment.PERFORMANCE, constants.Environment.REPLICA, constants.Environment.FEDRAMP, constants.Environment.OFFLINE, constants.Environment.ONLINE, constants.Environment.MASTER, constants.Environment.REMOTE, constants.Environment.LEGACY, constants.Environment.LOCAL, constants.Environment.ALPHA, constants.Environment.BETA, constants.Environment.DEMO, constants.Environment.GDPR, constants.Environment.MAIN, constants.Environment.TEST, constants.Environment.GOV, constants.Environment.NEW, constants.Environment.OLD, constants.Environment.UAT, ), default=constants.Environment.TEST, help="The database environment to run the tests in.", ) def configure(self, options, conf): from seleniumbase.core.testcase_manager import TestcaseManager super().configure(options, conf) self.options = options self.testcase_manager = TestcaseManager(self.options.database_env) def begin(self): """At the start of the run, we want to record the test execution information in the database.""" import getpass from seleniumbase.core.testcase_manager import ExecutionQueryPayload exec_payload = ExecutionQueryPayload() exec_payload.execution_start_time = int(time.time() * 1000) self.execution_start_time = exec_payload.execution_start_time exec_payload.guid = self.execution_guid exec_payload.username = getpass.getuser() self.testcase_manager.insert_execution_data(exec_payload) def startTest(self, test): """At the start of the test, set testcase details.""" from seleniumbase.core.application_manager import ApplicationManager from seleniumbase.core.testcase_manager import TestcaseDataPayload data_payload = TestcaseDataPayload() self.testcase_guid = str(uuid.uuid4()) data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid if hasattr(test, "browser"): data_payload.browser = test.browser else: data_payload.browser = "N/A" data_payload.test_address = test.id() application = ApplicationManager.generate_application_string(test) data_payload.env = application.split(".")[0] data_payload.start_time = application.split(".")[1] data_payload.state = constants.State.UNTESTED self.testcase_manager.insert_testcase_data(data_payload) self.case_start_time = int(time.time() * 1000) # Make the testcase guid available to other plugins test.testcase_guid = self.testcase_guid self._test = test self._test._nose_skip_reason = None def finalize(self, result): """At the end of the test run, we want to update the DB row with the total execution time.""" runtime = int(time.time() * 1000) - self.execution_start_time self.testcase_manager.update_execution_data( self.execution_guid, runtime ) def afterTest(self, test): if not self._result_set: err = None try: err = self._test._nose_skip_reason if err: err = "Skipped: " + str(err) err = (err, err) except Exception: pass if not err: err = "Skipped: (no reason given)" err = (err, err) self.__insert_test_result(constants.State.SKIPPED, self._test, err) def addSuccess(self, test, capt): """After each test success, record testcase run information.""" self.__insert_test_result(constants.State.PASSED, test) self._result_set = True def addFailure(self, test, err, capt=None, tbinfo=None): """After each test failure, record testcase run information.""" self.__insert_test_result(constants.State.FAILED, test, err) self._result_set = True def addError(self, test, err, capt=None): """After each test error, record testcase run information. (Test errors should be treated the same as test failures.)""" self.__insert_test_result(constants.State.FAILED, test, err) self._result_set = True def handleError(self, test, err, capt=None): """After each test error, record testcase run information. "Error" also encompasses any states other than Pass or Fail.""" from nose.exc import SkipTest from seleniumbase.fixtures import errors if err[0] == errors.BlockedTest: self.__insert_test_result(constants.State.BLOCKED, test, err) self._result_set = True raise SkipTest(err[1]) elif err[0] == errors.DeprecatedTest: self.__insert_test_result(constants.State.DEPRECATED, test, err) self._result_set = True raise SkipTest(err[1]) elif err[0] == errors.SkipTest: self.__insert_test_result(constants.State.SKIPPED, test, err) self._result_set = True raise SkipTest(err[1]) def __insert_test_result(self, state, test, err=None): from seleniumbase.core.testcase_manager import TestcaseDataPayload data_payload = TestcaseDataPayload() data_payload.runtime = int(time.time() * 1000) - self.case_start_time data_payload.guid = self.testcase_guid data_payload.execution_guid = self.execution_guid data_payload.state = state if err is not None: data_payload.message = ( err[1] .__str__() .split( """-------------------- >> """ """begin captured logging""" """ << --------------------""", 1, )[0] ) self.testcase_manager.update_testcase_data(data_payload) ================================================ FILE: seleniumbase/plugins/driver_manager.py ================================================ """ The SeleniumBase Driver as a Python Context Manager or a returnable object. ########################################################################### The SeleniumBase Driver as a context manager: Usage --> ``with DriverContext() as driver:`` Example --> ```python from seleniumbase import DriverContext with DriverContext(uc=True) as driver: driver.get("https://google.com/ncr") ``` # (The browser exits automatically after the "with" block ends.) ########################################################################### # Above: The driver as a context manager. (Used with a "with" statement.) # # ----------------------------------------------------------------------- # # Below: The driver as a returnable object. (Used with "return" command.) # ########################################################################### The SeleniumBase Driver as a returnable object: Usage --> ``driver = Driver()`` Example --> ```python from seleniumbase import Driver driver = Driver(uc=True) driver.get("https://google.com/ncr") ``` ########################################################################### """ import os import sys from seleniumbase.core import sb_driver class DriverContext(): def __init__(self, *args, **kwargs): self.driver = Driver(*args, **kwargs) def __enter__(self): return self.driver def __exit__(self, exc_type, exc_val, exc_tb): try: if ( hasattr(self, "driver") and hasattr(self.driver, "quit") and ( "win32" not in sys.platform or self.driver.service.process ) ): self.driver.quit() except Exception: pass return False def Driver( browser=None, # Choose from "chrome", "edge", "firefox", or "safari". headless=None, # Use the default headless mode for Chromium and Firefox. headless1=None, # Use Chromium's old headless mode. (Fast, but limited) headless2=None, # Use Chromium's new headless mode. (Has more features) headed=None, # Run tests in headed/GUI mode on Linux, where not default. locale_code=None, # Set the Language Locale Code for the web browser. protocol=None, # The Selenium Grid protocol: "http" or "https". servername=None, # The Selenium Grid server/IP used for tests. port=None, # The Selenium Grid port used by the test server. proxy=None, # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT". proxy_bypass_list=None, # Skip proxy when using the listed domains. proxy_pac_url=None, # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL) multi_proxy=None, # Allow multiple proxies with auth when multi-threaded. agent=None, # Modify the web browser's User-Agent string. cap_file=None, # The desired capabilities to use with a Selenium Grid. cap_string=None, # The desired capabilities to use with a Selenium Grid. recorder_ext=None, # Enables the SeleniumBase Recorder Chromium extension. disable_cookies=None, # Disable Cookies on websites. (Pages might break!) disable_js=None, # Disable JavaScript on websites. (Pages might break!) disable_csp=None, # Disable the Content Security Policy of websites. enable_ws=None, # Enable Web Security on Chromium-based browsers. disable_ws=None, # Reverse of "enable_ws". (None and False are different) enable_sync=None, # Enable "Chrome Sync" on websites. use_auto_ext=None, # Use Chrome's automation extension. undetectable=None, # Use undetected-chromedriver to evade bot-detection. uc_cdp_events=None, # Capture CDP events in undetected-chromedriver mode. uc_subprocess=None, # Use undetected-chromedriver as a subprocess. log_cdp_events=None, # Capture {"performance": "ALL", "browser": "ALL"} no_sandbox=None, # (DEPRECATED) - "--no-sandbox" is always used now. disable_gpu=None, # (DEPRECATED) - GPU is disabled if not "swiftshader". incognito=None, # Enable Chromium's Incognito mode. guest_mode=None, # Enable Chromium's Guest mode. dark_mode=None, # Enable Chromium's Dark mode. devtools=None, # Open Chromium's DevTools when the browser opens. remote_debug=None, # Enable Chrome's Debugger on "http://localhost:9222". enable_3d_apis=None, # Enable WebGL and 3D APIs. swiftshader=None, # Chrome: --use-gl=angle / --use-angle=swiftshader-webgl ad_block_on=None, # Block some types of display ads from loading. host_resolver_rules=None, # Set host-resolver-rules, comma-separated. block_images=None, # Block images from loading during tests. do_not_track=None, # Tell websites that you don't want to be tracked. chromium_arg=None, # "ARG=N,ARG2" (Set Chromium args, ","-separated.) firefox_arg=None, # "ARG=N,ARG2" (Set Firefox args, comma-separated.) firefox_pref=None, # SET (Set Firefox PREFERENCE:VALUE set, ","-separated) user_data_dir=None, # Set the Chrome user data directory to use. extension_zip=None, # Load a Chrome Extension .zip|.crx, comma-separated. extension_dir=None, # Load a Chrome Extension directory, comma-separated. disable_features=None, # "F1,F2" (Disable Chrome features, ","-separated.) binary_location=None, # Set path of the Chromium browser binary to use. driver_version=None, # Set the chromedriver or uc_driver version to use. page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none". use_wire=None, # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None, # Set the browser's starting window position: "X,Y" window_size=None, # Set the browser's starting window size: "Width,Height" is_mobile=None, # Use the mobile device emulator while running tests. mobile=None, # Shortcut / Duplicate of "is_mobile". d_width=None, # Set device width d_height=None, # Set device height d_p_r=None, # Set device pixel ratio position=None, # Shortcut / Duplicate of "window_position". size=None, # Shortcut / Duplicate of "window_size". uc=None, # Shortcut / Duplicate of "undetectable". undetected=None, # Shortcut / Duplicate of "undetectable". uc_cdp=None, # Shortcut / Duplicate of "uc_cdp_events". uc_sub=None, # Shortcut / Duplicate of "uc_subprocess". locale=None, # Shortcut / Duplicate of "locale_code". log_cdp=None, # Shortcut / Duplicate of "log_cdp_events". ad_block=None, # Shortcut / Duplicate of "ad_block_on". server=None, # Shortcut / Duplicate of "servername". guest=None, # Shortcut / Duplicate of "guest_mode". wire=None, # Shortcut / Duplicate of "use_wire". pls=None, # Shortcut / Duplicate of "page_load_strategy". cft=None, # Use "Chrome for Testing" chs=None, # Use "Chrome-Headless-Shell" use_chromium=None, # Use base "Chromium" ) -> sb_driver.DriverMethods: """ * SeleniumBase Driver as a Python Context Manager or a returnable object. * Example 1: (context manager format) ----------------------------------- .. code-block:: python from seleniumbase import DriverContext with DriverContext() as driver: driver.get("https://google.com/ncr") Example 2: (as a Python returnable) ----------------------------------- .. code-block:: python from seleniumbase import Driver driver = Driver() driver.get("https://google.com/ncr") Optional Parameters: -------------------- browser (str): Choose from "chrome", "edge", "firefox", or "safari". headless (bool): Use the default headless mode for Chromium and Firefox. headless1 (bool): Use Chromium's old headless mode. (Fast, but limited) headless2 (bool): Use Chromium's new headless mode. (Has more features) headed (bool): Run tests in headed/GUI mode on Linux, where not default. locale_code (str): Set the Language Locale Code for the web browser. protocol (str): The Selenium Grid protocol: "http" or "https". servername (str): The Selenium Grid server/IP used for tests. port (int): The Selenium Grid port used by the test server. proxy (str): Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT". proxy_bypass_list (str): Skip proxy when using the listed domains. proxy_pac_url (str): Use PAC file. (Format: URL or USERNAME:PASSWORD@URL) multi_proxy (bool): Allow multiple proxies with auth when multi-threaded. agent (str): Modify the web browser's User-Agent string. cap_file (str): The desired capabilities to use with a Selenium Grid. cap_string (str): The desired capabilities to use with a Selenium Grid. recorder_ext (bool): Enables the SeleniumBase Recorder Chromium extension. disable_cookies (bool): Disable Cookies on websites. (Pages might break!) disable_js (bool): Disable JavaScript on websites. (Pages might break!) disable_csp (bool): Disable the Content Security Policy of websites. enable_ws (bool): Enable Web Security on Chromium-based browsers. disable_ws (bool): Reverse of "enable_ws". (None and False are different) enable_sync (bool): Enable "Chrome Sync" on websites. use_auto_ext (bool): Use Chrome's automation extension. undetectable (bool): Use undetected-chromedriver to evade bot-detection. uc_cdp_events (bool): Capture CDP events in undetected-chromedriver mode. uc_subprocess (bool): Use undetected-chromedriver as a subprocess. log_cdp_events (bool): Capture {"performance": "ALL", "browser": "ALL"} no_sandbox (bool): (DEPRECATED) - "--no-sandbox" is always used now. disable_gpu (bool): (DEPRECATED) - GPU is disabled if not "swiftshader". incognito (bool): Enable Chromium's Incognito mode. guest_mode (bool): Enable Chromium's Guest mode. dark_mode (bool): Enable Chromium's Dark mode. devtools (bool): Open Chromium's DevTools when the browser opens. remote_debug (bool): Enable Chrome's Debugger on "http://localhost:9222". enable_3d_apis (bool): Enable WebGL and 3D APIs. swiftshader (bool): Chrome: --use-gl=angle / --use-angle=swiftshader-webgl ad_block_on (bool): Block some types of display ads from loading. host_resolver_rules (str): Set host-resolver-rules, comma-separated. block_images (bool): Block images from loading during tests. do_not_track (bool): Tell websites that you don't want to be tracked. chromium_arg (str): "ARG=N,ARG2" (Set Chromium args, ","-separated.) firefox_arg (str): "ARG=N,ARG2" (Set Firefox args, comma-separated.) firefox_pref (str): SET (Set Firefox PREFERENCE:VALUE set, ","-separated) user_data_dir (str): Set the Chrome user data directory to use. extension_zip (str): Load a Chrome Extension .zip|.crx, comma-separated. extension_dir (str): Load a Chrome Extension directory, comma-separated. disable_features (str): "F1,F2" (Disable Chrome features, ","-separated.) binary_location (str): Set path of the Chromium browser binary to use. driver_version (str): Set the chromedriver or uc_driver version to use. page_load_strategy (str): Set Chrome PLS to "normal", "eager", or "none". use_wire (bool): Use selenium-wire's webdriver over selenium webdriver. external_pdf (bool): Set Chrome "plugins.always_open_pdf_externally":True window_position (x,y): Set the browser's starting window position: "X,Y" window_size (w,h): Set the browser's starting window size: "Width,Height" is_mobile (bool): Use the mobile device emulator while running tests. mobile (bool): Shortcut / Duplicate of "is_mobile". d_width (int): Set device width d_height (int): Set device height d_p_r (float): Set device pixel ratio position (x,y): Shortcut / Duplicate of "window_position". size (w,h): Shortcut / Duplicate of "window_size". uc (bool): Shortcut / Duplicate of "undetectable". undetected (bool): Shortcut / Duplicate of "undetectable". uc_cdp (bool): Shortcut / Duplicate of "uc_cdp_events". uc_sub (bool): Shortcut / Duplicate of "uc_subprocess". locale (str): Shortcut / Duplicate of "locale_code". log_cdp (bool): Shortcut / Duplicate of "log_cdp_events". ad_block (bool): Shortcut / Duplicate of "ad_block_on". server (str): Shortcut / Duplicate of "servername". guest (bool): Shortcut / Duplicate of "guest_mode". wire (bool): Shortcut / Duplicate of "use_wire". pls (str): Shortcut / Duplicate of "page_load_strategy". """ from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import browser_launcher from seleniumbase.core import detect_b_ver from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils sys_argv = sys.argv arg_join = " ".join(sys_argv) existing_runner = False collect_only = ("--co" in sys_argv or "--collect-only" in sys_argv) all_scripts = (hasattr(sb_config, "all_scripts") and sb_config.all_scripts) if ( (hasattr(sb_config, "is_behave") and sb_config.is_behave) or (hasattr(sb_config, "is_pytest") and sb_config.is_pytest) or (hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest) ): existing_runner = True if ( existing_runner and not hasattr(sb_config, "_context_of_runner") ): if hasattr(sb_config, "is_pytest") and sb_config.is_pytest: import pytest msg = "Skipping `Driver()` script. (Use `python`, not `pytest`)" if not collect_only and not all_scripts: print("\n *** %s" % msg) if collect_only or not all_scripts: pytest.skip(allow_module_level=True) elif hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest: raise Exception( "\n A Driver() script was triggered by nosetest collection!" '\n (Prevent that by using: ``if __name__ == "__main__":``)' ) elif existing_runner: sb_config._context_of_runner = True sb_config._browser_shortcut = None sb_config._cdp_browser = None sb_config._cdp_bin_loc = None browser_changes = 0 browser_set = None browser_text = None browser_list = [] # Check if binary-location in options bin_loc_in_options = False if ( binary_location and len(str(binary_location)) > 5 and os.path.exists(str(binary_location)) ): bin_loc_in_options = True else: for arg in sys_argv: if arg in ["--binary-location", "--binary_location", "--bl"]: bin_loc_in_options = True if ( browser and browser in constants.ChromiumSubs.chromium_subs and not bin_loc_in_options ): bin_loc = detect_b_ver.get_binary_location(browser) if bin_loc and os.path.exists(bin_loc): if browser in bin_loc.lower().split("/")[-1].split("\\")[-1]: sb_config._cdp_browser = browser sb_config._cdp_bin_loc = bin_loc binary_location = bin_loc bin_loc_in_options = True # As a shortcut, you can use "--edge" instead of "--browser=edge", etc, # but you can only specify one default browser for tests. (Default: chrome) if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: browser_changes += 1 browser_set = "chrome" browser_list.append("--browser=chrome") if "--browser=edge" in sys_argv or "--browser edge" in sys_argv: browser_changes += 1 browser_set = "edge" browser_list.append("--browser=edge") if "--browser=firefox" in sys_argv or "--browser firefox" in sys_argv: browser_changes += 1 browser_set = "firefox" browser_list.append("--browser=firefox") if "--browser=safari" in sys_argv or "--browser safari" in sys_argv: browser_changes += 1 browser_set = "safari" browser_list.append("--browser=safari") if "--browser=ie" in sys_argv or "--browser ie" in sys_argv: browser_changes += 1 browser_set = "ie" browser_list.append("--browser=ie") if "--browser=remote" in sys_argv or "--browser remote" in sys_argv: browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=opera") if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=brave") if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 browser_text = "chrome" browser_list.append("--chrome") if "--edge" in sys_argv and not browser_set == "edge": browser_changes += 1 browser_text = "edge" browser_list.append("--edge") if "--firefox" in sys_argv and not browser_set == "firefox": browser_changes += 1 browser_text = "firefox" browser_list.append("--firefox") if "--ie" in sys_argv and not browser_set == "ie": browser_changes += 1 browser_text = "ie" browser_list.append("--ie") if "--safari" in sys_argv and not browser_set == "safari": browser_changes += 1 browser_text = "safari" browser_list.append("--safari") if "--opera" in sys_argv and not browser_set == "opera": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--opera") if "--brave" in sys_argv and not browser_set == "brave": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--brave") if "--comet" in sys_argv and not browser_set == "comet": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") if "--atlas" in sys_argv and not browser_set == "atlas": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( browser_changes, ", ".join(browser_list), ) message += "\n ONLY ONE default browser is allowed!" message += "\n Select a single browser & try again!\n" if not browser: raise Exception(message) if browser is None: if browser_text: browser = browser_text else: browser = "chrome" else: browser = browser.lower() valid_browsers = constants.ValidBrowsers.valid_browsers if browser not in valid_browsers: raise Exception( "Browser: {%s} is not a valid browser option. " "Valid options = {%s}" % (browser, valid_browsers) ) if sb_config._browser_shortcut: browser = sb_config._browser_shortcut if headless is None: if "--headless" in sys_argv: headless = True else: headless = False if headless1 is None: if "--headless1" in sys_argv: headless1 = True else: headless1 = False if headless1: headless = True if headless2 is None: if "--headless2" in sys_argv: headless2 = True else: headless2 = False if protocol is None: protocol = "http" # For the Selenium Grid only! if server is not None and servername is None: servername = server if servername is None: servername = "localhost" # For the Selenium Grid only! use_grid = False if servername != "localhost": # Use Selenium Grid (Use "127.0.0.1" for localhost Grid) use_grid = True if port is None: port = "4444" # For the Selenium Grid only! if incognito is None: if "--incognito" in sys_argv: incognito = True else: incognito = False if guest is not None and guest_mode is None: guest_mode = guest if guest_mode is None: if "--guest" in sys_argv: guest_mode = True else: guest_mode = False if dark_mode is None: if "--dark" in sys_argv: dark_mode = True else: dark_mode = False if devtools is None: if "--devtools" in sys_argv: devtools = True else: devtools = False if mobile is not None and is_mobile is None: is_mobile = mobile if is_mobile is None: if "--mobile" in sys_argv: is_mobile = True else: is_mobile = False test_id = "direct_driver" proxy_string = proxy if proxy_string is None and "--proxy" in arg_join: if "--proxy=" in arg_join: proxy_string = arg_join.split("--proxy=")[1].split(" ")[0] elif "--proxy " in arg_join: proxy_string = arg_join.split("--proxy ")[1].split(" ")[0] if proxy_string: if proxy_string.startswith('"') and proxy_string.endswith('"'): proxy_string = proxy_string[1:-1] elif proxy_string.startswith("'") and proxy_string.endswith("'"): proxy_string = proxy_string[1:-1] c_a = chromium_arg if c_a is None and "--chromium-arg" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--chromium-arg="): c_a = arg.split("--chromium-arg=")[1] break elif arg == "--chromium-arg" and len(sys_argv) > count + 1: c_a = sys_argv[count + 1] if c_a.startswith("-"): c_a = None break count += 1 chromium_arg = c_a d_f = disable_features if d_f is None and "--disable-features" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--disable-features="): d_f = arg.split("--disable-features=")[1] break elif arg == "--disable-features" and len(sys_argv) > count + 1: d_f = sys_argv[count + 1] if d_f.startswith("-"): d_f = None break count += 1 disable_features = d_f if window_position is None and position is not None: window_position = position w_p = window_position if w_p is None and "--window-position" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--window-position="): w_p = arg.split("--window-position=")[1] break elif arg == "--window-position" and len(sys_argv) > count + 1: w_p = sys_argv[count + 1] if w_p.startswith("-"): w_p = None break count += 1 window_position = w_p if window_position: if window_position.count(",") != 1: message = ( '\n\n window_position expects an "x,y" string!' '\n (Your input was: "%s")\n' % window_position ) raise Exception(message) window_position = window_position.replace(" ", "") win_x = None win_y = None try: win_x = int(window_position.split(",")[0]) win_y = int(window_position.split(",")[1]) except Exception: message = ( '\n\n Expecting integer values for "x,y"!' '\n (window_position input was: "%s")\n' % window_position ) raise Exception(message) settings.WINDOW_START_X = win_x settings.WINDOW_START_Y = win_y if window_size is None and size is not None: window_size = size w_s = window_size if w_s is None and "--window-size" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--window-size="): w_s = arg.split("--window-size=")[1] break elif arg == "--window-size" and len(sys_argv) > count + 1: w_s = sys_argv[count + 1] if w_s.startswith("-"): w_s = None break count += 1 window_size = w_s if window_size: if window_size.count(",") != 1: message = ( '\n\n window_size expects a "width,height" string!' '\n (Your input was: "%s")\n' % window_size ) raise Exception(message) window_size = window_size.replace(" ", "") width = None height = None try: width = int(window_size.split(",")[0]) height = int(window_size.split(",")[1]) except Exception: message = ( '\n\n Expecting integer values for "width,height"!' '\n (window_size input was: "%s")\n' % window_size ) raise Exception(message) settings.CHROME_START_WIDTH = width settings.CHROME_START_HEIGHT = height settings.HEADLESS_START_WIDTH = width settings.HEADLESS_START_HEIGHT = height if agent is None and "--agent" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--agent="): agent = arg.split("--agent=")[1] break elif arg == "--agent" and len(sys_argv) > count + 1: agent = sys_argv[count + 1] if agent.startswith("-"): agent = None break count += 1 user_agent = agent found_bl = None if hasattr(sb_config, "_cdp_bin_loc") and sb_config._cdp_bin_loc: binary_location = sb_config._cdp_bin_loc if binary_location is None and "--binary-location" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--binary-location="): found_bl = arg.split("--binary-location=")[1] break elif arg == "--binary-location" and len(sys_argv) > count + 1: found_bl = sys_argv[count + 1] if found_bl.startswith("-"): found_bl = None break count += 1 if found_bl: binary_location = found_bl if binary_location is None and "--bl=" in arg_join: for arg in sys_argv: if arg.startswith("--bl="): binary_location = arg.split("--bl=")[1] break if use_chromium and not binary_location: binary_location = "_chromium_" elif cft and not binary_location: binary_location = "cft" elif chs and not binary_location: binary_location = "chs" if "--use-chromium" in sys_argv and not binary_location: binary_location = "_chromium_" elif "--cft" in sys_argv and not binary_location: binary_location = "cft" elif "--chs" in sys_argv and not binary_location: binary_location = "chs" if ( binary_location and binary_location.lower() == "chs" and browser == "chrome" ): headless = True headless1 = False headless2 = False recorder_mode = False if recorder_ext: recorder_mode = True if ( "--recorder" in sys_argv or "--record" in sys_argv or "--rec" in sys_argv ): recorder_mode = True recorder_ext = True if ( undetectable or undetected or uc or uc_cdp_events or uc_cdp or uc_subprocess or uc_sub ): undetectable = True if undetectable or undetected or uc: uc_subprocess = True # Use UC as a subprocess by default. elif ( "--undetectable" in sys_argv or "--undetected" in sys_argv or "--uc" in sys_argv or "--uc-cdp-events" in sys_argv or "--uc_cdp_events" in sys_argv or "--uc-cdp" in sys_argv or "--uc-subprocess" in sys_argv or "--uc_subprocess" in sys_argv or "--uc-sub" in sys_argv ): undetectable = True if uc_subprocess is None and uc_sub is None: uc_subprocess = True # Use UC as a subprocess by default. else: undetectable = False if uc_subprocess or uc_sub: uc_subprocess = True elif ( "--uc-subprocess" in sys_argv or "--uc_subprocess" in sys_argv or "--uc-sub" in sys_argv ): uc_subprocess = True else: uc_subprocess = False if uc_cdp_events or uc_cdp: undetectable = True uc_cdp_events = True elif ( "--uc-cdp-events" in sys_argv or "--uc_cdp_events" in sys_argv or "--uc-cdp" in sys_argv or "--uc_cdp" in sys_argv ): undetectable = True uc_cdp_events = True else: uc_cdp_events = False if ( undetectable and browser not in ["chrome", "opera", "brave", "comet", "atlas"] ): message = ( '\n Undetected-Chromedriver Mode ONLY supports Chromium browsers!' '\n ("uc=True" / "undetectable=True" / "--uc")' '\n (Your browser choice was: "%s".)' '\n (Will use "%s" without UC Mode.)\n' % (browser, browser) ) print(message) if headed is None: # Override the default headless mode on Linux if set. if "--gui" in sys_argv or "--headed" in sys_argv: headed = True else: headed = False if ( shared_utils.is_linux() and not headed and not headless and not headless2 and ( not undetectable or "DISPLAY" not in os.environ.keys() or not os.environ["DISPLAY"] ) ): headless = True if recorder_mode and headless: headless = False headless1 = False headless2 = True if headless2 and browser == "firefox": headless2 = False # Only for Chromium browsers headless = True # Firefox has regular headless elif browser not in [ "chrome", "edge", "opera", "brave", "comet", "atlas" ]: headless2 = False # Only for Chromium browsers if disable_csp is None: if ( "--disable-csp" in sys_argv or "--no-csp" in sys_argv or "--dcsp" in sys_argv ): disable_csp = True else: disable_csp = False if ( (enable_ws is None and disable_ws is None) and ( "--disable-web-security" in sys_argv or "--disable-ws" in sys_argv or "--dws" in sys_argv ) ): enable_ws = False elif ( (enable_ws is None and disable_ws is None) or (disable_ws is not None and not disable_ws) or (enable_ws is not None and enable_ws) ): enable_ws = True else: enable_ws = False if log_cdp_events is None and log_cdp is None: if ( "--log-cdp-events" in sys_argv or "--log_cdp_events" in sys_argv or "--log-cdp" in sys_argv or "--log_cdp" in sys_argv ): log_cdp_events = True else: log_cdp_events = False elif log_cdp_events or log_cdp: log_cdp_events = True else: log_cdp_events = False if use_auto_ext is None: if "--use-auto-ext" in sys_argv: use_auto_ext = True else: use_auto_ext = False if disable_cookies is None: if "--disable-cookies" in sys_argv: disable_cookies = True else: disable_cookies = False if disable_js is None: if "--disable-js" in sys_argv: disable_js = True else: disable_js = False if pls is not None and page_load_strategy is None: page_load_strategy = pls if not page_load_strategy and "--pls=" in arg_join: if "--pls=none" in sys_argv or '--pls="none"' in sys_argv: page_load_strategy = "none" elif "--pls=eager" in sys_argv or '--pls="eager"' in sys_argv: page_load_strategy = "eager" elif "--pls=normal" in sys_argv or '--pls="normal"' in sys_argv: page_load_strategy = "normal" if page_load_strategy is not None: if page_load_strategy.lower() not in ["normal", "eager", "none"]: raise Exception( 'page_load_strategy must be "normal", "eager", or "none"!' ) page_load_strategy = page_load_strategy.lower() elif "--pls=normal" in sys_argv or '--pls="normal"' in sys_argv: page_load_strategy = "normal" elif "--pls=eager" in sys_argv or '--pls="eager"' in sys_argv: page_load_strategy = "eager" elif "--pls=none" in sys_argv or '--pls="none"' in sys_argv: page_load_strategy = "none" if block_images is None: if "--block-images" in sys_argv or "--block_images" in sys_argv: block_images = True else: block_images = False if do_not_track is None: if "--do-not-track" in sys_argv or "--do_not_track" in sys_argv: do_not_track = True else: do_not_track = False if use_wire is None and wire is None: if "--wire" in sys_argv: use_wire = True else: use_wire = False elif use_wire or wire: use_wire = True else: use_wire = False if external_pdf is None: if "--external-pdf" in sys_argv or "--external_pdf" in sys_argv: external_pdf = True else: external_pdf = False if remote_debug is None: if "--remote-debug" in sys_argv or "--remote_debug" in sys_argv: remote_debug = True else: remote_debug = False if enable_3d_apis is None: if "--enable-3d-apis" in sys_argv or "--enable_3d_apis" in sys_argv: enable_3d_apis = True else: enable_3d_apis = False if swiftshader is None: if "--swiftshader" in sys_argv: swiftshader = True else: swiftshader = False if locale is not None and locale_code is None: locale_code = locale if locale_code is None: if '--locale="' in arg_join: locale_code = ( arg_join.split('--locale="')[1].split('"')[0] ) elif '--locale=' in arg_join: locale_code = ( arg_join.split('--locale=')[1].split(' ')[0] ) elif '--locale-code="' in arg_join: locale_code = ( arg_join.split('--locale-code="')[1].split('"')[0] ) elif '--locale-code=' in arg_join: locale_code = ( arg_join.split('--locale-code=')[1].split(' ')[0] ) if ad_block is not None and ad_block_on is None: ad_block_on = ad_block if ad_block_on is None: if "--ad-block" in sys_argv or "--ad_block" in sys_argv: ad_block_on = True else: ad_block_on = False if host_resolver_rules is None: if '--host-resolver-rules="' in arg_join: host_resolver_rules = ( arg_join.split('--host-resolver-rules="')[1].split('"')[0] ) elif '--host_resolver_rules="' in arg_join: host_resolver_rules = ( arg_join.split("--host_resolver_rules=")[1].split('"')[0] ) if driver_version is None and "--driver-version" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--driver-version="): driver_version = arg.split("--driver-version=")[1] break elif arg == "--driver-version" and len(sys_argv) > count + 1: driver_version = sys_argv[count + 1] if driver_version.startswith("-"): driver_version = None break count += 1 if driver_version is None and "--driver_version" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--driver_version="): driver_version = arg.split("--driver_version=")[1] break elif arg == "--driver_version" and len(sys_argv) > count + 1: driver_version = sys_argv[count + 1] if driver_version.startswith("-"): driver_version = None break count += 1 if browser in constants.ChromiumSubs.chromium_subs: if not binary_location: browser = "chrome" # Still uses chromedriver sb_config._browser_shortcut = browser browser_name = browser # Launch a web browser driver = browser_launcher.get_driver( browser_name=browser_name, headless=headless, locale_code=locale_code, use_grid=use_grid, protocol=protocol, servername=servername, port=port, proxy_string=proxy_string, proxy_bypass_list=proxy_bypass_list, proxy_pac_url=proxy_pac_url, multi_proxy=multi_proxy, user_agent=user_agent, cap_file=cap_file, cap_string=cap_string, recorder_ext=recorder_ext, disable_cookies=disable_cookies, disable_js=disable_js, disable_csp=disable_csp, enable_ws=enable_ws, enable_sync=enable_sync, use_auto_ext=use_auto_ext, undetectable=undetectable, uc_cdp_events=uc_cdp_events, uc_subprocess=uc_subprocess, log_cdp_events=log_cdp_events, no_sandbox=no_sandbox, disable_gpu=disable_gpu, headless1=headless1, headless2=headless2, incognito=incognito, guest_mode=guest_mode, dark_mode=dark_mode, devtools=devtools, remote_debug=remote_debug, enable_3d_apis=enable_3d_apis, swiftshader=swiftshader, ad_block_on=ad_block_on, host_resolver_rules=host_resolver_rules, block_images=block_images, do_not_track=do_not_track, chromium_arg=chromium_arg, firefox_arg=firefox_arg, firefox_pref=firefox_pref, user_data_dir=user_data_dir, extension_zip=extension_zip, extension_dir=extension_dir, disable_features=disable_features, binary_location=binary_location, driver_version=driver_version, page_load_strategy=page_load_strategy, use_wire=use_wire, external_pdf=external_pdf, test_id=test_id, mobile_emulator=is_mobile, device_width=d_width, device_height=d_height, device_pixel_ratio=d_p_r, browser=browser_name, ) return driver ================================================ FILE: seleniumbase/plugins/page_source.py ================================================ """PageSource Plugin for SeleniumBase tests that run with pynose / nosetests""" import os from nose.plugins import Plugin from seleniumbase.config import settings from seleniumbase.core import log_helper class PageSource(Plugin): """Capture the page source after a test fails.""" name = "page_source" # Usage: --with-page_source logfile_name = settings.PAGE_SOURCE_NAME def options(self, parser, env): super().options(parser, env=env) def configure(self, options, conf): super().configure(options, conf) if not self.enabled: return self.options = options def addError(self, test, err, capt=None): try: page_source = test.driver.page_source except Exception: return test_logpath = self.options.log_path + "/" + test.id() if not os.path.exists(test_logpath): os.makedirs(test_logpath) html_file_name = os.path.join(test_logpath, self.logfile_name) html_file = open(html_file_name, mode="w+", encoding="utf-8") rendered_source = log_helper.get_html_source_with_base_href( test.driver, page_source ) html_file.write(rendered_source) html_file.close() def addFailure(self, test, err, capt=None, tbinfo=None): try: page_source = test.driver.page_source except Exception: return test_logpath = self.options.log_path + "/" + test.id() if not os.path.exists(test_logpath): os.makedirs(test_logpath) html_file_name = os.path.join(test_logpath, self.logfile_name) html_file = open(html_file_name, mode="w+", encoding="utf-8") rendered_source = log_helper.get_html_source_with_base_href( test.driver, page_source ) html_file.write(rendered_source) html_file.close() ================================================ FILE: seleniumbase/plugins/pytest_plugin.py ================================================ """This is the pytest configuration file for setting test options.""" import colorama import os import pytest import sys import time from contextlib import suppress from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import detect_b_ver from seleniumbase.core import log_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils is_windows = shared_utils.is_windows() python3_11_or_newer = False if sys.version_info >= (3, 11): python3_11_or_newer = True py311_patch2 = constants.PatchPy311.PATCH sys_argv = sys.argv full_time = None pytest_plugins = ["pytester"] # Adds the "testdir" fixture def pytest_addoption(parser): """This plugin adds the following command-line options to pytest: --browser=BROWSER (The web browser to use. Default: "chrome".) --chrome (Shortcut for "--browser=chrome". Default.) --edge (Shortcut for "--browser=edge".) --firefox (Shortcut for "--browser=firefox".) --safari (Shortcut for "--browser=safari".) --opera (Shortcut for "--browser=opera".) --brave (Shortcut for "--browser=brave".) --comet (Shortcut for "--browser=comet".) --atlas (Shortcut for "--browser=atlas".) --use-chromium (Shortcut for using base `Chromium`) --cft (Shortcut for using `Chrome for Testing`) --chs (Shortcut for using `Chrome-Headless-Shell`) --settings-file=FILE (Override default SeleniumBase settings.) --env=ENV (Set the test env. Access with "self.env" in tests.) --account=STR (Set account. Access with "self.account" in tests.) --data=STRING (Extra test data. Access with "self.data" in tests.) --var1=STRING (Extra test data. Access with "self.var1" in tests.) --var2=STRING (Extra test data. Access with "self.var2" in tests.) --var3=STRING (Extra test data. Access with "self.var3" in tests.) --variables=DICT (Extra test data. Access with "self.variables".) --user-data-dir=DIR (Set the Chrome user data directory to use.) --protocol=PROTOCOL (The Selenium Grid protocol: http|https.) --server=SERVER (The Selenium Grid server/IP used for tests.) --port=PORT (The Selenium Grid port used by the test server.) --cap-file=FILE (The web browser's desired capabilities to use.) --cap-string=STRING (The web browser's desired capabilities to use.) --proxy=SERVER:PORT (Connect to a proxy server:port as tests are running) --proxy=USERNAME:PASSWORD@SERVER:PORT (Use an authenticated proxy server) --proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com") --proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.) --proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.) --proxy-driver (If a driver download is needed, will use: --proxy=PROXY.) --multi-proxy (Allow multiple authenticated proxies when multi-threaded.) --agent=STRING (Modify the web browser's User-Agent string.) --mobile (Use the mobile device emulator while running tests.) --metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".) --chromium-arg="ARG=N,ARG2" (Set Chromium args, ","-separated, no spaces.) --firefox-arg="ARG=N,ARG2" (Set Firefox args, comma-separated, no spaces.) --firefox-pref=SET (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR (Load a Chrome Extension directory, comma-separated.) --disable-features="F1,F2" (Disable features, comma-separated, no spaces.) --binary-location=PATH (Set path of the Chromium browser binary to use.) --driver-version=VER (Set the chromedriver or uc_driver version to use.) --sjw (Skip JS Waits for readyState to be "complete" or Angular to load.) --wfa (Wait for AngularJS to be done loading after specific web actions.) --pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) --headless (The default headless mode. Linux uses this mode by default.) --headless1 (Use Chrome's old headless mode. Fast, but has limitations.) --headless2 (Use Chrome's new headless mode, which supports extensions.) --headed (Run tests in headed/GUI mode on Linux OS, where not default.) --xvfb (Run tests using the Xvfb virtual display server on Linux OS.) --xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".) --locale=LOCALE_CODE (Set the Language Locale Code for the web browser.) --interval=SECONDS (The autoplay interval for presentations & tour steps) --start-page=URL (The starting URL for the web browser when tests begin.) --archive-logs (Archive existing log files instead of deleting them.) --archive-downloads (Archive old downloads instead of deleting them.) --time-limit=SECONDS (Safely fail any test that exceeds the time limit.) --slow (Slow down the automation. Faster than using Demo Mode.) --demo (Slow down and visually see test actions as they occur.) --demo-sleep=SECONDS (Set the wait time after Slow & Demo Mode actions.) --highlights=NUM (Number of highlight animations for Demo Mode actions.) --message-duration=SECONDS (The time length for Messenger alerts.) --check-js (Check for JavaScript errors after page loads.) --ad-block (Block some types of display ads from loading.) --host-resolver-rules=RULES (Set host-resolver-rules, comma-separated.) --block-images (Block images from loading during tests.) --do-not-track (Indicate to websites that you don't want to be tracked.) --verify-delay=SECONDS (The delay before MasterQA verification checks.) --ee / --esc-end (Lets the user end the current test via the ESC key.) --recorder (Enables the Recorder for turning browser actions into code.) --rec-behave (Same as Recorder Mode, but also generates behave-gherkin.) --rec-sleep (If the Recorder is enabled, also records self.sleep calls.) --rec-print (If the Recorder is enabled, prints output after tests end.) --disable-cookies (Disable Cookies on websites. Pages might break!) --disable-js (Disable JavaScript on websites. Pages might break!) --disable-csp (Disable the Content Security Policy of websites.) --disable-ws (Disable Web Security on Chromium-based browsers.) --enable-ws (Enable Web Security on Chromium-based browsers.) --enable-sync (Enable "Chrome Sync" on websites.) --uc | --undetected (Use undetected-chromedriver to evade bot-detection.) --uc-cdp-events (Capture CDP events when running in "--undetected" mode.) --log-cdp ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) --remote-debug (Sync to Chrome Remote Debugger chrome://inspect/#devices) --ftrace | --final-trace (Debug Mode after each test. Don't use with CI!) --dashboard (Enable the SeleniumBase Dashboard. Saved at: dashboard.html) --dash-title=STRING (Set the title shown for the generated dashboard.) --enable-3d-apis (Enables WebGL and 3D APIs.) --swiftshader (Chrome "--use-gl=angle" / "--use-angle=swiftshader-webgl") --incognito (Enable Chrome's Incognito mode.) --guest (Enable Chrome's Guest mode.) --dark (Enable Chrome's Dark mode.) --devtools (Open Chrome's DevTools when the browser opens.) --rs | --reuse-session (Reuse browser session for all tests.) --rcs | --reuse-class-session (Reuse session for tests in class.) --crumbs (Delete all cookies between tests reusing a session.) --disable-beforeunload (Disable the "beforeunload" event on Chrome.) --window-position=X,Y (Set the browser's starting window position.) --window-size=WIDTH,HEIGHT (Set the browser's starting window size.) --maximize (Start tests with the browser window maximized.) --screenshot (Save a screenshot at the end of each test.) --no-screenshot (No screenshots saved unless tests directly ask it.) --visual-baseline (Set the visual baseline for Visual/Layout tests.) --wire (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf (Set Chromium "plugins.always_open_pdf_externally":True.) --timeout-multiplier=MULTIPLIER (Multiplies the default timeout values.) --list-fail-page (After each failing test, list the URL of the failure.) """ c1 = "" c2 = "" c3 = "" cr = "" if "linux" not in sys.platform: # This will be seen when typing "pytest --help" on the command line. c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c3 = colorama.Fore.MAGENTA + colorama.Back.LIGHTYELLOW_EX cr = colorama.Style.RESET_ALL s_str = "SeleniumBase" s_str = s_str.replace("SeleniumBase", c1 + "Selenium" + c2 + "Base" + cr) s_str = s_str + cr + " " + c3 + "command-line options for pytest" + cr parser = parser.getgroup("SeleniumBase", s_str) parser.addoption( "--browser", action="store", dest="browser", type=str.lower, choices=constants.ValidBrowsers.valid_browsers, default=constants.Browser.GOOGLE_CHROME, help="""Specifies the web browser to use. Default: Chrome. Examples: (--browser=edge OR --browser=firefox)""", ) parser.addoption( "--chrome", action="store_true", dest="use_chrome", default=False, help="""Shortcut for --browser=chrome (Default)""", ) parser.addoption( "--edge", action="store_true", dest="use_edge", default=False, help="""Shortcut for --browser=edge""", ) parser.addoption( "--firefox", action="store_true", dest="use_firefox", default=False, help="""Shortcut for --browser=firefox""", ) parser.addoption( "--ie", action="store_true", dest="use_ie", default=False, help="""Shortcut for --browser=ie""", ) parser.addoption( "--safari", action="store_true", dest="use_safari", default=False, help="""Shortcut for --browser=safari""", ) parser.addoption( "--opera", action="store_true", dest="use_opera", default=False, help="""Shortcut for --browser=opera""", ) parser.addoption( "--brave", action="store_true", dest="use_brave", default=False, help="""Shortcut for --browser=brave""", ) parser.addoption( "--comet", action="store_true", dest="use_comet", default=False, help="""Shortcut for --browser=comet""", ) parser.addoption( "--atlas", action="store_true", dest="use_atlas", default=False, help="""Shortcut for --browser=atlas""", ) parser.addoption( "--use-chromium", action="store_true", dest="use_chromium", default=False, help="""Shortcut for using base `Chromium`""", ) parser.addoption( "--cft", action="store_true", dest="use_cft", default=False, help="""Shortcut for using `Chrome for Testing`""", ) parser.addoption( "--chs", action="store_true", dest="use_chs", default=False, help="""Shortcut for using `Chrome-Headless-Shell`""", ) parser.addoption( "--with-selenium", action="store_true", dest="with_selenium", default=True, help="""(DEPRECATED) Start tests with an open web browser. (This is ALWAYS True now when importing BaseCase)""", ) parser.addoption( "--env", action="store", dest="environment", type=str.lower, choices=( constants.Environment.QA, constants.Environment.RC, constants.Environment.STAGING, constants.Environment.DEVELOP, constants.Environment.PRODUCTION, constants.Environment.PERFORMANCE, constants.Environment.REPLICA, constants.Environment.FEDRAMP, constants.Environment.OFFLINE, constants.Environment.ONLINE, constants.Environment.MASTER, constants.Environment.REMOTE, constants.Environment.LEGACY, constants.Environment.LOCAL, constants.Environment.ALPHA, constants.Environment.BETA, constants.Environment.DEMO, constants.Environment.GDPR, constants.Environment.MAIN, constants.Environment.TEST, constants.Environment.GOV, constants.Environment.NEW, constants.Environment.OLD, constants.Environment.UAT, ), default=constants.Environment.TEST, help="""This option sets a test env from a list of choices. Access using "self.env" or "self.environment".""", ) parser.addoption( "--account", dest="account", default=None, help="""This option sets a test account string. In tests, use "self.account" to get the value.""", ) parser.addoption( "--data", dest="data", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--var1", dest="var1", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--var2", dest="var2", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--var3", dest="var3", default=None, help="Extra data to pass to tests from the command line.", ) parser.addoption( "--variables", dest="variables", default=None, help="""A var dict to pass to tests from the command line. Example usage: ---------------------------------------------- Option: --variables='{"special":123}' Access: self.variables["special"] # (123) ---------------------------------------------- Option: --variables='{"color":"red","num":42}' Access: self.variables["color"] # ("red") Access: self.variables["num"] # (42) ----------------------------------------------""", ) parser.addoption( "--cap_file", "--cap-file", dest="cap_file", default=None, help="""The file that stores browser desired capabilities for BrowserStack, Sauce Labs, or other grids.""", ) parser.addoption( "--cap_string", "--cap-string", dest="cap_string", default=None, help="""The string that stores browser desired capabilities for BrowserStack, Sauce Labs, or other grids. Enclose cap-string in single quotes. Enclose parameter keys in double quotes. Example: --cap-string='{"name":"test1","v":"42"}'""", ) parser.addoption( "--settings_file", "--settings-file", "--settings", action="store", dest="settings_file", default=None, help="""The file that stores key/value pairs for overriding values in the seleniumbase/config/settings.py file.""", ) parser.addoption( "--user_data_dir", "--user-data-dir", dest="user_data_dir", default=None, help="""The Chrome User Data Directory to use. (Profile) If the directory doesn't exist, it'll be created.""", ) parser.addoption( "--with-testing_base", "--with-testing-base", action="store_true", dest="with_testing_base", default=True, help="""(DEPRECATED) - This option is always enabled now. Use for saving logs & screenshots when tests fail. The following options are now active by default with --with-testing_base (which is always on now): --with-screen_shots , --with-basic_test_info , --with-page_source """, ) parser.addoption( "--log_path", "--log-path", dest="log_path", default=constants.Logs.LATEST + "/", help="""(DEPRECATED) - This value is NOT EDITABLE anymore. Log files are saved to the "latest_logs/" folder.""", ) parser.addoption( "--archive_logs", "--archive-logs", action="store_true", dest="archive_logs", default=False, help="Archive old log files instead of deleting them.", ) parser.addoption( "--archive_downloads", "--archive-downloads", action="store_true", dest="archive_downloads", default=False, help="Archive old downloads instead of deleting them.", ) parser.addoption( "--sjw", "--skip_js_waits", "--skip-js-waits", action="store_true", dest="skip_js_waits", default=False, help="""Skip all calls to wait_for_ready_state_complete() and wait_for_angularjs(), which are part of many SeleniumBase methods for improving reliability.""", ) parser.addoption( "--wfa", "--wait_for_angularjs", "--wait-for-angularjs", action="store_true", dest="wait_for_angularjs", default=False, help="""Add waiting for AngularJS. (The default setting was changed to no longer wait for AngularJS to finish loading as an extra JavaScript call.)""", ) parser.addoption( "--with-db_reporting", "--with-db-reporting", action="store_true", dest="with_db_reporting", default=False, help="Use to record test data in the MySQL database.", ) parser.addoption( "--database_env", "--database-env", action="store", dest="database_env", choices=( constants.Environment.QA, constants.Environment.RC, constants.Environment.STAGING, constants.Environment.DEVELOP, constants.Environment.PRODUCTION, constants.Environment.PERFORMANCE, constants.Environment.REPLICA, constants.Environment.FEDRAMP, constants.Environment.OFFLINE, constants.Environment.ONLINE, constants.Environment.MASTER, constants.Environment.REMOTE, constants.Environment.LEGACY, constants.Environment.LOCAL, constants.Environment.ALPHA, constants.Environment.BETA, constants.Environment.DEMO, constants.Environment.GDPR, constants.Environment.MAIN, constants.Environment.TEST, constants.Environment.GOV, constants.Environment.NEW, constants.Environment.OLD, constants.Environment.UAT, ), default=constants.Environment.TEST, help="The database environment to run the tests in.", ) parser.addoption( "--with-s3_logging", "--with-s3-logging", action="store_true", dest="with_s3_logging", default=False, help="Use to save test log files in Amazon S3.", ) parser.addoption( "--with-screen_shots", "--with-screen-shots", action="store_true", dest="with_screen_shots", default=False, help="""(DEPRECATED) - Screenshots are always saved now. This option saves screenshots during test failures. Screenshots are saved in the "latest_logs/" folder. (Automatically on when using --with-testing_base)""", ) parser.addoption( "--with-basic_test_info", "--with-basic-test-info", action="store_true", dest="with_basic_test_info", default=False, help="""(DEPRECATED) - Info files are always saved now. This option saves basic test info on test failures. These files are saved in the "latest_logs/" folder. (Automatically on when using --with-testing_base)""", ) parser.addoption( "--with-page_source", "--with-page-source", action="store_true", dest="with_page_source", default=False, help="""(DEPRECATED) - Page source is saved by default. This option saves page source files on test failures. (Automatically on when using --with-testing_base)""", ) parser.addoption( "--protocol", action="store", dest="protocol", choices=( constants.Protocol.HTTP, constants.Protocol.HTTPS, ), default=constants.Protocol.HTTP, help="""Designates the Selenium Grid protocol to use. Default: http.""", ) parser.addoption( "--server", action="store", dest="servername", default="localhost", help="""Designates the Selenium Grid server to use. Use "127.0.0.1" to connect to a localhost Grid. If unset or set to "localhost", Grid isn't used. Default: "localhost".""", ) parser.addoption( "--port", action="store", dest="port", default="4444", help="""Designates the Selenium Grid port to use. Default: 4444. (If 443, protocol becomes "https")""", ) parser.addoption( "--proxy", "--proxy-server", "--proxy-string", action="store", dest="proxy_string", default=None, help="""Designates the proxy server:port to use. Format: servername:port. OR username:password@servername:port OR A dict key from proxy_list.PROXY_LIST Default: None.""", ) parser.addoption( "--proxy-bypass-list", "--proxy_bypass_list", action="store", dest="proxy_bypass_list", default=None, help="""Designates the hosts, domains, and/or IP addresses to bypass when using a proxy server with "--proxy". Format: A ";"-separated string. Example usage: pytest --proxy="username:password@servername:port" --proxy-bypass-list="*.foo.com;github.com" pytest --proxy="servername:port" --proxy-bypass-list="127.0.0.1:8080" Default: None.""", ) parser.addoption( "--proxy-pac-url", "--pac-url", action="store", dest="proxy_pac_url", default=None, help="""Designates the proxy PAC URL to use. Format: A URL string OR A username:password@URL string Default: None.""", ) parser.addoption( "--proxy-driver", "--proxy_driver", action="store_true", dest="proxy_driver", default=False, help="""If a driver download is needed for tests, uses proxy settings set via --proxy=PROXY.""", ) parser.addoption( "--multi-proxy", "--multi_proxy", action="store_true", dest="multi_proxy", default=False, help="""If you need to run multi-threaded tests with multiple proxies that require authentication, set this to allow multiple configurations.""", ) parser.addoption( "--agent", "--user-agent", "--user_agent", action="store", dest="user_agent", default=None, help="""Designates the User-Agent for the browser to use. Format: A string. Default: None.""", ) parser.addoption( "--mobile", "--mobile-emulator", "--mobile_emulator", action="store_true", dest="mobile_emulator", default=False, help="""If this option is enabled, the mobile emulator will be used while running tests.""", ) parser.addoption( "--metrics", "--device-metrics", "--device_metrics", action="store", dest="device_metrics", default=None, help="""Designates the three device metrics of the mobile emulator: CSS Width, CSS Height, and Pixel-Ratio. Format: A comma-separated string with the 3 values. Examples: "375,734,5" or "412,732,3" or "390,715,3" Default: None. (Will use default values if None)""", ) parser.addoption( "--chromium_arg", "--chromium-arg", action="store", dest="chromium_arg", default=None, help="""Add a Chromium argument for Chrome/Edge browsers. Format: A comma-separated list of Chromium args. If an arg doesn't start with "--", that will be added to the beginning of the arg automatically. Default: None.""", ) parser.addoption( "--firefox_arg", "--firefox-arg", action="store", dest="firefox_arg", default=None, help="""Add a Firefox argument for Firefox browser runs. Format: A comma-separated list of Firefox args. If an arg doesn't start with "--", that will be added to the beginning of the arg automatically. Default: None.""", ) parser.addoption( "--firefox_pref", "--firefox-pref", action="store", dest="firefox_pref", default=None, help="""Set a Firefox preference:value combination. Format: A comma-separated list of pref:value items. Example usage: --firefox-pref="browser.formfill.enable:True" --firefox-pref="pdfjs.disabled:False" --firefox-pref="abc.def.xyz:42,hello.world:text" Boolean and integer values to the right of the ":" will be automatically converted into proper format. If there's no ":" in the string, then True is used. Default: None.""", ) parser.addoption( "--extension_zip", "--extension-zip", "--crx", action="store", dest="extension_zip", default=None, help="""Designates the Chrome Extension ZIP file to load. Format: A comma-separated list of .zip or .crx files containing the Chrome extensions to load. Default: None.""", ) parser.addoption( "--extension_dir", "--extension-dir", action="store", dest="extension_dir", default=None, help="""Designates the Chrome Extension folder to load. Format: A directory containing the Chrome extension. (Can also be a comma-separated list of directories.) Default: None.""", ) parser.addoption( "--disable_features", "--disable-features", action="store", dest="disable_features", default=None, help="""Disable Chromium features from Chrome/Edge browsers. Format: A comma-separated list of Chromium features. Default: None.""", ) parser.addoption( "--binary_location", "--binary-location", "--bl", action="store", dest="binary_location", default=None, help="""Sets the path of the Chromium browser binary to use. Uses the default location if not os.path.exists(PATH)""", ) parser.addoption( "--driver_version", "--driver-version", action="store", dest="driver_version", default=None, help="""Setting this overrides the default driver version, which is set to match the detected browser version. Major version only. Example: "--driver-version=114" (Only chromedriver and uc_driver are affected.)""", ) parser.addoption( "--pls", "--page_load_strategy", "--page-load-strategy", action="store", dest="page_load_strategy", type=str.lower, choices=( constants.PageLoadStrategy.NORMAL, constants.PageLoadStrategy.EAGER, constants.PageLoadStrategy.NONE, ), default=None, help="""This option sets Chrome's pageLoadStrategy. List of choices: "normal", "eager", "none".""", ) parser.addoption( "--headless", action="store_true", dest="headless", default=False, help="""Using this option activates headless mode, which is required on headless machines UNLESS using a virtual display with Xvfb. Default: False on Mac/Windows. True on Linux.""", ) parser.addoption( "--headless1", action="store_true", dest="headless1", default=False, help="""This option activates the old headless mode, which is faster, but has limitations. (May be phased out by Chrome in the future.)""", ) parser.addoption( "--headless2", action="store_true", dest="headless2", default=False, help="""This option activates the new headless mode, which supports Chromium extensions, and more, but is slower than the standard headless mode.""", ) parser.addoption( "--headed", "--gui", action="store_true", dest="headed", default=False, help="""Using this makes Webdriver run web browsers with a GUI when running tests on Linux machines. (The default setting on Linux is headless.) (The default setting on Mac or Windows is headed.)""", ) parser.addoption( "--xvfb", action="store_true", dest="xvfb", default=False, help="""Using this makes tests run headlessly using Xvfb instead of the browser's built-in headless mode. When using "--xvfb", the "--headless" option will no longer be enabled by default on Linux. Default: False. (Linux-ONLY!)""", ) parser.addoption( "--xvfb-metrics", "--xvfb_metrics", action="store", dest="xvfb_metrics", default=None, help="""Customize the Xvfb metrics (Width,Height) on Linux. Format: A comma-separated string with the 2 values. Examples: "1920,1080" or "1366,768" or "1024,768". Default: None. (None: "1366,768". Min: "1024,768".)""", ) parser.addoption( "--locale_code", "--locale-code", "--locale", action="store", dest="locale_code", default=None, help="""Designates the Locale Code for the web browser. A Locale is a specific version of a spoken Language. The Locale alters visible text on supported websites. See: https://seleniumbase.io/help_docs/locale_codes/ Default: None. (The web browser's default mode.)""", ) parser.addoption( "--interval", action="store", dest="interval", default=None, help="""This globally overrides the default interval, (in seconds), of features that include autoplay functionality, such as tours and presentations. Overrides from methods take priority over this. (Headless Mode skips tours and presentations.)""", ) parser.addoption( "--start_page", "--start-page", "--url", action="store", dest="start_page", default=None, help="""Designates the starting URL for the web browser when each test begins. Default: None.""", ) parser.addoption( "--is_pytest", "--is-pytest", action="store_true", dest="is_pytest", default=True, help="""This is used by the BaseCase class to tell apart pytest runs from nosetest runs. (Automatic)""", ) parser.addoption( "--all-scripts", "--all_scripts", action="store_true", dest="all_scripts", default=False, help="""Use this to run `SB()`, `DriverContext()` and `Driver()` scripts that are discovered during the pytest collection phase.""", ) parser.addoption( "--time_limit", "--time-limit", "--timelimit", action="store", dest="time_limit", default=None, help="""Use this to set a time limit per test, in seconds. If a test runs beyond the limit, it fails.""", ) parser.addoption( "--slow_mode", "--slow-mode", "--slowmo", "--slow", action="store_true", dest="slow_mode", default=False, help="""Using this slows down the automation.""", ) parser.addoption( "--demo_mode", "--demo-mode", "--demo", action="store_true", dest="demo_mode", default=False, help="""Using this slows down the automation and lets you visually see what the tests are actually doing.""", ) parser.addoption( "--demo_sleep", "--demo-sleep", action="store", dest="demo_sleep", default=None, help="""Setting this overrides the Demo Mode sleep time that happens after browser actions.""", ) parser.addoption( "--highlights", action="store", dest="highlights", default=None, help="""Setting this overrides the default number of highlight animation loops to have per call.""", ) parser.addoption( "--message_duration", "--message-duration", action="store", dest="message_duration", default=None, help="""Setting this overrides the default time that messenger notifications remain visible when reaching assert statements during Demo Mode.""", ) parser.addoption( "--check_js", "--check-js", action="store_true", dest="js_checking_on", default=False, help="""The option to check for JavaScript errors after every page load.""", ) parser.addoption( "--adblock", "--ad_block", "--ad-block", "--block_ads", "--block-ads", action="store_true", dest="ad_block_on", default=False, help="""Using this makes WebDriver block display ads that are defined in ad_block_list.AD_BLOCK_LIST.""", ) parser.addoption( "--host_resolver_rules", "--host-resolver-rules", action="store", dest="host_resolver_rules", default=None, help="""Use this option to set "host-resolver-rules". This lets you re-map traffic from any domain. Eg. "MAP www.google-analytics.com 0.0.0.0". Eg. "MAP * ~NOTFOUND , EXCLUDE myproxy". Eg. "MAP * 0.0.0.0 , EXCLUDE 127.0.0.1". Eg. "MAP *.google.com myproxy". Find more examples on these pages: (https://www.electronjs.org/docs/ latest/api/command-line-switches) (https://www.chromium.org/developers/ design-documents/network-stack/socks-proxy/) Use comma-separation for multiple host rules.""", ) parser.addoption( "--block_images", "--block-images", action="store_true", dest="block_images", default=False, help="""Using this makes WebDriver block images from loading on web pages during tests.""", ) parser.addoption( "--do_not_track", "--do-not-track", action="store_true", dest="do_not_track", default=False, help="""Indicate to websites that you don't want to be tracked. The browser will send an extra HTTP header each time it requests a web page. https://support.google.com/chrome/answer/2790761""", ) parser.addoption( "--verify_delay", "--verify-delay", action="store", dest="verify_delay", default=None, help="""Setting this overrides the default wait time before each MasterQA verification pop-up.""", ) parser.addoption( "--esc-end", "--esc_end", "--ee", action="store_true", dest="esc_end", default=False, help="""End the current test early via the ESC key. The test will be marked as skipped.""", ) parser.addoption( "--recorder", "--record", "--rec", "--codegen", action="store_true", dest="recorder_mode", default=False, help="""Using this enables the SeleniumBase Recorder, which records browser actions for converting into SeleniumBase scripts.""", ) parser.addoption( "--rec-behave", "--rec-gherkin", action="store_true", dest="rec_behave", default=False, help="""Not only enables the SeleniumBase Recorder, but also saves recorded actions into the behave-gerkin format, which includes a feature file, an imported steps file, and the environment.py file.""", ) parser.addoption( "--rec-sleep", "--record-sleep", action="store_true", dest="record_sleep", default=False, help="""If Recorder Mode is enabled, records sleep(seconds) calls.""", ) parser.addoption( "--rec-print", action="store_true", dest="rec_print", default=False, help="""If Recorder Mode is enabled, prints output after tests end.""", ) parser.addoption( "--disable_js", "--disable-js", action="store_true", dest="disable_js", default=False, help="""The option to disable JavaScript on web pages. Warning: Most web pages will stop working!""", ) parser.addoption( "--disable_cookies", "--disable-cookies", action="store_true", dest="disable_cookies", default=False, help="""The option to disable Cookies on web pages. Warning: Several pages may stop working!""", ) parser.addoption( "--disable_csp", "--disable-csp", "--no_csp", "--no-csp", "--dcsp", action="store_true", dest="disable_csp", default=False, help="""Using this disables the Content Security Policy of websites, which may interfere with some features of SeleniumBase, such as loading custom JavaScript libraries for various testing actions. Setting this to True (--disable-csp) overrides the value set in seleniumbase/config/settings.py""", ) parser.addoption( "--disable_ws", "--disable-ws", "--dws", "--disable-web-security", action="store_true", dest="disable_ws", default=False, help="""Using this disables the "Web Security" feature of Chrome and Chromium-based browsers such as Edge.""", ) parser.addoption( "--enable_ws", "--enable-ws", "--enable-web-security", action="store_true", dest="enable_ws", default=False, help="""Using this enables the "Web Security" feature of Chrome and Chromium-based browsers such as Edge.""", ) parser.addoption( "--enable_sync", "--enable-sync", action="store_true", dest="enable_sync", default=False, help="""Using this enables the "Chrome Sync" feature.""", ) parser.addoption( "--use_auto_ext", "--use-auto-ext", "--auto-ext", action="store_true", dest="use_auto_ext", default=False, help="""(DEPRECATED) - Enable the automation extension. It's not required, but some commands & advanced features may need it.""", ) parser.addoption( "--undetected", "--undetectable", "--uc", # undetected-chromedriver action="store_true", dest="undetectable", default=False, help="""Using this option makes chromedriver undetectable to websites that use anti-bot services to block automation tools from navigating them freely.""", ) parser.addoption( "--uc_cdp_events", "--uc-cdp-events", "--uc-cdp", # For capturing CDP events during UC Mode action="store_true", dest="uc_cdp_events", default=None, help="""Captures CDP events during Undetectable Mode runs. Then you can add a listener to perform actions on received data, such as printing it to the console: from pprint import pformat self.driver.add_cdp_listener( "*", lambda data: print(pformat(data)) ) self.open(URL)""", ) parser.addoption( "--uc_subprocess", "--uc-subprocess", "--uc-sub", # undetected-chromedriver subprocess mode action="store_true", dest="uc_subprocess", default=None, help="""(DEPRECATED) - (UC Mode always uses this now.) Use undetectable-chromedriver as a subprocess, which can help avoid issues that might result.""", ) parser.addoption( "--no_sandbox", "--no-sandbox", action="store_true", dest="no_sandbox", default=False, help="""(DEPRECATED) - "--no-sandbox" is always used now. Using this enables the "No Sandbox" feature. (This setting is now always enabled by default.)""", ) parser.addoption( "--disable_gpu", "--disable-gpu", action="store_true", dest="disable_gpu", default=False, help="""(DEPRECATED) - GPU is disabled if no swiftshader. Using this enables the "Disable GPU" feature. (GPU is disabled by default if swiftshader off.)""", ) parser.addoption( "--log_cdp", "--log-cdp", "--log_cdp_events", "--log-cdp-events", action="store_true", dest="log_cdp_events", default=None, help="""Capture CDP events. Then you can print them. Eg. print(driver.get_log("performance"))""", ) parser.addoption( "--remote_debug", "--remote-debug", "--remote-debugger", "--remote_debugger", action="store_true", dest="remote_debug", default=False, help="""This syncs the browser to Chromium's remote debugger. To access the remote debugging interface, go to: chrome://inspect/#devices while tests are running. The previous URL was at: http://localhost:9222/ Info: chromedevtools.github.io/devtools-protocol/""", ) parser.addoption( "--final-debug", "--final-trace", "--fdebug", "--ftrace", action="store_true", dest="final_debug", default=False, help="""Enter Debug Mode at the end of each test. To enter Debug Mode only on failures, use "--pdb". If using both "--final-debug" and "--pdb" together, then Debug Mode will activate twice on failures.""", ) parser.addoption( "--dashboard", action="store_true", dest="dashboard", default=False, help="""Using this enables the SeleniumBase Dashboard. To access the SeleniumBase Dashboard interface, open the dashboard.html file located in the same folder that the pytest command was run from.""", ) parser.addoption( "--dash_title", "--dash-title", dest="dash_title", default=None, help="Set the title shown for the generated dashboard.", ) parser.addoption( "--enable_3d_apis", "--enable-3d-apis", action="store_true", dest="enable_3d_apis", default=False, help="""Using this enables WebGL and 3D APIs.""", ) parser.addoption( "--swiftshader", action="store_true", dest="swiftshader", default=False, help="""Using this enables the "--use-gl=swiftshader" feature when running tests on Chrome.""", ) parser.addoption( "--incognito", "--incognito_mode", "--incognito-mode", action="store_true", dest="incognito", default=False, help="""Using this enables Chrome's Incognito mode.""", ) parser.addoption( "--guest", "--guest_mode", "--guest-mode", action="store_true", dest="guest_mode", default=False, help="""Using this enables Chrome's Guest mode.""", ) parser.addoption( "--dark", "--dark_mode", "--dark-mode", action="store_true", dest="dark_mode", default=False, help="""Using this enables Chrome's Dark mode.""", ) parser.addoption( "--devtools", "--open_devtools", "--open-devtools", action="store_true", dest="devtools", default=False, help="""Using this opens Chrome's DevTools.""", ) parser.addoption( "--rs", "--reuse_session", "--reuse-session", action="store_true", dest="reuse_session", default=False, help="""The option to reuse the selenium browser window session for all tests.""", ) parser.addoption( "--rcs", "--reuse_class_session", "--reuse-class-session", action="store_true", dest="reuse_class_session", default=False, help="""The option to reuse the selenium browser window session for all tests within the same class.""", ) parser.addoption( "--crumbs", action="store_true", dest="crumbs", default=False, help="""The option to delete all cookies between tests that reuse the same browser session. This option is only useful if using "--reuse-session"/"--rs" or "--reuse-class-session"/"--rcs" because tests use a new clean browser if not reusing sessions.""", ) parser.addoption( "--disable-beforeunload", "--disable_beforeunload", action="store_true", dest="_disable_beforeunload", default=False, help="""The option to disable the "beforeunload" event on Chromium browsers (Chrome or Edge). This is already the default Firefox option.""", ) parser.addoption( "--window-position", "--window_position", action="store", dest="window_position", default=None, help="""The option to set the starting window x,y position Format: A comma-separated string with the 2 values. Example: "55,66" Default: None. (Will use default values if None)""", ) parser.addoption( "--window-size", "--window_size", action="store", dest="window_size", default=None, help="""The option to set the default window "width,height". Format: A comma-separated string with the 2 values. Example: "1200,800" Default: None. (Will use default values if None)""", ) parser.addoption( "--maximize_window", "--maximize-window", "--maximize", "--fullscreen", action="store_true", dest="maximize_option", default=False, help="""The option to start with a maximized browser window. (Overrides the "window-size" option if used.)""", ) parser.addoption( "--screenshot", "--save_screenshot", "--save-screenshot", "--ss", action="store_true", dest="save_screenshot", default=False, help="""Save a screenshot at the end of every test. By default, this is only done for failures. Will be saved in the "latest_logs/" folder.""", ) parser.addoption( "--no-screenshot", "--no_screenshot", "--ns", action="store_true", dest="no_screenshot", default=False, help="""No screenshots saved unless tests directly ask it. This changes default behavior where screenshots are saved for test failures and pytest-html reports.""", ) parser.addoption( "--visual_baseline", "--visual-baseline", action="store_true", dest="visual_baseline", default=False, help="""Setting this resets the visual baseline for Automated Visual Testing with SeleniumBase. When a test calls self.check_window(), it will rebuild its files in the visual_baseline folder.""", ) parser.addoption( "--wire", action="store_true", dest="use_wire", default=False, help="""Use selenium-wire's webdriver for selenium webdriver.""", ) parser.addoption( "--external_pdf", "--external-pdf", action="store_true", dest="external_pdf", default=False, help="""This option sets the following on Chromium: "plugins.always_open_pdf_externally": True, which causes opened PDF URLs to download immediately, instead of being displayed in the browser window.""", ) parser.addoption( "--timeout_multiplier", "--timeout-multiplier", action="store", dest="timeout_multiplier", default=None, help="""Setting this overrides the default timeout by the multiplier when waiting for page elements. Unused when tests override the default value.""", ) parser.addoption( "--lfp", "--list-fail-page", "--list-fail-pages", action="store_true", dest="fail_page", default=False, help="""(For debugging) After each failing test, list the URL where the failure occurred. Useful when you don't have access to the latest_logs/ folder, such as when running tests in GitHub Actions.""", ) arg_join = " ".join(sys_argv) sb_config._browser_shortcut = None sb_config._cdp_browser = None sb_config._cdp_bin_loc = None sb_config._vd_list = [] # Check if binary-location in options bin_loc_in_options = False for arg in sys_argv: if arg in ["--binary-location", "--binary_location", "--bl"]: bin_loc_in_options = True # SeleniumBase does not support pytest-timeout due to hanging browsers. for arg in sys_argv: if "--timeout=" in arg: raise Exception( "\n Don't use --timeout=s from pytest-timeout! " "\n It's not thread-safe for WebDriver processes! " "\n Use --time-limit=s from SeleniumBase instead!\n" ) # Dashboard Mode does not support tests using forked subprocesses. if "--forked" in sys_argv and "--dashboard" in sys_argv: raise Exception( "\n Dashboard Mode does NOT support forked subprocesses!" '\n (*** DO NOT combine "--forked" with "--dashboard"! ***)\n' ) # Reuse-Session Mode does not support tests using forked subprocesses. if "--forked" in sys_argv and ( "--rs" in sys_argv or "--reuse-session" in sys_argv ): raise Exception( "\n Reuse-Session Mode does NOT support forked subprocesses!" '\n (DO NOT combine "--forked" with "--rs"/"--reuse-session"!)\n' ) # Recorder Mode does not support multi-threaded / multi-process runs. if ( "--recorder" in sys_argv or "--record" in sys_argv or "--rec" in sys_argv ): if ("-n" in sys_argv) or (" -n=" in arg_join) or ("-c" in sys_argv): raise Exception( "\n Recorder Mode does NOT support multi-process mode (-n)!" '\n (DO NOT combine "--recorder" with "-n NUM_PROCESSES"!)\n' ) using_recorder = False if ( "--recorder" in sys_argv or "--record" in sys_argv or "--rec" in sys_argv ): using_recorder = True # As a shortcut, you can use "--edge" instead of "--browser=edge", etc, # but you can only specify one default browser for tests. (Default: chrome) browser_changes = 0 browser_set = None browser_text = None browser_list = [] if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: browser_changes += 1 browser_set = "chrome" browser_list.append("--browser=chrome") if "--browser=edge" in sys_argv or "--browser edge" in sys_argv: browser_changes += 1 browser_set = "edge" browser_list.append("--browser=edge") if "--browser=firefox" in sys_argv or "--browser firefox" in sys_argv: browser_changes += 1 browser_set = "firefox" browser_list.append("--browser=firefox") if "--browser=safari" in sys_argv or "--browser safari" in sys_argv: browser_changes += 1 browser_set = "safari" browser_list.append("--browser=safari") if "--browser=ie" in sys_argv or "--browser ie" in sys_argv: browser_changes += 1 browser_set = "ie" browser_list.append("--browser=ie") if "--browser=remote" in sys_argv or "--browser remote" in sys_argv: browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=opera") if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=brave") if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 browser_text = "chrome" sb_config._browser_shortcut = "chrome" browser_list.append("--chrome") if "--edge" in sys_argv and not browser_set == "edge": browser_changes += 1 browser_text = "edge" sb_config._browser_shortcut = "edge" browser_list.append("--edge") if "--firefox" in sys_argv and not browser_set == "firefox": browser_changes += 1 browser_text = "firefox" sb_config._browser_shortcut = "firefox" browser_list.append("--firefox") if "--ie" in sys_argv and not browser_set == "ie": browser_changes += 1 browser_text = "ie" sb_config._browser_shortcut = "ie" browser_list.append("--ie") if "--safari" in sys_argv and not browser_set == "safari": browser_changes += 1 browser_text = "safari" sb_config._browser_shortcut = "safari" browser_list.append("--safari") if "--opera" in sys_argv and not browser_set == "opera": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--opera") if "--brave" in sys_argv and not browser_set == "brave": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--brave") if "--comet" in sys_argv and not browser_set == "comet": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") if "--atlas" in sys_argv and not browser_set == "atlas": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--atlas") if browser_changes > 1: message = "\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( browser_changes, ", ".join(browser_list), ) message += "\n ONLY ONE default browser is allowed!" message += "\n Select a single browser & try again!\n" raise Exception(message) if ( using_recorder and browser_changes == 1 and browser_text not in [ "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium" ] ): message = ( "\n Recorder Mode ONLY supports Chromium browsers!" '\n (Your browser choice was: "%s")\n' % browser_list[0] ) raise Exception(message) undetectable = False if ( "--undetected" in sys_argv or "--undetectable" in sys_argv or "--uc" in sys_argv or "--uc-cdp-events" in sys_argv or "--uc_cdp_events" in sys_argv or "--uc-cdp" in sys_argv or "--uc-subprocess" in sys_argv or "--uc_subprocess" in sys_argv or "--uc-sub" in sys_argv ): undetectable = True if ( browser_changes == 1 and browser_text not in [ "chrome", "opera", "brave", "comet", "atlas", "chromium" ] and undetectable ): message = ( '\n Undetected-Chromedriver Mode ONLY supports Chrome!' '\n ("--uc" / "--undetected" / "--undetectable")' '\n (Your browser choice was: "%s")\n' % browser_list[0] ) raise Exception(message) if undetectable and "--wire" in sys_argv: raise Exception( "\n SeleniumBase doesn't support mixing --uc with --wire mode!" "\n If you need both, override get_new_driver() from BaseCase:" "\n https://seleniumbase.io/help_docs/syntax_formats/#sb_sf_09\n" ) def pytest_configure(config): """This runs after command-line options have been parsed.""" sb_config.item_count = 0 sb_config.item_count_passed = 0 sb_config.item_count_failed = 0 sb_config.item_count_skipped = 0 sb_config.item_count_untested = 0 sb_config.is_pytest = True sb_config.is_behave = False sb_config.is_nosetest = False sb_config.is_context_manager = False sb_config.pytest_config = config sb_config.browser = config.getoption("browser") if sb_config._browser_shortcut: sb_config.browser = sb_config._browser_shortcut elif sys_argv == ["-c"]: # Multithreading messes with args if config.getoption("use_opera"): bin_loc = detect_b_ver.get_binary_location("opera") if bin_loc and os.path.exists(bin_loc): sb_config.browser = "opera" elif config.getoption("use_brave"): bin_loc = detect_b_ver.get_binary_location("brave") if bin_loc and os.path.exists(bin_loc): sb_config.browser = "brave" elif config.getoption("use_comet"): bin_loc = detect_b_ver.get_binary_location("comet") if bin_loc and os.path.exists(bin_loc): sb_config.browser = "comet" elif config.getoption("use_atlas"): bin_loc = detect_b_ver.get_binary_location("atlas") if bin_loc and os.path.exists(bin_loc): sb_config.browser = "atlas" sb_config.account = config.getoption("account") sb_config.data = config.getoption("data") sb_config.var1 = config.getoption("var1") sb_config.var2 = config.getoption("var2") sb_config.var3 = config.getoption("var3") sb_config.variables = config.getoption("variables") sb_config.environment = config.getoption("environment") sb_config.with_selenium = config.getoption("with_selenium") sb_config.user_agent = config.getoption("user_agent") sb_config.mobile_emulator = config.getoption("mobile_emulator") sb_config.device_metrics = config.getoption("device_metrics") sb_config.headless = config.getoption("headless") sb_config.headless1 = config.getoption("headless1") if sb_config.headless1: sb_config.headless = True sb_config.headless2 = config.getoption("headless2") if sb_config.headless2 and sb_config.browser == "firefox": sb_config.headless2 = False # Only for Chromium browsers sb_config.headless = True # Firefox has regular headless elif ( sb_config.browser not in [ "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium" ] ): sb_config.headless2 = False # Only for Chromium browsers sb_config.headed = config.getoption("headed") sb_config.xvfb = config.getoption("xvfb") sb_config.xvfb_metrics = config.getoption("xvfb_metrics") sb_config.locale_code = config.getoption("locale_code") sb_config.interval = config.getoption("interval") sb_config.start_page = config.getoption("start_page") sb_config.chromium_arg = config.getoption("chromium_arg") sb_config.firefox_arg = config.getoption("firefox_arg") sb_config.firefox_pref = config.getoption("firefox_pref") sb_config.extension_zip = config.getoption("extension_zip") sb_config.extension_dir = config.getoption("extension_dir") sb_config.disable_features = config.getoption("disable_features") sb_config.binary_location = config.getoption("binary_location") if getattr(sb_config, "_cdp_bin_loc", None): sb_config.binary_location = sb_config._cdp_bin_loc elif not sb_config.binary_location: if ( config.getoption("use_opera") or sb_config._browser_shortcut == "opera" ): bin_loc = detect_b_ver.get_binary_location("opera") if bin_loc and os.path.exists(bin_loc): sb_config.binary_location = bin_loc elif ( config.getoption("use_brave") or sb_config._browser_shortcut == "brave" ): bin_loc = detect_b_ver.get_binary_location("brave") if bin_loc and os.path.exists(bin_loc): sb_config.binary_location = bin_loc elif ( config.getoption("use_comet") or sb_config._browser_shortcut == "comet" ): bin_loc = detect_b_ver.get_binary_location("comet") if bin_loc and os.path.exists(bin_loc): sb_config.binary_location = bin_loc elif ( config.getoption("use_atlas") or sb_config._browser_shortcut == "atlas" ): bin_loc = detect_b_ver.get_binary_location("atlas") if bin_loc and os.path.exists(bin_loc): sb_config.binary_location = bin_loc if config.getoption("use_chromium") and not sb_config.binary_location: sb_config.binary_location = "_chromium_" elif config.getoption("use_cft") and not sb_config.binary_location: sb_config.binary_location = "cft" elif config.getoption("use_chs") and not sb_config.binary_location: sb_config.binary_location = "chs" if ( sb_config.binary_location and sb_config.binary_location.lower() == "chs" and sb_config.browser == "chrome" ): sb_config.headless = True sb_config.headless1 = False sb_config.headless2 = False if sb_config.browser in constants.ChromiumSubs.chromium_subs: if not sb_config.binary_location: sb_config.browser = "chrome" # Still uses chromedriver sb_config._browser_shortcut = sb_config.browser sb_config.driver_version = config.getoption("driver_version") sb_config.page_load_strategy = config.getoption("page_load_strategy") sb_config.with_testing_base = config.getoption("with_testing_base") sb_config.with_db_reporting = config.getoption("with_db_reporting") sb_config.with_s3_logging = config.getoption("with_s3_logging") sb_config.with_screen_shots = config.getoption("with_screen_shots") sb_config.with_basic_test_info = config.getoption("with_basic_test_info") sb_config.with_page_source = config.getoption("with_page_source") sb_config.protocol = config.getoption("protocol") sb_config.servername = config.getoption("servername") sb_config.port = config.getoption("port") if sb_config.servername != "localhost": # Using Selenium Grid # (Set --server="127.0.0.1" for localhost Grid) if str(sb_config.port) == "443": sb_config.protocol = "https" sb_config.proxy_string = config.getoption("proxy_string") sb_config.proxy_bypass_list = config.getoption("proxy_bypass_list") sb_config.proxy_pac_url = config.getoption("proxy_pac_url") sb_config.proxy_driver = config.getoption("proxy_driver") sb_config.multi_proxy = config.getoption("multi_proxy") sb_config.cap_file = config.getoption("cap_file") sb_config.cap_string = config.getoption("cap_string") sb_config.settings_file = config.getoption("settings_file") sb_config.user_data_dir = config.getoption("user_data_dir") sb_config.database_env = config.getoption("database_env") sb_config.log_path = constants.Logs.LATEST + "/" sb_config.archive_logs = config.getoption("archive_logs") if config.getoption("archive_downloads"): settings.ARCHIVE_EXISTING_DOWNLOADS = True if config.getoption("skip_js_waits"): settings.SKIP_JS_WAITS = True if config.getoption("wait_for_angularjs"): settings.WAIT_FOR_ANGULARJS = True sb_config.all_scripts = config.getoption("all_scripts") sb_config._time_limit = config.getoption("time_limit") sb_config.time_limit = config.getoption("time_limit") sb_config.slow_mode = config.getoption("slow_mode") sb_config.demo_mode = config.getoption("demo_mode") sb_config.demo_sleep = config.getoption("demo_sleep") sb_config.highlights = config.getoption("highlights") sb_config.message_duration = config.getoption("message_duration") sb_config.js_checking_on = config.getoption("js_checking_on") sb_config.ad_block_on = config.getoption("ad_block_on") sb_config.host_resolver_rules = config.getoption("host_resolver_rules") sb_config.block_images = config.getoption("block_images") sb_config.do_not_track = config.getoption("do_not_track") sb_config.verify_delay = config.getoption("verify_delay") sb_config.esc_end = config.getoption("esc_end") sb_config.recorder_mode = config.getoption("recorder_mode") sb_config.recorder_ext = config.getoption("recorder_mode") # Again sb_config.rec_behave = config.getoption("rec_behave") sb_config.rec_print = config.getoption("rec_print") sb_config.record_sleep = config.getoption("record_sleep") if sb_config.rec_print and not sb_config.recorder_mode: sb_config.recorder_mode = True sb_config.recorder_ext = True elif sb_config.rec_behave and not sb_config.recorder_mode: sb_config.recorder_mode = True sb_config.recorder_ext = True elif sb_config.record_sleep and not sb_config.recorder_mode: sb_config.recorder_mode = True sb_config.recorder_ext = True sb_config.disable_cookies = config.getoption("disable_cookies") sb_config.disable_js = config.getoption("disable_js") sb_config.disable_csp = config.getoption("disable_csp") sb_config.disable_ws = config.getoption("disable_ws") sb_config.enable_ws = config.getoption("enable_ws") if not sb_config.disable_ws: sb_config.enable_ws = True sb_config.enable_sync = config.getoption("enable_sync") sb_config.use_auto_ext = config.getoption("use_auto_ext") sb_config.undetectable = config.getoption("undetectable") sb_config.uc_cdp_events = config.getoption("uc_cdp_events") if sb_config.uc_cdp_events and not sb_config.undetectable: sb_config.undetectable = True sb_config.uc_subprocess = config.getoption("uc_subprocess") if sb_config.uc_subprocess and not sb_config.undetectable: sb_config.undetectable = True sb_config.no_sandbox = config.getoption("no_sandbox") sb_config.disable_gpu = config.getoption("disable_gpu") sb_config.log_cdp_events = config.getoption("log_cdp_events") sb_config.remote_debug = config.getoption("remote_debug") sb_config.final_debug = config.getoption("final_debug") sb_config.dashboard = config.getoption("dashboard") sb_config.dash_title = config.getoption("dash_title") sb_config.enable_3d_apis = config.getoption("enable_3d_apis") sb_config.swiftshader = config.getoption("swiftshader") sb_config.incognito = config.getoption("incognito") sb_config.guest_mode = config.getoption("guest_mode") sb_config.dark_mode = config.getoption("dark_mode") sb_config.devtools = config.getoption("devtools") sb_config.reuse_session = config.getoption("reuse_session") sb_config.reuse_class_session = config.getoption("reuse_class_session") if sb_config.reuse_class_session: sb_config.reuse_session = True sb_config.shared_driver = None # The default driver for session reuse sb_config.crumbs = config.getoption("crumbs") sb_config._disable_beforeunload = config.getoption("_disable_beforeunload") sb_config.window_position = config.getoption("window_position") sb_config.window_size = config.getoption("window_size") sb_config.maximize_option = config.getoption("maximize_option") sb_config.save_screenshot = config.getoption("save_screenshot") sb_config.no_screenshot = config.getoption("no_screenshot") sb_config.visual_baseline = config.getoption("visual_baseline") sb_config.use_wire = config.getoption("use_wire") sb_config.external_pdf = config.getoption("external_pdf") sb_config.timeout_multiplier = config.getoption("timeout_multiplier") sb_config.list_fp = config.getoption("fail_page") sb_config._is_timeout_changed = False sb_config._has_logs = False sb_config._fail_page = None sb_config._SMALL_TIMEOUT = settings.SMALL_TIMEOUT sb_config._LARGE_TIMEOUT = settings.LARGE_TIMEOUT sb_config.pytest_html_report = config.getoption("htmlpath") # --html=FILE sb_config._sb_class = None # (Used with the sb fixture for "--rcs") sb_config._sb_node = {} # sb node dictionary (Used with the sb fixture) # Dashboard-specific variables sb_config._results = {} # SBase Dashboard test results sb_config._duration = {} # SBase Dashboard test duration sb_config._display_id = {} # SBase Dashboard display ID sb_config._d_t_log_path = {} # SBase Dashboard test log path sb_config._dash_html = None # SBase Dashboard HTML copy sb_config._test_id = None # SBase Dashboard test id sb_config._latest_display_id = None # The latest SBase display id sb_config._dashboard_initialized = False # Becomes True after init sb_config._has_exception = False # This becomes True if any test fails sb_config._multithreaded = False # This becomes True if multithreading sb_config._only_unittest = True # If any test uses BaseCase, becomes False sb_config._sbase_detected = False # Becomes True during SeleniumBase tests sb_config._extra_dash_entries = [] # Dashboard entries for non-SBase tests sb_config._using_html_report = False # Becomes True when using html report sb_config._dash_is_html_report = False # Dashboard becomes the html report sb_config._saved_dashboard_pie = None # Copy of pie chart for html report sb_config._dash_final_summary = None # Dash status to add to html report sb_config._html_report_name = None # The name of the pytest html report sb_config._html_report_copy = None # The copy of the pytest html report arg_join = " ".join(sys_argv) if ( "-n" in sys_argv or " -n=" in arg_join or " -n" in arg_join or "-c" in sys_argv or "-n=" in config.getini("addopts") or "-n " in config.getini("addopts") or "-n" in config.getini("addopts") ): sb_config._multithreaded = True if ( "--html" in sys_argv or " --html=" in arg_join or "--html=" in config.getini("addopts") or "--html " in config.getini("addopts") ): sb_config._using_html_report = True sb_config._html_report_name = config.getoption("htmlpath") if sb_config.dashboard: if sb_config._html_report_name == "dashboard.html": sb_config._dash_is_html_report = True sb_config._html_report_copy = "last_report.html" # Recorder Mode does not support multi-threaded / multi-process runs. if sb_config.recorder_mode and sb_config._multithreaded: # At this point, the user likely put a "-n NUM" in the pytest.ini file. # Since raising an exception in pytest_configure raises INTERNALERROR, # print a message here instead and cancel Recorder Mode. print( "\n Recorder Mode does NOT support multi-process mode (-n)!" '\n (DO NOT combine "--recorder" with "-n NUM_PROCESSES"!)' "\n (The Recorder WILL BE DISABLED during this run!)\n" ) sb_config.recorder_mode = False sb_config.recorder_ext = False if sb_config.xvfb and "linux" not in sys.platform: # The Xvfb virtual display server is for Linux OS Only! sb_config.xvfb = False if ( "linux" in sys.platform and not sb_config.headed and not sb_config.headless and not sb_config.headless2 and not sb_config.xvfb ): if not sb_config.undetectable: print( "(Linux uses --headless by default. " "To override, use --headed / --gui. " "For Xvfb mode instead, use --xvfb. " "Or you can hide this info by using " "--headless / --headless2 / --uc.)" ) sb_config.headless = True else: sb_config.xvfb = True # Recorder Mode can still optimize scripts in --headless2 mode. if sb_config.recorder_mode and sb_config.headless: sb_config.headless = False sb_config.headless1 = False sb_config.headless2 = True if not sb_config.headless and not sb_config.headless2: sb_config.headed = True if config.getoption("use_chrome"): sb_config.browser = "chrome" elif config.getoption("use_edge"): sb_config.browser = "edge" elif config.getoption("use_firefox"): sb_config.browser = "firefox" elif config.getoption("use_ie"): sb_config.browser = "ie" elif config.getoption("use_safari"): sb_config.browser = "safari" else: pass # Use the browser specified by "--browser=BROWSER" if sb_config.browser == "safari" and sb_config.headless: sb_config.headless = False # Safari doesn't support headless mode sb_config.headless1 = False if sb_config.dash_title: constants.Dashboard.TITLE = sb_config.dash_title.replace("_", " ") if sb_config.save_screenshot and sb_config.no_screenshot: sb_config.save_screenshot = False # "no_screenshot" has priority if ( "-v" in sys_argv and not sb_config._multithreaded or ( hasattr(config, "invocation_params") and "-v" in config.invocation_params.args and ( "-n=1" in config.invocation_params.args or "-n1" in config.invocation_params.args or "-n 1" in " ".join(config.invocation_params.args) ) ) ): sb_config.list_fp = True # List the fail pages in console output if ( sb_config._multithreaded and "--co" not in sys_argv and "--collect-only" not in sys_argv ): from seleniumbase.core import download_helper from seleniumbase.core import proxy_helper log_helper.log_folder_setup( constants.Logs.LATEST + "/", sb_config.archive_logs ) download_helper.reset_downloads_folder() proxy_helper.remove_proxy_zip_if_present() def pytest_sessionstart(session): pass def _get_test_ids_(the_item): test_id = the_item.nodeid if not test_id: test_id = "unidentified_TestCase" display_id = test_id r""" # Now using the nodeid for both the test_id and display_id. # (This only impacts tests using The Dashboard.) # If there are any issues, we'll revert back to the old code. test_id = the_item.nodeid.split("/")[-1].replace(" ", "_") if "[" in test_id: test_id_intro = test_id.split("[")[0] parameter = test_id.split("[")[1] parameter = re.sub(re.compile(r"\W"), "", parameter) test_id = test_id_intro + "__" + parameter display_id = test_id test_id = test_id.replace("/", ".").replace("\\", ".") test_id = test_id.replace("::", ".").replace(".py", "") """ return test_id, display_id def _create_dashboard_assets_(): from seleniumbase.js_code.live_js import live_js from seleniumbase.core.style_sheet import get_pytest_style abs_path = os.path.abspath(".") assets_folder = os.path.join(abs_path, "assets") if not os.path.exists(assets_folder): with suppress(Exception): os.makedirs(assets_folder, exist_ok=True) pytest_style_css = os.path.join(assets_folder, "pytest_style.css") add_pytest_style_css = True if os.path.exists(pytest_style_css): existing_pytest_style = None with open(pytest_style_css, mode="r") as f: existing_pytest_style = f.read() if existing_pytest_style == get_pytest_style(): add_pytest_style_css = False if add_pytest_style_css: out_file = open(pytest_style_css, mode="w+", encoding="utf-8") out_file.writelines(get_pytest_style()) out_file.close() live_js_file = os.path.join(assets_folder, "live.js") add_live_js_file = True if os.path.exists(live_js_file): existing_live_js = None with open(live_js_file, mode="r") as f: existing_live_js = f.read() if existing_live_js == live_js: add_live_js_file = False if add_live_js_file: out_file = open(live_js_file, mode="w+", encoding="utf-8") out_file.writelines(live_js) out_file.close() def pytest_itemcollected(item): if "--co" in sys_argv or "--collect-only" in sys_argv: return sb_config.item_count += 1 if sb_config.dashboard: test_id, display_id = _get_test_ids_(item) sb_config._results[test_id] = "Untested" sb_config._duration[test_id] = "-" sb_config._display_id[test_id] = display_id sb_config._d_t_log_path[test_id] = None def pytest_deselected(items): if "--co" in sys_argv or "--collect-only" in sys_argv: return if sb_config.dashboard: sb_config.item_count -= len(items) for item in items: test_id, display_id = _get_test_ids_(item) if test_id in sb_config._results.keys(): sb_config._results.pop(test_id) def pytest_collection_finish(session): """This runs after item collection is finalized. https://docs.pytest.org/en/stable/reference.html """ sb_config._context_of_runner = False # Context Manager Compatibility if "--co" in sys_argv or "--collect-only" in sys_argv: return if len(session.items) > 0 and not sb_config._multithreaded: from seleniumbase.core import download_helper from seleniumbase.core import proxy_helper log_helper.log_folder_setup( constants.Logs.LATEST + "/", sb_config.archive_logs ) download_helper.reset_downloads_folder() proxy_helper.remove_proxy_zip_if_present() if sb_config.dashboard and len(session.items) > 0: _create_dashboard_assets_() # Print the Dashboard path if at least one test runs. sb_config.item_count_untested = sb_config.item_count dash_path = os.path.join(os.getcwd(), "dashboard.html") dash_url = "file://" + dash_path.replace("\\", "/") star_len = len("Dashboard: ") + len(dash_url) with suppress(Exception): terminal_size = os.get_terminal_size().columns if terminal_size > 30 and star_len > terminal_size: star_len = terminal_size stars = "*" * star_len c1 = "" cr = "" if "linux" not in sys.platform: c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX cr = colorama.Style.RESET_ALL if sb_config._multithreaded: if ( hasattr(session.config, "workerinput") and session.config.workerinput["workerid"] == "gw0" ): sys.stderr.write( "\nDashboard: %s%s%s\n%s\n" % (c1, dash_url, cr, stars) ) else: print("Dashboard: %s%s%s\n%s" % (c1, dash_url, cr, stars)) def pytest_runtest_setup(item): """This runs before every test with pytest.""" if "--co" in sys_argv or "--collect-only" in sys_argv: return if sb_config.dashboard: sb_config._sbase_detected = False sb_config._pdb_failure = False sb_config._fail_page = None test_id, display_id = _get_test_ids_(item) sb_config._test_id = test_id sb_config._latest_display_id = display_id def pytest_runtest_teardown(item): """This runs after every test with pytest. Make sure that webdriver and headless displays have exited. (Has zero effect on tests using --reuse-session / --rs)""" if "--co" in sys_argv or "--collect-only" in sys_argv: return with suppress(Exception): if hasattr(item, "_testcase") or hasattr(sb_config, "_sb_pdb_driver"): if hasattr(item, "_testcase"): self = item._testcase with suppress(Exception): if ( getattr(self, "driver", None) and "--pdb" not in sys_argv ): if not (is_windows or self.driver.service.process): self.driver.quit() elif getattr(sb_config, "_sb_pdb_driver", None): with suppress(Exception): if ( not is_windows or sb_config._sb_pdb_driver.service.process ): sb_config._sb_pdb_driver.quit() sb_config._sb_pdb_driver = None with suppress(Exception): if ( getattr(self, "_xvfb_display", None) and hasattr(self._xvfb_display, "stop") and not getattr(sb_config, "reuse_session", None) ): self.headless_active = False sb_config.headless_active = False self._xvfb_display.stop() self._xvfb_display = None if ( getattr(sb_config, "_virtual_display", None) and hasattr(sb_config._virtual_display, "stop") and not getattr(sb_config, "reuse_session", None) ): sb_config._virtual_display.stop() sb_config._virtual_display = None if ( ( sb_config._has_exception or (python3_11_or_newer and py311_patch2) or "--pdb" in sys_argv ) and sb_config.list_fp and sb_config._fail_page ): if ( "-s" in sys_argv or "--capture=no" in sys_argv or "--capture=tee-sys" in sys_argv or ( hasattr(sb_config.pytest_config, "invocation_params") and ( "-s" in sb_config.pytest_config.invocation_params.args or "--capture=no" in ( sb_config.pytest_config.invocation_params.args ) or "--capture=tee-sys" in ( sb_config.pytest_config.invocation_params.args ) ) ) ): print("\n=> Fail Page: %s" % sb_config._fail_page) else: sys.stdout.write("\n=> Fail Page: %s\n" % sb_config._fail_page) def pytest_html_duration_format(duration): return "%.2f" % duration def pytest_sessionfinish(session): pass def pytest_terminal_summary(terminalreporter): if "--co" in sys_argv or "--collect-only" in sys_argv: return if ( not hasattr(terminalreporter, "stats") or not hasattr(terminalreporter.stats, "keys") ): return if len(terminalreporter.stats.keys()) == 0: return if not sb_config._multithreaded and not sb_config._sbase_detected: return latest_logs_dir = os.path.join(os.getcwd(), constants.Logs.LATEST) + os.sep if ( "failed" in terminalreporter.stats.keys() and os.path.exists(latest_logs_dir) and os.listdir(latest_logs_dir) ): sb_config._has_exception = True if sb_config._multithreaded: if os.path.exists(latest_logs_dir) and os.listdir(latest_logs_dir): sb_config._has_exception = True if sb_config.dashboard: abs_path = os.path.abspath(".") dash_lock = constants.Dashboard.LOCKFILE dash_lock_path = os.path.join(abs_path, dash_lock) if os.path.exists(dash_lock_path): sb_config._only_unittest = False if sb_config._has_exception and ( sb_config.dashboard and not sb_config._only_unittest ): # Print link a second time because the first one may be off-screen dashboard_file = os.path.join(os.getcwd(), "dashboard.html") dashboard_url = "file://" + dashboard_file.replace("\\", "/") terminalreporter.write_sep("-", "Dashboard = %s" % dashboard_url) if ( sb_config._has_exception or sb_config.save_screenshot or sb_config._has_logs ): # Log files are generated during test failures and Screenshot Mode terminalreporter.write_sep( "-", "Latest Logs dir: %s" % latest_logs_dir ) def _perform_pytest_unconfigure_(config): from seleniumbase.core import proxy_helper reporter = config.pluginmanager.get_plugin("terminalreporter") start_time = None if hasattr(reporter, "_sessionstarttime"): start_time = reporter._sessionstarttime # (pytest < 8.4.0) else: start_time = reporter._session_start.time # (pytest >= 8.4.0) duration = time.time() - start_time if not getattr(sb_config, "multi_proxy", None): proxy_helper.remove_proxy_zip_if_present() if getattr(sb_config, "reuse_session", None): # Close the shared browser session if sb_config.shared_driver: try: if ( not is_windows or sb_config.browser == "ie" or sb_config.shared_driver.service.process ): sb_config.shared_driver.quit() except AttributeError: pass except Exception: pass sb_config.shared_driver = None with suppress(Exception): if ( getattr(sb_config, "_virtual_display", None) and hasattr(sb_config._virtual_display, "stop") ): sb_config._virtual_display.stop() sb_config._virtual_display = None sb_config.headless_active = False if getattr(sb_config, "_vd_list", None): if isinstance(sb_config._vd_list, list): for display in sb_config._vd_list: if display: with suppress(Exception): display.stop() if hasattr(sb_config, "log_path") and sb_config.item_count > 0: log_helper.archive_logs_if_set( constants.Logs.LATEST + "/", sb_config.archive_logs ) if os.path.exists("./assets/"): # Used by pytest-html reports with suppress(Exception): shared_utils.make_dir_files_writable("./assets/") log_helper.clear_empty_logs() # Dashboard post-processing: Disable time-based refresh and stamp complete if not getattr(sb_config, "dashboard", None): html_report_path = None the_html_r = None abs_path = os.path.abspath(".") if sb_config._html_report_name: html_report_path = os.path.join( abs_path, sb_config._html_report_name ) if sb_config._html_report_copy: html_report_path_copy = os.path.join( abs_path, sb_config._html_report_copy ) if ( sb_config._using_html_report and html_report_path and os.path.exists(html_report_path) ): with open(html_report_path, mode="r", encoding="utf-8") as f: the_html_r = f.read() assets_chunk = "if (assets.length === 1) {" remove_media = "container.classList.remove('media-container')" rm_n_left = '
      <
      ' rm_n_right = '
      >
      ' the_html_r = the_html_r.replace( assets_chunk, "%s %s" % (assets_chunk, remove_media), ) the_html_r = the_html_r.replace(rm_n_left, "") the_html_r = the_html_r.replace(rm_n_right, "") the_html_r = the_html_r.replace("
        $", "$") the_html_r = the_html_r.replace("}
          ", "}") the_html_r = the_html_r.replace( "
        • ${val}
        • ", "${val},  " ) the_html_r = the_html_r.replace( "
          ${value}
          ", "${value}' 'SeleniumBase' ) the_html_r = the_html_r.replace( ph_link, "%s and %s" % (sb_link, ph_link) ) the_html_r = the_html_r.replace( "findAll('.collapsible", "//findAll('.collapsible" ) the_html_r = the_html_r.replace( "mediaName.innerText", "//mediaName.innerText" ) the_html_r = the_html_r.replace( "counter.innerText", "//counter.innerText" ) run_count = '

          ' run_c_loc = the_html_r.find(run_count) rc_loc = the_html_r.find(" took ", run_c_loc) end_rc_loc = the_html_r.find(".

          ", rc_loc) run_time = "%.2f" % duration new_time = " ran in %s seconds" % run_time the_html_r = ( the_html_r[:rc_loc] + new_time + the_html_r[end_rc_loc:] ) with open(html_report_path, mode="w", encoding="utf-8") as f: f.write(the_html_r) # Finalize the HTML report with suppress(Exception): shared_utils.make_writable(html_report_path) with open(html_report_path_copy, mode="w", encoding="utf-8") as f: f.write(the_html_r) # Finalize the HTML report copy with suppress(Exception): shared_utils.make_writable(html_report_path_copy) assets_style = "./assets/style.css" if os.path.exists(assets_style): html_style = None with open(assets_style, mode="r", encoding="utf-8") as f: html_style = f.read() if html_style: html_style = html_style.replace("top: -50px;", "top: 2px;") html_style = html_style.replace("+ 50px)", "+ 40px)") html_style = html_style.replace("ht: 240px;", "ht: 228px;") html_style = html_style.replace( "- 80px);", "- 80px);\n margin-bottom: -42px;" ) html_style = html_style.replace(".collapsible", ".oldc") html_style = html_style.replace(" (hide details)", "") html_style = html_style.replace(" (show details)", "") with open(assets_style, mode="w", encoding="utf-8") as f: f.write(html_style) with suppress(Exception): shared_utils.make_writable(assets_style) # Done with "pytest_unconfigure" unless using the Dashboard return stamp = "" if sb_config._dash_is_html_report: # (If the Dashboard URL is the same as the HTML Report URL:) # Have the html report refresh back to a dashboard on update stamp += ( '\n" % constants.Dashboard.LIVE_JS ) stamp += "\n" find_it = constants.Dashboard.META_REFRESH_HTML swap_with = "" # Stop refreshing the page after the run is done find_it_2 = "Awaiting results... (Refresh the page for updates)" swap_with_2 = ( "Test Run ENDED: Some results UNREPORTED due to skipped tearDown()" ) find_it_3 = 'Untested' swap_with_3 = 'Unreported' # These use caching to prevent extra method calls DASH_PIE_PNG_1 = constants.Dashboard.get_dash_pie_1() DASH_PIE_PNG_2 = constants.Dashboard.get_dash_pie_2() DASH_PIE_PNG_3 = constants.Dashboard.get_dash_pie_3() find_it_4 = 'href="%s"' % DASH_PIE_PNG_1 swap_with_4 = 'href="%s"' % DASH_PIE_PNG_2 try: abs_path = os.path.abspath(".") dashboard_path = os.path.join(abs_path, "dashboard.html") # Part 1: Finalizing the dashboard / integrating html report if os.path.exists(dashboard_path): the_html_d = None with open(dashboard_path, mode="r", encoding="utf-8") as f: the_html_d = f.read() if sb_config._multithreaded and "-c" in sys_argv: # Threads have "-c" in sys.argv, except for the last raise Exception('Break out of "try" block.') if sb_config._multithreaded: dash_pie_loc = constants.Dashboard.DASH_PIE pie_path = os.path.join(abs_path, dash_pie_loc) if os.path.exists(pie_path): import json with open(pie_path, mode="r") as f: dash_pie = f.read().strip() sb_config._saved_dashboard_pie = json.loads(dash_pie) # If the test run doesn't complete by itself, stop refresh the_html_d = the_html_d.replace(find_it, swap_with) the_html_d = the_html_d.replace(find_it_2, swap_with_2) the_html_d = the_html_d.replace(find_it_3, swap_with_3) the_html_d = the_html_d.replace(find_it_4, swap_with_4) the_html_d += stamp if sb_config._dash_is_html_report and ( sb_config._saved_dashboard_pie ): the_html_d = the_html_d.replace( "

          dashboard.html

          ", sb_config._saved_dashboard_pie, ) the_html_d = the_html_d.replace( "", '' % DASH_PIE_PNG_3, ) the_html_d = the_html_d.replace("", '') the_html_d = the_html_d.replace( "", '' '', ) if sb_config._dash_final_summary: the_html_d += sb_config._dash_final_summary time.sleep(0.1) # Add time for "livejs" to detect changes with open(dashboard_path, mode="w", encoding="utf-8") as f: f.write(the_html_d) # Finalize the dashboard time.sleep(0.1) # Add time for "livejs" to detect changes the_html_d = the_html_d.replace( "", "" ) with open(dashboard_path, mode="w", encoding="utf-8") as f: f.write(the_html_d) # Finalize the dashboard with suppress(Exception): shared_utils.make_writable(dashboard_path) assets_style = "./assets/style.css" if os.path.exists(assets_style): html_style = None with open(assets_style, mode="r", encoding="utf-8") as f: html_style = f.read() if html_style: html_style = html_style.replace("top: -50px;", "top: 2px;") html_style = html_style.replace("+ 50px)", "+ 40px)") html_style = html_style.replace("ht: 240px;", "ht: 228px;") html_style = html_style.replace( "- 80px);", "- 80px);\n margin-bottom: -42px;" ) html_style = html_style.replace(".collapsible", ".oldc") html_style = html_style.replace(" (hide details)", "") html_style = html_style.replace(" (show details)", "") with open(assets_style, mode="w", encoding="utf-8") as f: f.write(html_style) with suppress(Exception): shared_utils.make_writable(assets_style) # Part 2: Appending a pytest html report with dashboard data html_report_path = None if sb_config._html_report_name: html_report_path = os.path.join( abs_path, sb_config._html_report_name ) if sb_config._html_report_copy: html_report_path_copy = os.path.join( abs_path, sb_config._html_report_copy ) if ( sb_config._using_html_report and html_report_path and os.path.exists(html_report_path) and not sb_config._dash_is_html_report ): # Add the dashboard pie to the pytest html report the_html_r = None with open(html_report_path, mode="r", encoding="utf-8") as f: the_html_r = f.read() if sb_config._saved_dashboard_pie: h_r_name = sb_config._html_report_name if "/" in h_r_name and h_r_name.endswith(".html"): h_r_name = h_r_name.split("/")[-1] elif "\\" in h_r_name and h_r_name.endswith(".html"): h_r_name = h_r_name.split("\\")[-1] the_html_r = the_html_r.replace( '

          %s

          ' % h_r_name, sb_config._saved_dashboard_pie, ) the_html_r = the_html_r.replace( "", '' % DASH_PIE_PNG_3, ) if sb_config._dash_final_summary: the_html_r += sb_config._dash_final_summary assets_chunk = "if (assets.length === 1) {" remove_media = "container.classList.remove('media-container')" rm_n_left = '
          <
          ' rm_n_right = '
          >
          ' the_html_r = the_html_r.replace( assets_chunk, "%s %s" % (assets_chunk, remove_media), ) the_html_r = the_html_r.replace(rm_n_left, "") the_html_r = the_html_r.replace(rm_n_right, "") the_html_r = the_html_r.replace("
            $", "$") the_html_r = the_html_r.replace("}
              ", "}") the_html_r = the_html_r.replace( "
            • ${val}
            • ", "${val},  " ) the_html_r = the_html_r.replace( "
              ${value}
              ", "${value}' 'SeleniumBase' ) the_html_r = the_html_r.replace( ph_link, "%s and %s" % (sb_link, ph_link) ) the_html_r = the_html_r.replace( "findAll('.collapsible", "//findAll('.collapsible" ) the_html_r = the_html_r.replace( "mediaName.innerText", "//mediaName.innerText" ) the_html_r = the_html_r.replace( "counter.innerText", "//counter.innerText" ) run_count = '

              ' run_c_loc = the_html_r.find(run_count) rc_loc = the_html_r.find(" took ", run_c_loc) end_rc_loc = the_html_r.find(".

              ", rc_loc) run_time = "%.2f" % duration new_time = " ran in %s seconds" % run_time the_html_r = ( the_html_r[:rc_loc] + new_time + the_html_r[end_rc_loc:] ) with open(html_report_path, mode="w", encoding="utf-8") as f: f.write(the_html_r) # Finalize the HTML report with suppress(Exception): shared_utils.make_writable(html_report_path) with open( html_report_path_copy, mode="w", encoding="utf-8" ) as f: f.write(the_html_r) # Finalize the HTML report copy with suppress(Exception): shared_utils.make_writable(html_report_path_copy) except KeyboardInterrupt: pass except Exception: pass def pytest_unconfigure(config): """This runs after all tests have completed with pytest.""" if "--co" in sys_argv or "--collect-only" in sys_argv: return reporter = config.pluginmanager.get_plugin("terminalreporter") if ( not hasattr(reporter, "_sessionstarttime") and ( not hasattr(reporter, "_session_start") or ( hasattr(reporter, "_session_start") and not hasattr(reporter._session_start, "time") ) ) ): return if getattr(sb_config, "_multithreaded", None): import fasteners dash_lock = fasteners.InterProcessLock(constants.Dashboard.LOCKFILE) if getattr(sb_config, "dashboard", None): # Multi-threaded tests with the Dashboard abs_path = os.path.abspath(".") dash_lock_file = constants.Dashboard.LOCKFILE dash_lock_path = os.path.join(abs_path, dash_lock_file) if os.path.exists(dash_lock_path): sb_config._only_unittest = False dashboard_path = os.path.join(abs_path, "dashboard.html") with dash_lock: if ( sb_config._dash_html and config.getoption("htmlpath") == "dashboard.html" ): # Dash is HTML Report (Multithreaded) sb_config._dash_is_html_report = True with open( dashboard_path, mode="w", encoding="utf-8" ) as f: f.write(sb_config._dash_html) # Dashboard Multithreaded _perform_pytest_unconfigure_(config) return else: # Dash Lock is missing _perform_pytest_unconfigure_(config) return with dash_lock: # Multi-threaded tests _perform_pytest_unconfigure_(config) return else: # Single-threaded tests _perform_pytest_unconfigure_(config) return @pytest.fixture() def sb(request): """SeleniumBase as a pytest fixture. Usage example: "def test_one(sb):" You may need to use this for tests that use other pytest fixtures.""" from seleniumbase import BaseCase from seleniumbase.core import session_helper class BaseClass(BaseCase): def setUp(self): super().setUp() def tearDown(self): self.save_teardown_screenshot() super().tearDown() def base_method(self): pass if request.cls: if sb_config.reuse_class_session: the_class = str(request.cls).split(".")[-1].split("'")[0] if the_class != sb_config._sb_class: session_helper.end_reused_class_session_as_needed() sb_config._sb_class = the_class request.cls.sb = BaseClass("base_method") request.cls.sb.setUp() request.cls.sb._needs_tearDown = True request.cls.sb._using_sb_fixture = True request.cls.sb._using_sb_fixture_class = True sb_config._sb_node[request.node.nodeid] = request.cls.sb yield request.cls.sb if request.cls.sb._needs_tearDown: request.cls.sb.tearDown() request.cls.sb._needs_tearDown = False else: sb = BaseClass("base_method") sb.setUp() sb._needs_tearDown = True sb._using_sb_fixture = True sb._using_sb_fixture_no_class = True sb_config._sb_node[request.node.nodeid] = sb yield sb if sb._needs_tearDown: sb.tearDown() sb._needs_tearDown = False @pytest.mark.hookwrapper def pytest_runtest_makereport(item, call): if "--co" in sys_argv or "--collect-only" in sys_argv: return pytest_html = item.config.pluginmanager.getplugin("html") outcome = yield report = outcome.get_result() if sb_config._multithreaded: sb_config._using_html_report = True # For Dashboard use if ( pytest_html and report.when == "call" and hasattr(sb_config, "dashboard") ): if sb_config.dashboard and not sb_config._sbase_detected: test_id, display_id = _get_test_ids_(item) r_outcome = report.outcome if len(r_outcome) > 1: r_outcome = r_outcome[0].upper() + r_outcome[1:] sb_config._results[test_id] = r_outcome sb_config._duration[test_id] = "*****" sb_config._display_id[test_id] = display_id sb_config._d_t_log_path[test_id] = "" if test_id not in sb_config._extra_dash_entries: sb_config._extra_dash_entries.append(test_id) elif ( sb_config._sbase_detected and ((python3_11_or_newer and py311_patch2) or "--pdb" in sys_argv) and (report.outcome == "failed" or "AssertionError" in str(call)) and not sb_config._has_exception ): # Handle a bug on Python 3.11 where exceptions aren't seen log_path = "" sb_config._has_logs = True if hasattr(sb_config, "_test_logpath"): log_path = sb_config._test_logpath if sb_config.dashboard: sb_config._process_dashboard_entry(True) if hasattr(sb_config, "_add_pytest_html_extra"): sb_config._add_pytest_html_extra() if hasattr(sb_config, "_visual_baseline_copies"): sb_config._process_v_baseline_logs() sb_config._excinfo_value = call.excinfo.value sb_config._excinfo_tb = call.excinfo.tb if "pytest_plugin.BaseClass.base_method" not in log_path: source = None if hasattr(sb_config, "_last_page_source"): source = sb_config._last_page_source if log_path and source: log_helper.log_page_source(log_path, None, source) last_page_screenshot_png = None if hasattr(sb_config, "_last_page_screenshot_png"): last_page_screenshot_png = ( sb_config._last_page_screenshot_png ) if log_path and last_page_screenshot_png: log_helper.log_screenshot( log_path, None, last_page_screenshot_png ) if log_path: sb_config._log_fail_data() with suppress(Exception): extra_report = None if hasattr(item, "_testcase"): extra_report = item._testcase._html_report_extra elif hasattr(item.instance, "sb") or ( item.nodeid in sb_config._sb_node ): if not hasattr(item.instance, "sb"): sb_node = sb_config._sb_node[item.nodeid] else: sb_node = item.instance.sb test_id = item.nodeid if not test_id: test_id = "unidentified_TestCase" r""" # Now using the nodeid for both the test_id and display_id. # (This only impacts tests using The Dashboard.) # If there are any issues, we'll revert back to the old code. test_id = test_id.split("/")[-1].replace(" ", "_") if "[" in test_id: test_id_intro = test_id.split("[")[0] parameter = test_id.split("[")[1] parameter = re.sub(re.compile(r"\W"), "", parameter) test_id = test_id_intro + "__" + parameter test_id = test_id.replace("/", ".").replace("\\", ".") test_id = test_id.replace("::", ".").replace(".py", "") """ sb_node._sb_test_identifier = test_id if sb_node._needs_tearDown: sb_node.tearDown() sb_node._needs_tearDown = False extra_report = sb_node._html_report_extra else: return extra = getattr(report, "extra", []) if len(extra_report) > 1 and extra_report[1]["content"]: report.extras = extra + extra_report if sb_config._dash_is_html_report: # If the Dashboard URL is the same as the HTML Report URL, # have the html report refresh back to a dashboard on update. refresh_updates = ( '" % constants.Dashboard.LIVE_JS ) report.extras.append(pytest_html.extras.html(refresh_updates)) ================================================ FILE: seleniumbase/plugins/s3_logging_plugin.py ================================================ """S3 Logging Plugin for SeleniumBase tests that run with pynose / nosetests""" import uuid import os from nose.plugins import Plugin class S3Logging(Plugin): """The plugin for uploading test logs to the S3 bucket specified.""" name = "s3_logging" # Usage: --with-s3-logging def configure(self, options, conf): """Get the options.""" super().configure(options, conf) self.options = options self.test_id = None def save_data_to_logs(self, data, file_name): from seleniumbase.fixtures import page_utils test_logpath = os.path.join(self.options.log_path, self.test_id) file_name = str(file_name) destination_folder = test_logpath page_utils._save_data_as(data, destination_folder, file_name) def afterTest(self, test): """Upload logs to the S3 bucket after tests complete.""" from seleniumbase.core.s3_manager import S3LoggingBucket self.test_id = test.test.id() s3_bucket = S3LoggingBucket() guid = str(uuid.uuid4().hex) path = os.path.join(self.options.log_path, self.test_id) uploaded_files = [] for logfile in os.listdir(path): logfile_name = "%s/%s/%s" % ( guid, self.test_id, logfile.split(path)[-1], ) s3_bucket.upload_file(logfile_name, os.path.join(path, logfile)) uploaded_files.append(logfile_name) s3_bucket.save_uploaded_file_names(uploaded_files) index_file = s3_bucket.upload_index_file( test.id(), guid, path, self.save_data_to_logs ) print("\n*** Log files uploaded: ***\n%s\n" % index_file) # If the SB database plugin is also being used, # attach a link to the logs index database row. if hasattr(test.test, "testcase_guid"): from seleniumbase.core.testcase_manager import ( TestcaseDataPayload, TestcaseManager, ) self.testcase_manager = TestcaseManager(self.options.database_env) data_payload = TestcaseDataPayload() data_payload.guid = test.test.testcase_guid data_payload.log_url = index_file self.testcase_manager.update_testcase_log_url(data_payload) ================================================ FILE: seleniumbase/plugins/sb_manager.py ================================================ """ SeleniumBase as a Python Context Manager. ######################################### The SeleniumBase SB Context Manager: Usage --> ``with SB() as sb:`` Example --> ```python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://google.com/ncr" sb.activate_cdp_mode(url) sb.type('[name="q"]', "SeleniumBase GitHub page") sb.click('[value="Google Search"]') sb.sleep(2) print(sb.get_page_title()) ``` # (The browser exits automatically after the "with" block ends.) ######################################### """ from contextlib import contextmanager, suppress from typing import Any, Generator from seleniumbase import BaseCase @contextmanager # Usage: -> ``with SB() as sb:`` def SB( test=None, # Test Mode: Output, Logging, Continue on failure unless "rtf". rtf=None, # Shortcut / Duplicate of "raise_test_failure". raise_test_failure=None, # If "test" mode, raise Exception on 1st failure. browser=None, # Choose from "chrome", "edge", "firefox", or "safari". headless=None, # Use the default headless mode for Chromium and Firefox. headless1=None, # Use Chromium's old headless mode. (Fast, but limited) headless2=None, # Use Chromium's new headless mode. (Has more features) locale_code=None, # Set the Language Locale Code for the web browser. protocol=None, # The Selenium Grid protocol: "http" or "https". servername=None, # The Selenium Grid server/IP used for tests. port=None, # The Selenium Grid port used by the test server. proxy=None, # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT". proxy_bypass_list=None, # Skip proxy when using the listed domains. proxy_pac_url=None, # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL) multi_proxy=None, # Allow multiple proxies with auth when multi-threaded. agent=None, # Modify the web browser's User-Agent string. cap_file=None, # The desired capabilities to use with a Selenium Grid. cap_string=None, # The desired capabilities to use with a Selenium Grid. recorder_ext=None, # Enables the SeleniumBase Recorder Chromium extension. disable_cookies=None, # Disable Cookies on websites. (Pages might break!) disable_js=None, # Disable JavaScript on websites. (Pages might break!) disable_csp=None, # Disable the Content Security Policy of websites. enable_ws=None, # Enable Web Security on Chromium-based browsers. enable_sync=None, # Enable "Chrome Sync" on websites. use_auto_ext=None, # Use Chrome's automation extension. undetectable=None, # Use undetected-chromedriver to evade bot-detection. uc_cdp_events=None, # Capture CDP events in undetected-chromedriver mode. uc_subprocess=None, # Use undetected-chromedriver as a subprocess. log_cdp_events=None, # Capture {"performance": "ALL", "browser": "ALL"} incognito=None, # Enable Chromium's Incognito mode. guest_mode=None, # Enable Chromium's Guest mode. dark_mode=None, # Enable Chromium's Dark mode. devtools=None, # Open Chromium's DevTools when the browser opens. remote_debug=None, # Enable Chrome's Debugger on "http://localhost:9222". enable_3d_apis=None, # Enable WebGL and 3D APIs. swiftshader=None, # Chrome: --use-gl=angle / --use-angle=swiftshader-webgl ad_block_on=None, # Block some types of display ads from loading. host_resolver_rules=None, # Set host-resolver-rules, comma-separated. block_images=None, # Block images from loading during tests. do_not_track=None, # Tell websites that you don't want to be tracked. chromium_arg=None, # "ARG=N,ARG2" (Set Chromium args, ","-separated.) firefox_arg=None, # "ARG=N,ARG2" (Set Firefox args, comma-separated.) firefox_pref=None, # SET (Set Firefox PREFERENCE:VALUE set, ","-separated) user_data_dir=None, # Set the Chrome user data directory to use. extension_zip=None, # Load a Chrome Extension .zip|.crx, comma-separated. extension_dir=None, # Load a Chrome Extension directory, comma-separated. disable_features=None, # "F1,F2" (Disable Chrome features, ","-separated.) binary_location=None, # Set path of the Chromium browser binary to use. driver_version=None, # Set the chromedriver or uc_driver version to use. skip_js_waits=None, # Skip JS Waits (readyState=="complete" and Angular). wait_for_angularjs=None, # Wait for AngularJS to load after some actions. use_wire=None, # Use selenium-wire's webdriver over selenium webdriver. external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True. window_position=None, # Set the browser's starting window position: "X,Y" window_size=None, # Set the browser's starting window size: "Width,Height" is_mobile=None, # Use the mobile device emulator while running tests. mobile=None, # Shortcut / Duplicate of "is_mobile". device_metrics=None, # Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio" xvfb=None, # Run tests using the Xvfb virtual display server on Linux OS. xvfb_metrics=None, # Set Xvfb display size on Linux: "Width,Height". start_page=None, # The starting URL for the web browser when tests begin. rec_print=None, # If Recorder is enabled, prints output after tests end. rec_behave=None, # Like Recorder Mode, but also generates behave-gherkin. record_sleep=None, # If Recorder enabled, also records self.sleep calls. data=None, # Extra test data. Access with "self.data" in tests. var1=None, # Extra test data. Access with "self.var1" in tests. var2=None, # Extra test data. Access with "self.var2" in tests. var3=None, # Extra test data. Access with "self.var3" in tests. variables=None, # DICT (Extra test data. Access with "self.variables") account=None, # Set account. Access with "self.account" in tests. environment=None, # Set the test env. Access with "self.env" in tests. headed=None, # Run tests in headed/GUI mode on Linux, where not default. maximize=None, # Start tests with the browser window maximized. disable_ws=None, # Reverse of "enable_ws". (None and False are different) disable_beforeunload=None, # Disable the "beforeunload" event on Chromium. settings_file=None, # A file for overriding default SeleniumBase settings. position=None, # Shortcut / Duplicate of "window_position". size=None, # Shortcut / Duplicate of "window_size". uc=None, # Shortcut / Duplicate of "undetectable". undetected=None, # Shortcut / Duplicate of "undetectable". uc_cdp=None, # Shortcut / Duplicate of "uc_cdp_events". uc_sub=None, # Shortcut / Duplicate of "uc_subprocess". locale=None, # Shortcut / Duplicate of "locale_code". log_cdp=None, # Shortcut / Duplicate of "log_cdp_events". ad_block=None, # Shortcut / Duplicate of "ad_block_on". server=None, # Shortcut / Duplicate of "servername". guest=None, # Shortcut / Duplicate of "guest_mode". wire=None, # Shortcut / Duplicate of "use_wire". pls=None, # Shortcut / Duplicate of "page_load_strategy". sjw=None, # Shortcut / Duplicate of "skip_js_waits". wfa=None, # Shortcut / Duplicate of "wait_for_angularjs". cft=None, # Use "Chrome for Testing" chs=None, # Use "Chrome-Headless-Shell" use_chromium=None, # Use base "Chromium" save_screenshot=None, # Save a screenshot at the end of each test. no_screenshot=None, # No screenshots saved unless tests directly ask it. page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none". timeout_multiplier=None, # Multiplies the default timeout values. js_checking_on=None, # Check for JavaScript errors after page loads. slow=None, # Slow down the automation. Faster than using Demo Mode. demo=None, # Slow down and visually see test actions as they occur. demo_sleep=None, # SECONDS (Set wait time after Slow & Demo Mode actions.) message_duration=None, # SECONDS (The time length for Messenger alerts.) highlights=None, # Number of highlight animations for Demo Mode actions. interval=None, # SECONDS (Autoplay interval for SB Slides & Tour steps.) time_limit=None, # SECONDS (Safely fail tests that exceed the time limit.) ) -> Generator[BaseCase, Any, None]: """ * SeleniumBase as a Python Context Manager * Example: -------- .. code-block:: python from seleniumbase import SB with SB(uc=True, test=True) as sb: url = "https://google.com/ncr" sb.activate_cdp_mode(url) sb.type('[name="q"]', "SeleniumBase GitHub page") sb.click('[value="Google Search"]') sb.sleep(2) print(sb.get_page_title()) Optional Parameters: -------------------- test (bool): Test Mode: Output, Logging, Continue on failure unless "rtf". rtf (bool): Shortcut / Duplicate of "raise_test_failure". raise_test_failure (bool): If "test" mode, raise Exception on 1st failure. browser (str): Choose from "chrome", "edge", "firefox", or "safari". headless (bool): Use the default headless mode for Chromium and Firefox. headless1 (bool): Use Chromium's old headless mode. (Fast, but limited) headless2 (bool): Use Chromium's new headless mode. (Has more features) locale_code (str): Set the Language Locale Code for the web browser. protocol (str): The Selenium Grid protocol: "http" or "https". servername (str): The Selenium Grid server/IP used for tests. port (int): The Selenium Grid port used by the test server. proxy (str): Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT". proxy_bypass_list (str): Skip proxy when using the listed domains. proxy_pac_url (str): Use PAC file. (Format: URL or USERNAME:PASSWORD@URL) multi_proxy (bool): # Allow multiple proxies with auth when multithreaded. agent (str): Modify the web browser's User-Agent string. cap_file (str): The desired capabilities to use with a Selenium Grid. cap_string (str): The desired capabilities to use with a Selenium Grid. recorder_ext (bool): Enables the SeleniumBase Recorder Chromium extension. disable_cookies (bool): Disable Cookies on websites. (Pages might break!) disable_js (bool): Disable JavaScript on websites. (Pages might break!) disable_csp (bool): Disable the Content Security Policy of websites. enable_ws (bool): Enable Web Security on Chromium-based browsers. enable_sync (bool): Enable "Chrome Sync" on websites. use_auto_ext (bool): Use Chrome's automation extension. undetectable (bool): Use undetected-chromedriver to evade bot-detection. uc_cdp_events (bool): Capture CDP events in undetected-chromedriver mode. uc_subprocess (bool): Use undetected-chromedriver as a subprocess. log_cdp_events (bool): Capture {"performance": "ALL", "browser": "ALL"} incognito (bool): Enable Chromium's Incognito mode. guest_mode (bool): Enable Chromium's Guest mode. dark_mode (bool): Enable Chromium's Dark mode. devtools (bool): Open Chromium's DevTools when the browser opens. remote_debug (bool): Enable Chrome's Debugger on "http://localhost:9222". enable_3d_apis (bool): Enable WebGL and 3D APIs. swiftshader (bool): Chrome: --use-gl=angle / --use-angle=swiftshader-webgl ad_block_on (bool): Block some types of display ads from loading. host_resolver_rules (str): Set host-resolver-rules, comma-separated. block_images (bool): Block images from loading during tests. do_not_track (bool): Tell websites that you don't want to be tracked. chromium_arg (str): "ARG=N,ARG2" (Set Chromium args, ","-separated.) firefox_arg (str): "ARG=N,ARG2" (Set Firefox args, comma-separated.) firefox_pref (str): SET (Set Firefox PREFERENCE:VALUE set, ","-separated) user_data_dir (str): Set the Chrome user data directory to use. extension_zip (str): Load a Chrome Extension .zip|.crx, comma-separated. extension_dir (str): Load a Chrome Extension directory, comma-separated. disable_features (str): "F1,F2" (Disable Chrome features, ","-separated.) binary_location (str): Set path of the Chromium browser binary to use. driver_version (str): Set the chromedriver or uc_driver version to use. skip_js_waits (bool): Skip JS Waits (readyState=="complete" and Angular). wait_for_angularjs (bool): Wait for AngularJS to load after some actions. use_wire (bool): Use selenium-wire's webdriver over selenium webdriver. external_pdf (bool): Set Chrome "plugins.always_open_pdf_externally":True. window_position (x,y): Set the browser's starting window position: "X,Y" window_size (w,h): Set the browser's starting window size: "Width,Height" is_mobile (bool): Use the mobile device emulator while running tests. mobile (bool): Shortcut / Duplicate of "is_mobile". device_metrics (w,h,pr): Mobile metrics: "CSSWidth,CSSHeight,PixelRatio" xvfb (bool): Run tests using the Xvfb virtual display server on Linux OS. xvfb_metrics (w,h): Set Xvfb display size on Linux: "Width,Height". start_page (str): The starting URL for the web browser when tests begin. rec_print (bool): If Recorder is enabled, prints output after tests end. rec_behave (bool): Like Recorder Mode, but also generates behave-gherkin. record_sleep (bool): If Recorder enabled, also records self.sleep calls. data (str): Extra test data. Access with "self.data" in tests. var1 (str): Extra test data. Access with "self.var1" in tests. var2 (str): Extra test data. Access with "self.var2" in tests. var3 (str): Extra test data. Access with "self.var3" in tests. variables (dict): Extra test data. Access with "self.variables". account (str): Set account. Access with "self.account" in tests. environment (str): Set the test env. Access with "self.env" in tests. headed (bool): Run tests in headed/GUI mode on Linux, where not default. maximize (bool): Start tests with the browser window maximized. disable_ws (bool): Reverse of "enable_ws". (None and False are different) disable_beforeunload (bool): Disable the "beforeunload" event on Chromium. settings_file (str): A file for overriding default SeleniumBase settings. position (x,y): Shortcut / Duplicate of "window_position". size (w,h): Shortcut / Duplicate of "window_size". uc (bool): Shortcut / Duplicate of "undetectable". undetected (bool): Shortcut / Duplicate of "undetectable". uc_cdp (bool): Shortcut / Duplicate of "uc_cdp_events". uc_sub (bool): Shortcut / Duplicate of "uc_subprocess". locale (str): Shortcut / Duplicate of "locale_code". log_cdp (bool): Shortcut / Duplicate of "log_cdp_events". ad_block (bool): Shortcut / Duplicate of "ad_block_on". server (str): Shortcut / Duplicate of "servername". guest (bool): Shortcut / Duplicate of "guest_mode". wire (bool): Shortcut / Duplicate of "use_wire". pls (str): Shortcut / Duplicate of "page_load_strategy". sjw (bool): Shortcut / Duplicate of "skip_js_waits". wfa (bool): Shortcut / Duplicate of "wait_for_angularjs". save_screenshot (bool): Save a screenshot at the end of each test. no_screenshot (bool): No screenshots saved unless tests directly ask it. page_load_strategy (str): Set Chrome PLS to "normal", "eager", or "none". timeout_multiplier (float): Multiplies the default timeout values. js_checking_on (bool): Check for JavaScript errors after page loads. slow (bool): Slow down the automation. Faster than using Demo Mode. demo (bool): Slow down and visually see test actions as they occur. demo_sleep (float): SECONDS (Set wait time after Slow & Demo Mode actions) message_duration (float): SECONDS (The time length for Messenger alerts.) highlights (int): Number of highlight animations for Demo Mode actions. interval (float): SECONDS (Autoplay interval for SB Slides & Tour steps.) time_limit (float): SECONDS (Safely fail tests that exceed the time limit) """ import colorama import gc import os import sys import time import traceback from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import detect_b_ver from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils sb_config_backup = sb_config sb_config._do_sb_post_mortem = False sys_argv = sys.argv arg_join = " ".join(sys_argv) archive_logs = False existing_runner = False collect_only = ("--co" in sys_argv or "--collect-only" in sys_argv) all_scripts = (hasattr(sb_config, "all_scripts") and sb_config.all_scripts) do_log_folder_setup = False # The first "test=True" run does it inner_test = False if ( (hasattr(sb_config, "is_behave") and sb_config.is_behave) or (hasattr(sb_config, "is_pytest") and sb_config.is_pytest) or (hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest) ): existing_runner = True if test: inner_test = True test = False # Already using a test runner. Skip extra test steps. elif test is None and "--test" in sys_argv: test = True if existing_runner and not hasattr(sb_config, "_context_of_runner"): if hasattr(sb_config, "is_pytest") and sb_config.is_pytest: import pytest msg = "Skipping `SB()` script. (Use `python`, not `pytest`)" if not collect_only and not all_scripts: print("\n *** %s" % msg) if collect_only or not all_scripts: pytest.skip(allow_module_level=True) elif hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest: raise Exception( "\n SB Manager script was triggered by nosetest collection!" '\n (Prevent that by using: ``if __name__ == "__main__":``)' ) elif existing_runner: sb_config._context_of_runner = True if ( not existing_runner and not hasattr(sb_config, "_has_older_context") and test ): # This is the first "test" from context manager scripts run. sb_config._has_older_context = True do_log_folder_setup = True else: if test: pass # Not the first "test" of context manager scripts. else: pass # Not in "test" mode. (No special output/logging.) with_testing_base = False if test: with_testing_base = True if ( raise_test_failure or rtf or "--raise-test-failure" in sys_argv or "--raise_test_failure" in sys_argv or "--rtf" in sys_argv or "-x" in sys_argv # Carry-over from "pytest" or "--exitfirst" in sys_argv # Carry-over from "pytest" ): raise_test_failure = True # Exit on first error or failed test. else: raise_test_failure = False sb_config._browser_shortcut = None sb_config._cdp_browser = None sb_config._cdp_bin_loc = None browser_changes = 0 browser_set = None browser_text = None browser_list = [] # Check if binary-location in options bin_loc_in_options = False if ( binary_location and len(str(binary_location)) > 5 and os.path.exists(str(binary_location)) ): bin_loc_in_options = True else: for arg in sys_argv: if arg in ["--binary-location", "--binary_location", "--bl"]: bin_loc_in_options = True if ( browser and browser in constants.ChromiumSubs.chromium_subs and not bin_loc_in_options ): bin_loc = detect_b_ver.get_binary_location(browser) if bin_loc and os.path.exists(bin_loc): if browser in bin_loc.lower().split("/")[-1].split("\\")[-1]: sb_config._cdp_browser = browser sb_config._cdp_bin_loc = bin_loc binary_location = bin_loc bin_loc_in_options = True # As a shortcut, you can use "--edge" instead of "--browser=edge", etc, # but you can only specify one default browser for tests. (Default: chrome) if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: browser_changes += 1 browser_set = "chrome" browser_list.append("--browser=chrome") if "--browser=edge" in sys_argv or "--browser edge" in sys_argv: browser_changes += 1 browser_set = "edge" browser_list.append("--browser=edge") if "--browser=firefox" in sys_argv or "--browser firefox" in sys_argv: browser_changes += 1 browser_set = "firefox" browser_list.append("--browser=firefox") if "--browser=safari" in sys_argv or "--browser safari" in sys_argv: browser_changes += 1 browser_set = "safari" browser_list.append("--browser=safari") if "--browser=ie" in sys_argv or "--browser ie" in sys_argv: browser_changes += 1 browser_set = "ie" browser_list.append("--browser=ie") if "--browser=remote" in sys_argv or "--browser remote" in sys_argv: browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=opera") if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=brave") if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 browser_text = "chrome" sb_config._browser_shortcut = "chrome" browser_list.append("--chrome") if "--edge" in sys_argv and not browser_set == "edge": browser_changes += 1 browser_text = "edge" sb_config._browser_shortcut = "edge" browser_list.append("--edge") if "--firefox" in sys_argv and not browser_set == "firefox": browser_changes += 1 browser_text = "firefox" sb_config._browser_shortcut = "firefox" browser_list.append("--firefox") if "--ie" in sys_argv and not browser_set == "ie": browser_changes += 1 browser_text = "ie" sb_config._browser_shortcut = "ie" browser_list.append("--ie") if "--safari" in sys_argv and not browser_set == "safari": browser_changes += 1 browser_text = "safari" sb_config._browser_shortcut = "safari" browser_list.append("--safari") if "--opera" in sys_argv and not browser_set == "opera": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--opera") if "--brave" in sys_argv and not browser_set == "brave": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--brave") if "--comet" in sys_argv and not browser_set == "comet": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") if "--atlas" in sys_argv and not browser_set == "atlas": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( browser_changes, ", ".join(browser_list), ) message += "\n ONLY ONE default browser is allowed!" message += "\n Select a single browser & try again!\n" if not browser: raise Exception(message) if browser is None: if browser_text: browser = browser_text else: browser = "chrome" else: browser = browser.lower() valid_browsers = constants.ValidBrowsers.valid_browsers if browser not in valid_browsers: raise Exception( "Browser: {%s} is not a valid browser option. " "Valid options = {%s}" % (browser, valid_browsers) ) if headless is None: if "--headless" in sys_argv: headless = True else: headless = False if headless1 is None: if "--headless1" in sys_argv: headless1 = True else: headless1 = False if headless1: headless = True if headless2 is None: if "--headless2" in sys_argv: headless2 = True else: headless2 = False if protocol is None: protocol = "http" # For the Selenium Grid only! if server is not None and servername is None: servername = server if servername is None: servername = "localhost" # For the Selenium Grid only! if port is None: port = "4444" # For the Selenium Grid only! if not environment: environment = "test" if incognito is None: if "--incognito" in sys_argv: incognito = True else: incognito = False if guest is not None and guest_mode is None: guest_mode = guest if guest_mode is None: if "--guest" in sys_argv: guest_mode = True else: guest_mode = False if dark_mode is None: if "--dark" in sys_argv: dark_mode = True else: dark_mode = False if devtools is None: if "--devtools" in sys_argv: devtools = True else: devtools = False if mobile is not None and is_mobile is None: is_mobile = mobile if is_mobile is None: if "--mobile" in sys_argv: is_mobile = True else: is_mobile = False if is_mobile: sb_config.mobile_emulator = True proxy_string = proxy if proxy_string is None and "--proxy" in arg_join: if "--proxy=" in arg_join: proxy_string = arg_join.split("--proxy=")[1].split(" ")[0] elif "--proxy " in arg_join: proxy_string = arg_join.split("--proxy ")[1].split(" ")[0] if proxy_string: if proxy_string.startswith('"') and proxy_string.endswith('"'): proxy_string = proxy_string[1:-1] elif proxy_string.startswith("'") and proxy_string.endswith("'"): proxy_string = proxy_string[1:-1] c_a = chromium_arg if c_a is None and "--chromium-arg" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--chromium-arg="): c_a = arg.split("--chromium-arg=")[1] break elif arg == "--chromium-arg" and len(sys_argv) > count + 1: c_a = sys_argv[count + 1] if c_a.startswith("-"): c_a = None break count += 1 chromium_arg = c_a d_f = disable_features if d_f is None and "--disable-features" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--disable-features="): d_f = arg.split("--disable-features=")[1] break elif arg == "--disable-features" and len(sys_argv) > count + 1: d_f = sys_argv[count + 1] if d_f.startswith("-"): d_f = None break count += 1 disable_features = d_f if window_position is None and position is not None: window_position = position w_p = window_position if ( w_p is None and ("--window-position" in arg_join or "--position" in arg_join) ): count = 0 for arg in sys_argv: if arg.startswith("--window-position="): w_p = arg.split("--window-position=")[1] break elif arg == "--window-position" and len(sys_argv) > count + 1: w_p = sys_argv[count + 1] if w_p.startswith("-"): w_p = None break count += 1 window_position = w_p if window_size is None and size is not None: window_size = size w_s = window_size if w_s is None and "--window-size" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--window-size="): w_s = arg.split("--window-size=")[1] break elif arg == "--window-size" and len(sys_argv) > count + 1: w_s = sys_argv[count + 1] if w_s.startswith("-"): w_s = None break count += 1 window_size = w_s x_m = xvfb_metrics if x_m is None and "--xvfb-metrics" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--xvfb-metrics="): x_m = arg.split("--xvfb-metrics=")[1] break elif arg == "--xvfb-metrics" and len(sys_argv) > count + 1: x_m = sys_argv[count + 1] if x_m.startswith("-"): x_m = None break count += 1 xvfb_metrics = x_m if agent is None and "--agent" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--agent="): agent = arg.split("--agent=")[1] break elif arg == "--agent" and len(sys_argv) > count + 1: agent = sys_argv[count + 1] if agent.startswith("-"): agent = None break count += 1 user_agent = agent found_bl = None if binary_location is None and "--binary-location" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--binary-location="): found_bl = arg.split("--binary-location=")[1] break elif arg == "--binary-location" and len(sys_argv) > count + 1: found_bl = sys_argv[count + 1] if found_bl.startswith("-"): found_bl = None break count += 1 if found_bl: binary_location = found_bl if binary_location is None and "--bl=" in arg_join: for arg in sys_argv: if arg.startswith("--bl="): binary_location = arg.split("--bl=")[1] break if use_chromium and not binary_location: binary_location = "_chromium_" elif cft and not binary_location: binary_location = "cft" elif chs and not binary_location: binary_location = "chs" if "--use-chromium" in sys_argv and not binary_location: binary_location = "_chromium_" elif "--cft" in sys_argv and not binary_location: binary_location = "cft" elif "--chs" in sys_argv and not binary_location: binary_location = "chs" if ( binary_location and binary_location.lower() == "chs" and browser == "chrome" ): headless = True headless1 = False headless2 = False recorder_mode = False if recorder_ext: recorder_mode = True if ( "--recorder" in sys_argv or "--record" in sys_argv or "--rec" in sys_argv ): recorder_mode = True recorder_ext = True if rec_print is None: if "--rec-print" in sys_argv: rec_print = True else: rec_print = False if rec_behave is None: if "--rec-behave" in sys_argv: rec_behave = True else: rec_behave = False if record_sleep is None: if "--rec-sleep" in sys_argv or "--record-sleep" in sys_argv: record_sleep = True else: record_sleep = False if xvfb is None: if "--xvfb" in sys_argv: xvfb = True else: xvfb = False if not shared_utils.is_linux(): # The Xvfb virtual display server is for Linux OS Only! xvfb = False if ( undetectable or undetected or uc or uc_cdp_events or uc_cdp or uc_subprocess or uc_sub ): undetectable = True if undetectable or undetected or uc: uc_subprocess = True # Use UC as a subprocess by default. elif ( "--undetectable" in sys_argv or "--undetected" in sys_argv or "--uc" in sys_argv or "--uc-cdp-events" in sys_argv or "--uc_cdp_events" in sys_argv or "--uc-cdp" in sys_argv or "--uc-subprocess" in sys_argv or "--uc_subprocess" in sys_argv or "--uc-sub" in sys_argv ): undetectable = True if uc_subprocess is None and uc_sub is None: uc_subprocess = True # Use UC as a subprocess by default. else: undetectable = False if uc_subprocess or uc_sub: uc_subprocess = True elif ( "--uc-subprocess" in sys_argv or "--uc_subprocess" in sys_argv or "--uc-sub" in sys_argv ): uc_subprocess = True else: uc_subprocess = False if uc_cdp_events or uc_cdp: undetectable = True uc_cdp_events = True elif ( "--uc-cdp-events" in sys_argv or "--uc_cdp_events" in sys_argv or "--uc-cdp" in sys_argv or "--uc_cdp" in sys_argv ): undetectable = True uc_cdp_events = True else: uc_cdp_events = False if ( undetectable and browser not in ["chrome", "opera", "brave", "comet", "atlas"] ): message = ( '\n Undetected-Chromedriver Mode ONLY supports Chromium browsers!' '\n ("uc=True" / "undetectable=True" / "--uc")' '\n (Your browser choice was: "%s".)' '\n (Will use "%s" without UC Mode.)\n' % (browser, browser) ) print(message) if headed is None: # Override the default headless mode on Linux if set. if "--gui" in sys_argv or "--headed" in sys_argv: headed = True else: headed = False if ( shared_utils.is_linux() and not headed and not headless and not headless2 and not xvfb ): if not undetectable: headless = True else: xvfb = True if headless2 and browser == "firefox": headless2 = False # Only for Chromium browsers headless = True # Firefox has regular headless elif browser not in ["chrome", "edge", "opera", "brave", "comet", "atlas"]: headless2 = False # Only for Chromium browsers if not headless and not headless2: headed = True if rec_print and not recorder_mode: recorder_mode = True recorder_ext = True elif rec_behave and not recorder_mode: recorder_mode = True recorder_ext = True elif record_sleep and not recorder_mode: recorder_mode = True recorder_ext = True if recorder_mode and headless: headless = False headless1 = False headless2 = True sb_config.proxy_driver = False if "--proxy-driver" in sys_argv or "--proxy_driver" in sys_argv: sb_config.proxy_driver = True if variables and isinstance(variables, str) and len(variables) > 0: import ast bad_input = False if ( not variables.startswith("{") or not variables.endswith("}") ): bad_input = True else: try: variables = ast.literal_eval(variables) if not isinstance(variables, dict): bad_input = True except Exception: bad_input = True if bad_input: raise Exception( '\nExpecting a Python dictionary for "variables"!' "\nEg. --variables=\"{'KEY1':'VALUE', 'KEY2':123}\"" ) elif not isinstance(variables, dict): variables = {} if disable_csp is None: if ( "--disable-csp" in sys_argv or "--no-csp" in sys_argv or "--dcsp" in sys_argv ): disable_csp = True else: disable_csp = False if ( (enable_ws is None and disable_ws is None) and ( "--disable-web-security" in sys_argv or "--disable-ws" in sys_argv or "--dws" in sys_argv ) ): enable_ws = False disable_ws = True elif ( (enable_ws is None and disable_ws is None) or (disable_ws is not None and not disable_ws) or (enable_ws is not None and enable_ws) ): enable_ws = True disable_ws = False else: enable_ws = False disable_ws = True if log_cdp_events is None and log_cdp is None: if ( "--log-cdp-events" in sys_argv or "--log_cdp_events" in sys_argv or "--log-cdp" in sys_argv or "--log_cdp" in sys_argv ): log_cdp_events = True else: log_cdp_events = False elif log_cdp_events or log_cdp: log_cdp_events = True else: log_cdp_events = False if use_auto_ext is None: if "--use-auto-ext" in sys_argv: use_auto_ext = True else: use_auto_ext = False if disable_cookies is None: if "--disable-cookies" in sys_argv: disable_cookies = True else: disable_cookies = False if disable_js is None: if "--disable-js" in sys_argv: disable_js = True else: disable_js = False maximize_option = False if maximize or "--maximize" in sys_argv: maximize_option = True _disable_beforeunload = False if disable_beforeunload: _disable_beforeunload = True if pls is not None and page_load_strategy is None: page_load_strategy = pls if not page_load_strategy and "--pls=" in arg_join: if "--pls=none" in sys_argv or '--pls="none"' in sys_argv: page_load_strategy = "none" elif "--pls=eager" in sys_argv or '--pls="eager"' in sys_argv: page_load_strategy = "eager" elif "--pls=normal" in sys_argv or '--pls="normal"' in sys_argv: page_load_strategy = "normal" if page_load_strategy is not None: if page_load_strategy.lower() not in ["normal", "eager", "none"]: raise Exception( 'page_load_strategy must be "normal", "eager", or "none"!' ) page_load_strategy = page_load_strategy.lower() elif "--pls=normal" in sys_argv or '--pls="normal"' in sys_argv: page_load_strategy = "normal" elif "--pls=eager" in sys_argv or '--pls="eager"' in sys_argv: page_load_strategy = "eager" elif "--pls=none" in sys_argv or '--pls="none"' in sys_argv: page_load_strategy = "none" if sjw is not None and skip_js_waits is None: skip_js_waits = sjw if skip_js_waits is None: if ( "--sjw" in sys_argv or "--skip_js_waits" in sys_argv or "--skip-js-waits" in sys_argv ): settings.SKIP_JS_WAITS = True elif skip_js_waits: settings.SKIP_JS_WAITS = skip_js_waits if wfa is not None and wait_for_angularjs is None: wait_for_angularjs = wfa if wait_for_angularjs is None: if ( "--wfa" in sys_argv or "--wait_for_angularjs" in sys_argv or "--wait-for-angularjs" in sys_argv ): settings.WAIT_FOR_ANGULARJS = True elif wait_for_angularjs: settings.WAIT_FOR_ANGULARJS = wait_for_angularjs if save_screenshot is None: if ( "--screenshot" in sys_argv or "--save-screenshot" in sys_argv or "--ss" in sys_argv ): save_screenshot = True else: save_screenshot = False if no_screenshot is None: if "--no-screenshot" in sys_argv or "--ns" in sys_argv: no_screenshot = True else: no_screenshot = False if save_screenshot and no_screenshot: save_screenshot = False # "no_screenshot" has priority if browser == "safari" and headless: headless = False # Safari doesn't support headless mode headless1 = False if js_checking_on is None: if "--check-js" in sys_argv: js_checking_on = True else: js_checking_on = False slow_mode = False if slow: slow_mode = True elif "--slow" in sys_argv: slow_mode = True demo_mode = False if demo: demo_mode = True elif "--demo" in sys_argv: demo_mode = True if block_images is None: if "--block-images" in sys_argv or "--block_images" in sys_argv: block_images = True else: block_images = False if do_not_track is None: if "--do-not-track" in sys_argv or "--do_not_track" in sys_argv: do_not_track = True else: do_not_track = False if use_wire is None and wire is None: if "--wire" in sys_argv: use_wire = True else: use_wire = False elif use_wire or wire: use_wire = True else: use_wire = False if external_pdf is None: if "--external-pdf" in sys_argv or "--external_pdf" in sys_argv: external_pdf = True else: external_pdf = False if remote_debug is None: if "--remote-debug" in sys_argv or "--remote_debug" in sys_argv: remote_debug = True else: remote_debug = False if enable_3d_apis is None: if "--enable-3d-apis" in sys_argv or "--enable_3d_apis" in sys_argv: enable_3d_apis = True else: enable_3d_apis = False if swiftshader is None: if "--swiftshader" in sys_argv: swiftshader = True else: swiftshader = False if locale is not None and locale_code is None: locale_code = locale if locale_code is None: if '--locale="' in arg_join: locale_code = ( arg_join.split('--locale="')[1].split('"')[0] ) elif '--locale=' in arg_join: locale_code = ( arg_join.split('--locale=')[1].split(' ')[0] ) elif '--locale-code="' in arg_join: locale_code = ( arg_join.split('--locale-code="')[1].split('"')[0] ) elif '--locale-code=' in arg_join: locale_code = ( arg_join.split('--locale-code=')[1].split(' ')[0] ) if ad_block is not None and ad_block_on is None: ad_block_on = ad_block if ad_block_on is None: if "--ad-block" in sys_argv or "--ad_block" in sys_argv: ad_block_on = True else: ad_block_on = False if host_resolver_rules is None: if '--host-resolver-rules="' in arg_join: host_resolver_rules = ( arg_join.split('--host-resolver-rules="')[1].split('"')[0] ) elif '--host_resolver_rules="' in arg_join: host_resolver_rules = ( arg_join.split("--host_resolver_rules=")[1].split('"')[0] ) if driver_version is None and "--driver-version" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--driver-version="): driver_version = arg.split("--driver-version=")[1] break elif arg == "--driver-version" and len(sys_argv) > count + 1: driver_version = sys_argv[count + 1] if driver_version.startswith("-"): driver_version = None break count += 1 if driver_version is None and "--driver_version" in arg_join: count = 0 for arg in sys_argv: if arg.startswith("--driver_version="): driver_version = arg.split("--driver_version=")[1] break elif arg == "--driver_version" and len(sys_argv) > count + 1: driver_version = sys_argv[count + 1] if driver_version.startswith("-"): driver_version = None break count += 1 if highlights is not None: try: highlights = int(highlights) except Exception: raise Exception('"highlights" must be an integer!') if interval is not None: try: interval = float(interval) except Exception: raise Exception('"interval" must be numeric!') if time_limit is not None: try: time_limit = float(time_limit) except Exception: raise Exception('"time_limit" must be numeric!') sb_config.with_testing_base = with_testing_base sb_config.browser = browser if sb_config._browser_shortcut: sb_config.browser = sb_config._browser_shortcut if not hasattr(sb_config, "is_behave"): sb_config.is_behave = False if not hasattr(sb_config, "is_pytest"): sb_config.is_pytest = False if not hasattr(sb_config, "is_nosetest"): sb_config.is_nosetest = False sb_config.is_context_manager = True sb_config.headless = headless sb_config.headless1 = headless1 sb_config.headless2 = headless2 sb_config.headed = headed sb_config.xvfb = xvfb sb_config.xvfb_metrics = xvfb_metrics sb_config.start_page = start_page sb_config.locale_code = locale_code sb_config.protocol = protocol sb_config.servername = servername sb_config.port = port sb_config.data = data sb_config.var1 = var1 sb_config.var2 = var2 sb_config.var3 = var3 sb_config.variables = variables sb_config.account = account sb_config.environment = environment sb_config.env = environment sb_config.user_agent = user_agent sb_config.incognito = incognito sb_config.guest_mode = guest_mode sb_config.dark_mode = dark_mode sb_config.devtools = devtools sb_config.mobile_emulator = is_mobile sb_config.device_metrics = device_metrics sb_config.extension_zip = extension_zip sb_config.extension_dir = extension_dir sb_config.database_env = "test" sb_config.log_path = constants.Logs.LATEST sb_config.archive_logs = archive_logs sb_config.disable_csp = disable_csp sb_config.disable_ws = disable_ws sb_config.enable_ws = enable_ws sb_config.enable_sync = enable_sync sb_config.use_auto_ext = use_auto_ext sb_config.undetectable = undetectable sb_config.uc_cdp_events = uc_cdp_events sb_config.uc_subprocess = uc_subprocess sb_config.log_cdp_events = log_cdp_events sb_config.no_sandbox = None sb_config.disable_gpu = None sb_config.disable_cookies = disable_cookies sb_config.disable_js = disable_js sb_config._multithreaded = False sb_config.reuse_session = False sb_config.crumbs = False sb_config.final_debug = False sb_config.visual_baseline = False sb_config.window_position = window_position sb_config.window_size = window_size sb_config.maximize_option = maximize_option sb_config._disable_beforeunload = _disable_beforeunload sb_config.save_screenshot = save_screenshot sb_config.no_screenshot = no_screenshot sb_config.binary_location = binary_location if hasattr(sb_config, "_cdp_bin_loc") and sb_config._cdp_bin_loc: sb_config.binary_location = sb_config._cdp_bin_loc sb_config.driver_version = driver_version sb_config.page_load_strategy = page_load_strategy sb_config.timeout_multiplier = timeout_multiplier sb_config.pytest_html_report = None sb_config.with_db_reporting = False sb_config.with_s3_logging = False sb_config.js_checking_on = js_checking_on sb_config.recorder_mode = recorder_mode sb_config.recorder_ext = recorder_ext sb_config.record_sleep = record_sleep sb_config.rec_behave = rec_behave sb_config.rec_print = rec_print sb_config.report_on = False sb_config.slow_mode = slow_mode sb_config.demo_mode = demo_mode sb_config._time_limit = time_limit sb_config.demo_sleep = demo_sleep sb_config.dashboard = False sb_config._dashboard_initialized = False sb_config.message_duration = message_duration sb_config.host_resolver_rules = host_resolver_rules sb_config.block_images = block_images sb_config.do_not_track = do_not_track sb_config.use_wire = use_wire sb_config.external_pdf = external_pdf sb_config.remote_debug = remote_debug sb_config.settings_file = settings_file sb_config.user_data_dir = user_data_dir sb_config.chromium_arg = chromium_arg sb_config.firefox_arg = firefox_arg sb_config.firefox_pref = firefox_pref sb_config.disable_features = disable_features sb_config.proxy_string = proxy_string sb_config.proxy_bypass_list = proxy_bypass_list sb_config.proxy_pac_url = proxy_pac_url sb_config.multi_proxy = multi_proxy sb_config.enable_3d_apis = enable_3d_apis sb_config.swiftshader = swiftshader sb_config.ad_block_on = ad_block_on sb_config.highlights = highlights sb_config.interval = interval sb_config.cap_file = cap_file sb_config.cap_string = cap_string if sb_config.browser in constants.ChromiumSubs.chromium_subs: if not sb_config.binary_location: sb_config.browser = "chrome" # Still uses chromedriver sb_config._browser_shortcut = sb_config.browser sb = BaseCase() sb.with_testing_base = sb_config.with_testing_base sb.browser = sb_config.browser sb.is_behave = False sb.is_pytest = False sb.is_nosetest = False sb.is_context_manager = sb_config.is_context_manager sb.headless = sb_config.headless sb.headless1 = sb_config.headless1 sb.headless2 = sb_config.headless2 sb.headed = sb_config.headed sb.xvfb = sb_config.xvfb sb.xvfb_metrics = sb_config.xvfb_metrics sb.start_page = sb_config.start_page sb.locale_code = sb_config.locale_code sb.protocol = sb_config.protocol sb.servername = sb_config.servername sb.port = sb_config.port sb.data = sb_config.data sb.var1 = sb_config.var1 sb.var2 = sb_config.var2 sb.var3 = sb_config.var3 sb.variables = sb_config.variables sb.account = sb_config.account sb.environment = sb_config.environment sb.env = sb_config.env sb.user_agent = sb_config.user_agent sb.incognito = sb_config.incognito sb.guest_mode = sb_config.guest_mode sb.dark_mode = sb_config.dark_mode sb.devtools = sb_config.devtools sb.binary_location = sb_config.binary_location sb.driver_version = sb_config.driver_version sb.mobile_emulator = sb_config.mobile_emulator sb.device_metrics = sb_config.device_metrics sb.extension_zip = sb_config.extension_zip sb.extension_dir = sb_config.extension_dir sb.database_env = sb_config.database_env sb.log_path = sb_config.log_path sb.archive_logs = sb_config.archive_logs sb.disable_csp = sb_config.disable_csp sb.disable_ws = sb_config.disable_ws sb.enable_ws = sb_config.enable_ws sb.enable_sync = sb_config.enable_sync sb.use_auto_ext = sb_config.use_auto_ext sb.undetectable = sb_config.undetectable sb.uc_cdp_events = sb_config.uc_cdp_events sb.uc_subprocess = sb_config.uc_subprocess sb.log_cdp_events = sb_config.log_cdp_events sb.no_sandbox = sb_config.no_sandbox sb.disable_gpu = sb_config.disable_gpu sb.disable_cookies = sb_config.disable_cookies sb.disable_js = sb_config.disable_js sb._multithreaded = sb_config._multithreaded sb._reuse_session = sb_config.reuse_session sb._crumbs = sb_config.crumbs sb._final_debug = sb_config.final_debug sb.visual_baseline = sb_config.visual_baseline sb.window_position = sb_config.window_position sb.window_size = sb_config.window_size sb.maximize_option = sb_config.maximize_option sb._disable_beforeunload = sb_config._disable_beforeunload sb.save_screenshot_after_test = sb_config.save_screenshot sb.no_screenshot_after_test = sb_config.no_screenshot sb.page_load_strategy = sb_config.page_load_strategy sb.timeout_multiplier = sb_config.timeout_multiplier sb.pytest_html_report = sb_config.pytest_html_report sb.with_db_reporting = sb_config.with_db_reporting sb.with_s3_logging = sb_config.with_s3_logging sb.js_checking_on = sb_config.js_checking_on sb.recorder_mode = sb_config.recorder_mode sb.recorder_ext = sb_config.recorder_ext sb.record_sleep = sb_config.record_sleep sb.rec_behave = sb_config.rec_behave sb.rec_print = sb_config.rec_print sb.report_on = sb_config.report_on sb.slow_mode = sb_config.slow_mode sb.demo_mode = sb_config.demo_mode sb.time_limit = sb_config._time_limit sb.demo_sleep = sb_config.demo_sleep sb.dashboard = sb_config.dashboard sb._dash_initialized = sb_config._dashboard_initialized sb.message_duration = sb_config.message_duration sb.host_resolver_rules = sb_config.host_resolver_rules sb.block_images = sb_config.block_images sb.do_not_track = sb_config.do_not_track sb.use_wire = sb_config.use_wire sb.external_pdf = sb_config.external_pdf sb.remote_debug = sb_config.remote_debug sb.settings_file = sb_config.settings_file sb.user_data_dir = sb_config.user_data_dir sb.chromium_arg = sb_config.chromium_arg sb.firefox_arg = sb_config.firefox_arg sb.firefox_pref = sb_config.firefox_pref sb.disable_features = sb_config.disable_features sb.proxy_string = sb_config.proxy_string sb.proxy_bypass_list = sb_config.proxy_bypass_list sb.proxy_pac_url = sb_config.proxy_pac_url sb.multi_proxy = sb_config.multi_proxy sb.enable_3d_apis = sb_config.enable_3d_apis sb._swiftshader = sb_config.swiftshader sb.ad_block_on = sb_config.ad_block_on sb.highlights = sb_config.highlights sb.interval = sb_config.interval sb.cap_file = sb_config.cap_file sb.cap_string = sb_config.cap_string sb._has_failure = False # This may change with suppress(Exception): stack_base = traceback.format_stack()[0].split("with SB(")[0] stack_base = stack_base.split(os.sep)[-1] test_base = stack_base.split(", in ")[0] filename = test_base.split('"')[0] methodname = ".line_" + test_base.split(", line ")[-1] context_id = filename.split(".")[0] + methodname sb._manager_saved_id = context_id if hasattr(sb_config, "headless_active"): sb.headless_active = sb_config.headless_active else: sb.headless_active = False test_name = None terminal_width = shared_utils.get_terminal_width() if test: c1 = colorama.Fore.GREEN b1 = colorama.Style.BRIGHT cr = colorama.Style.RESET_ALL stack_base = traceback.format_stack()[0].split("with SB(")[0] stack_base = stack_base.split(os.sep)[-1] test_name = stack_base.split(", in ")[0].replace('", line ', ":") test_name += ":SB" start_text = "=== {%s} starts ===" % test_name remaining_spaces = terminal_width - len(start_text) left_space = "" right_space = "" if remaining_spaces > 0: left_spaces = int(remaining_spaces / 2) left_space = left_spaces * "=" right_spaces = remaining_spaces - left_spaces right_space = right_spaces * "=" if not test_name.startswith("runpy.py:"): print("%s%s%s%s%s" % (b1, left_space, start_text, right_space, cr)) if do_log_folder_setup: from seleniumbase.core import log_helper from seleniumbase.core import download_helper from seleniumbase.core import proxy_helper log_helper.log_folder_setup(constants.Logs.LATEST + os.sep) log_helper.clear_empty_logs() download_helper.reset_downloads_folder() if not sb_config.multi_proxy: proxy_helper.remove_proxy_zip_if_present() start_time = time.time() sb.setUp() test_passed = True # This can change later teardown_exception = None if "--trace" in sys_argv: import pdb pdb.set_trace() # Debug Mode # Type "s" and press [Enter] to step into "yield sb". try: yield sb except Exception as e: sb._has_failure = True exception = e test_passed = False if (test or inner_test) and not test_name: print(e) return elif not test_name: raise else: the_traceback = traceback.format_exc().strip() try: p2 = the_traceback.split(', in ')[1].split('", line ')[0] filename = p2.split(os.sep)[-1] sb.cm_filename = filename except Exception: sb.cm_filename = None # Tests will raise an exception if raise_test_failure is True finally: if sb._has_failure and "--pdb" in sys_argv: sb_config._do_sb_post_mortem = True elif ( "--final-debug" in sys_argv or "--final-trace" in sys_argv or "--fdebug" in sys_argv or "--ftrace" in sys_argv ): sb_config._do_sb_final_trace = True try: sb.tearDown() except Exception as t_e: teardown_exception = t_e print(traceback.format_exc().strip()) if test and not test_passed: print("********** ERROR: The test AND the tearDown() FAILED!") if ( hasattr(sb_config, "_virtual_display") and sb_config._virtual_display and hasattr(sb_config._virtual_display, "stop") ): try: sb_config._virtual_display.stop() sb_config._virtual_display = None sb_config.headless_active = False except AttributeError: pass except Exception: pass end_time = time.time() run_time = end_time - start_time sb_config = sb_config_backup if test: sb_config._has_older_context = True if test_name: result = "passed" if test and not test_passed: result = "failed" c1 = colorama.Fore.RED end_text = ( "=== {%s} %s in %.2fs ===" % (test_name, result, run_time) ) remaining_spaces = terminal_width - len(end_text) end_text = ( "=== %s%s{%s} %s%s%s in %.2fs ===" % (b1, c1, test_name, result, cr, c1, run_time) ) left_space = "" right_space = "" if remaining_spaces > 0: left_spaces = int(remaining_spaces / 2) left_space = left_spaces * "=" right_spaces = remaining_spaces - left_spaces right_space = right_spaces * "=" if test and not test_passed: print(the_traceback) if not test_name.startswith("runpy.py:"): print( "%s%s%s%s%s" % (c1, left_space, end_text, right_space, cr) ) if undetectable and hasattr(sb, "_drivers_browser_map"): import asyncio for driver in sb._drivers_browser_map.keys(): if ( hasattr(driver, "cdp") and driver.cdp and hasattr(driver.cdp, "loop") ): asyncio.set_event_loop(driver.cdp.loop) tasks = [tab.aclose() for tab in driver.cdp.get_tabs()] tasks.append(driver.cdp.driver.connection.aclose()) driver.cdp.loop.run_until_complete(asyncio.gather(*tasks)) driver.cdp.loop.close() gc.collect() if test and test_name and not test_passed and raise_test_failure: raise exception elif ( teardown_exception and ( not test or (test_passed and raise_test_failure) ) ): raise teardown_exception ================================================ FILE: seleniumbase/plugins/screen_shots.py ================================================ """Screenshot Plugin for SeleniumBase tests that run with pynose / nosetests""" import os from nose.plugins import Plugin from seleniumbase.config import settings class ScreenShots(Plugin): """This plugin takes a screenshot when a test fails.""" name = "screen_shots" logfile_name = settings.SCREENSHOT_NAME def options(self, parser, env): super().options(parser, env=env) def configure(self, options, conf): super().configure(options, conf) if not self.enabled: return self.options = options def add_screenshot(self, test, err, capt=None, tbinfo=None): test_logpath = self.options.log_path + "/" + test.id() if not os.path.exists(test_logpath): os.makedirs(test_logpath) screenshot_file = "%s/%s" % (test_logpath, self.logfile_name) test.driver.get_screenshot_as_file(screenshot_file) def addError(self, test, err, capt=None): self.add_screenshot(test, err, capt=capt) def addFailure(self, test, err, capt=None, tbinfo=None): self.add_screenshot(test, err, capt=capt, tbinfo=tbinfo) ================================================ FILE: seleniumbase/plugins/selenium_plugin.py ================================================ """Selenium Plugin for SeleniumBase tests that run with pynose / nosetests""" import os import sys from contextlib import suppress from nose.plugins import Plugin from seleniumbase import config as sb_config from seleniumbase.config import settings from seleniumbase.core import detect_b_ver from seleniumbase.core import proxy_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils class SeleniumBrowser(Plugin): """This plugin adds the following command-line options to pynose: --browser=BROWSER (The web browser to use. Default: "chrome".) --chrome (Shortcut for "--browser=chrome". Default.) --edge (Shortcut for "--browser=edge".) --firefox (Shortcut for "--browser=firefox".) --safari (Shortcut for "--browser=safari".) --opera (Shortcut for "--browser=opera".) --brave (Shortcut for "--browser=brave".) --comet (Shortcut for "--browser=comet".) --atlas (Shortcut for "--browser=atlas".) --use-chromium (Shortcut for using base `Chromium`) --cft (Shortcut for using `Chrome for Testing`) --chs (Shortcut for using `Chrome-Headless-Shell`) --user-data-dir=DIR (Set the Chrome user data directory to use.) --protocol=PROTOCOL (The Selenium Grid protocol: http|https.) --server=SERVER (The Selenium Grid server/IP used for tests.) --port=PORT (The Selenium Grid port used by the test server.) --cap-file=FILE (The web browser's desired capabilities to use.) --cap-string=STRING (The web browser's desired capabilities to use.) --proxy=SERVER:PORT (Connect to a proxy server:port as tests are running) --proxy=USERNAME:PASSWORD@SERVER:PORT (Use an authenticated proxy server) --proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com") --proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.) --proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.) --proxy-driver (If a driver download is needed, will use: --proxy=PROXY.) --multi-proxy (Allow multiple authenticated proxies when multi-threaded.) --agent=STRING (Modify the web browser's User-Agent string.) --mobile (Use the mobile device emulator while running tests.) --metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".) --chromium-arg="ARG=N,ARG2" (Set Chromium args, ","-separated, no spaces.) --firefox-arg="ARG=N,ARG2" (Set Firefox args, comma-separated, no spaces.) --firefox-pref=SET (Set a Firefox preference:value set, comma-separated.) --extension-zip=ZIP (Load a Chrome Extension .zip|.crx, comma-separated.) --extension-dir=DIR (Load a Chrome Extension directory, comma-separated.) --disable-features="F1,F2" (Disable features, comma-separated, no spaces.) --binary-location=PATH (Set path of the Chromium browser binary to use.) --driver-version=VER (Set the chromedriver or uc_driver version to use.) --sjw (Skip JS Waits for readyState to be "complete" or Angular to load.) --wfa (Wait for AngularJS to be done loading after specific web actions.) --pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".) --headless (The default headless mode. Linux uses this mode by default.) --headless1 (Use Chrome's old headless mode. Fast, but has limitations.) --headless2 (Use Chrome's new headless mode, which supports extensions.) --headed (Run tests in headed/GUI mode on Linux OS, where not default.) --xvfb (Run tests using the Xvfb virtual display server on Linux OS.) --xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".) --locale=LOCALE_CODE (Set the Language Locale Code for the web browser.) --interval=SECONDS (The autoplay interval for presentations & tour steps) --start-page=URL (The starting URL for the web browser when tests begin.) --time-limit=SECONDS (Safely fail any test that exceeds the time limit.) --slow (Slow down the automation. Faster than using Demo Mode.) --demo (Slow down and visually see test actions as they occur.) --demo-sleep=SECONDS (Set the wait time after Slow & Demo Mode actions.) --highlights=NUM (Number of highlight animations for Demo Mode actions.) --message-duration=SECONDS (The time length for Messenger alerts.) --check-js (Check for JavaScript errors after page loads.) --ad-block (Block some types of display ads from loading.) --host-resolver-rules=RULES (Set host-resolver-rules, comma-separated.) --block-images (Block images from loading during tests.) --do-not-track (Indicate to websites that you don't want to be tracked.) --verify-delay=SECONDS (The delay before MasterQA verification checks.) --ee / --esc-end (Lets the user end the current test via the ESC key.) --recorder (Enables the Recorder for turning browser actions into code.) --rec-behave (Same as Recorder Mode, but also generates behave-gherkin.) --rec-sleep (If the Recorder is enabled, also records self.sleep calls.) --rec-print (If the Recorder is enabled, prints output after tests end.) --disable-cookies (Disable Cookies on websites. Pages might break!) --disable-js (Disable JavaScript on websites. Pages might break!) --disable-csp (Disable the Content Security Policy of websites.) --disable-ws (Disable Web Security on Chromium-based browsers.) --enable-ws (Enable Web Security on Chromium-based browsers.) --enable-sync (Enable "Chrome Sync" on websites.) --uc | --undetected (Use undetected-chromedriver to evade bot-detection.) --uc-cdp-events (Capture CDP events when running in "--undetected" mode.) --log-cdp ("goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"}) --remote-debug (Sync to Chrome Remote Debugger chrome://inspect/#devices) --enable-3d-apis (Enables WebGL and 3D APIs.) --swiftshader (Chrome "--use-gl=angle" / "--use-angle=swiftshader-webgl") --incognito (Enable Chrome's Incognito mode.) --guest (Enable Chrome's Guest mode.) --dark (Enable Chrome's Dark mode.) --devtools (Open Chrome's DevTools when the browser opens.) --disable-beforeunload (Disable the "beforeunload" event on Chrome.) --window-position=X,Y (Set the browser's starting window position.) --window-size=WIDTH,HEIGHT (Set the browser's starting window size.) --maximize (Start tests with the browser window maximized.) --screenshot (Save a screenshot at the end of each test.) --visual-baseline (Set the visual baseline for Visual/Layout tests.) --wire (Use selenium-wire's webdriver for replacing selenium webdriver.) --external-pdf (Set Chromium "plugins.always_open_pdf_externally": True.) --timeout-multiplier=MULTIPLIER (Multiplies the default timeout values.) """ name = "selenium" # Usage: --with-selenium def options(self, parser, env): super().options(parser, env=env) parser.addoption = parser.add_option # Reuse name from pytest parser parser.addoption( "--browser", action="store", dest="browser", choices=constants.ValidBrowsers.valid_browsers, default=constants.Browser.GOOGLE_CHROME, help="""Specifies the web browser to use. Default: Chrome. Examples: (--browser=edge OR --browser=firefox)""", ) parser.addoption( "--chrome", action="store_true", dest="use_chrome", default=False, help="""Shortcut for --browser=chrome (Default)""", ) parser.addoption( "--edge", action="store_true", dest="use_edge", default=False, help="""Shortcut for --browser=edge""", ) parser.addoption( "--firefox", action="store_true", dest="use_firefox", default=False, help="""Shortcut for --browser=firefox""", ) parser.addoption( "--ie", action="store_true", dest="use_ie", default=False, help="""Shortcut for --browser=ie""", ) parser.addoption( "--safari", action="store_true", dest="use_safari", default=False, help="""Shortcut for --browser=safari""", ) parser.addoption( "--opera", action="store_true", dest="use_opera", default=False, help="""Shortcut for --browser=opera""", ) parser.addoption( "--brave", action="store_true", dest="use_brave", default=False, help="""Shortcut for --browser=brave""", ) parser.addoption( "--comet", action="store_true", dest="use_comet", default=False, help="""Shortcut for --browser=comet""", ) parser.addoption( "--atlas", action="store_true", dest="use_atlas", default=False, help="""Shortcut for --browser=atlas""", ) parser.addoption( "--use-chromium", action="store_true", dest="use_chromium", default=False, help="""Shortcut for using base `Chromium`""", ) parser.addoption( "--cft", action="store_true", dest="use_cft", default=False, help="""Shortcut for using `Chrome for Testing`""", ) parser.addoption( "--chs", action="store_true", dest="use_chs", default=False, help="""Shortcut for using `Chrome-Headless-Shell`""", ) parser.addoption( "--cap_file", "--cap-file", action="store", dest="cap_file", default=None, help="""The file that stores browser desired capabilities for BrowserStack, Sauce Labs, or other grids.""", ) parser.addoption( "--cap_string", "--cap-string", dest="cap_string", default=None, help="""The string that stores browser desired capabilities for BrowserStack, Sauce Labs, or other grids. Enclose cap-string in single quotes. Enclose parameter keys in double quotes. Example: --cap-string='{"name":"test1","v":"42"}'""", ) parser.addoption( "--user_data_dir", "--user-data-dir", action="store", dest="user_data_dir", default=None, help="""The Chrome User Data Directory to use. (Chrome Profile) If the directory doesn't exist, it'll be created.""", ) parser.addoption( "--sjw", "--skip_js_waits", "--skip-js-waits", action="store_true", dest="skip_js_waits", default=False, help="""Skip all calls to wait_for_ready_state_complete() and wait_for_angularjs(), which are part of many SeleniumBase methods for improving reliability.""", ) parser.addoption( "--wfa", "--wait_for_angularjs", "--wait-for-angularjs", action="store_true", dest="wait_for_angularjs", default=False, help="""Add waiting for AngularJS. (The default setting was changed to no longer wait for AngularJS to finish loading as an extra JavaScript call.)""", ) parser.addoption( "--protocol", action="store", dest="protocol", choices=( constants.Protocol.HTTP, constants.Protocol.HTTPS, ), default=constants.Protocol.HTTP, help="""Designates the Selenium Grid protocol to use. Default: http.""", ) parser.addoption( "--server", action="store", dest="servername", default="localhost", help="""Designates the Selenium Grid server to use. Use "127.0.0.1" to connect to a localhost Grid. If unset or set to "localhost", Grid isn't used. Default: "localhost".""", ) parser.addoption( "--port", action="store", dest="port", default="4444", help="""Designates the Selenium Grid port to use. Default: 4444. (If 443, protocol becomes "https")""", ) parser.addoption( "--proxy", "--proxy-server", "--proxy-string", action="store", dest="proxy_string", default=None, help="""Designates the proxy server:port to use. Format: servername:port. OR username:password@servername:port OR A dict key from proxy_list.PROXY_LIST Default: None.""", ) parser.addoption( "--proxy-bypass-list", "--proxy_bypass_list", action="store", dest="proxy_bypass_list", default=None, help="""Designates the hosts, domains, and/or IP addresses to bypass when using a proxy server with "--proxy". Format: A ";"-separated string. Example usage: pytest --proxy="username:password@servername:port" --proxy-bypass-list="*.foo.com;github.com" pytest --proxy="servername:port" --proxy-bypass-list="127.0.0.1:8080" Default: None.""", ) parser.addoption( "--proxy-pac-url", "--pac-url", action="store", dest="proxy_pac_url", default=None, help="""Designates the proxy PAC URL to use. Format: A URL string OR A username:password@URL string Default: None.""", ) parser.addoption( "--proxy-driver", "--proxy_driver", action="store_true", dest="proxy_driver", default=False, help="""If a driver download is needed for tests, uses proxy settings set via --proxy=PROXY.""", ) parser.addoption( "--multi-proxy", "--multi_proxy", action="store_true", dest="multi_proxy", default=False, help="""If you need to run multi-threaded tests with multiple proxies that require authentication, set this to allow multiple configurations.""", ) parser.addoption( "--agent", "--user-agent", "--user_agent", action="store", dest="user_agent", default=None, help="""Designates the User-Agent for the browser to use. Format: A string. Default: None.""", ) parser.addoption( "--mobile", "--mobile-emulator", "--mobile_emulator", action="store_true", dest="mobile_emulator", default=False, help="""If this option is enabled, the mobile emulator will be used while running tests.""", ) parser.addoption( "--metrics", "--device-metrics", "--device_metrics", action="store", dest="device_metrics", default=None, help="""Designates the three device metrics of the mobile emulator: CSS Width, CSS Height, and Pixel-Ratio. Format: A comma-separated string with the 3 values. Examples: "375,734,5" or "412,732,3" or "390,715,3" Default: None. (Will use default values if None)""", ) parser.addoption( "--chromium_arg", "--chromium-arg", action="store", dest="chromium_arg", default=None, help="""Add a Chromium argument for Chrome/Edge browsers. Format: A comma-separated list of Chromium args. If an arg doesn't start with "--", that will be added to the beginning of the arg automatically. Default: None.""", ) parser.addoption( "--firefox_arg", "--firefox-arg", action="store", dest="firefox_arg", default=None, help="""Add a Firefox argument for Firefox browser runs. Format: A comma-separated list of Firefox args. If an arg doesn't start with "--", that will be added to the beginning of the arg automatically. Default: None.""", ) parser.addoption( "--firefox_pref", "--firefox-pref", action="store", dest="firefox_pref", default=None, help="""Set a Firefox preference:value combination. Format: A comma-separated list of pref:value items. Example usage: --firefox-pref="browser.formfill.enable:True" --firefox-pref="pdfjs.disabled:False" --firefox-pref="abc.def.xyz:42,hello.world:text" Boolean and integer values to the right of the ":" will be automatically converted into proper format. If there's no ":" in the string, then True is used. Default: None.""", ) parser.addoption( "--extension_zip", "--extension-zip", "--crx", action="store", dest="extension_zip", default=None, help="""Designates the Chrome Extension ZIP file to load. Format: A comma-separated list of .zip or .crx files containing the Chrome extensions to load. Default: None.""", ) parser.addoption( "--extension_dir", "--extension-dir", action="store", dest="extension_dir", default=None, help="""Designates the Chrome Extension folder to load. Format: A directory containing the Chrome extension. (Can also be a comma-separated list of directories.) Default: None.""", ) parser.addoption( "--disable_features", "--disable-features", action="store", dest="disable_features", default=None, help="""Disable Chromium features from Chrome/Edge browsers. Format: A comma-separated list of Chromium features. Default: None.""", ) parser.addoption( "--binary_location", "--binary-location", "--bl", action="store", dest="binary_location", default=None, help="""Sets the path of the Chromium browser binary to use. Uses the default location if not os.path.exists(PATH)""", ) parser.addoption( "--driver_version", "--driver-version", action="store", dest="driver_version", default=None, help="""Setting this overrides the default driver version, which is set to match the detected browser version. Major version only. Example: "--driver-version=114" (Only chromedriver and uc_driver are affected.)""", ) parser.addoption( "--pls", "--page_load_strategy", "--page-load-strategy", action="store", dest="page_load_strategy", choices=( constants.PageLoadStrategy.NORMAL, constants.PageLoadStrategy.EAGER, constants.PageLoadStrategy.NONE, ), default=None, help="""This option sets Chrome's pageLoadStrategy. List of choices: "normal", "eager", "none".""", ) parser.addoption( "--headless", action="store_true", dest="headless", default=False, help="""Using this option activates headless mode, which is required on headless machines UNLESS using a virtual display with Xvfb. Default: False on Mac/Windows. True on Linux.""", ) parser.addoption( "--headless1", action="store_true", dest="headless1", default=False, help="""This option activates the old headless mode, which is faster, but has limitations. (May be phased out by Chrome in the future.)""", ) parser.addoption( "--headless2", action="store_true", dest="headless2", default=False, help="""This option activates the new headless mode, which supports Chromium extensions, and more, but is slower than the standard headless mode.""", ) parser.addoption( "--headed", "--gui", action="store_true", dest="headed", default=False, help="""Using this makes Webdriver run web browsers with a GUI when running tests on Linux machines. (The default setting on Linux is headless.) (The default setting on Mac or Windows is headed.)""", ) parser.addoption( "--xvfb", action="store_true", dest="xvfb", default=False, help="""Using this makes tests run headlessly using Xvfb instead of the browser's built-in headless mode. When using "--xvfb", the "--headless" option will no longer be enabled by default on Linux. Default: False. (Linux-ONLY!)""", ) parser.addoption( "--xvfb-metrics", "--xvfb_metrics", action="store", dest="xvfb_metrics", default=None, help="""Customize the Xvfb metrics (Width,Height) on Linux. Format: A comma-separated string with the 2 values. Examples: "1920,1080" or "1366,768" or "1024,768". Default: None. (None: "1366,768". Min: "1024,768".)""", ) parser.addoption( "--locale_code", "--locale-code", "--locale", action="store", dest="locale_code", default=None, help="""Designates the Locale Code for the web browser. A Locale is a specific version of a spoken Language. The Locale alters visible text on supported websites. See: https://seleniumbase.io/help_docs/locale_codes/ Default: None. (The web browser's default mode.)""", ) parser.addoption( "--interval", action="store", dest="interval", default=None, help="""This globally overrides the default interval, (in seconds), of features that include autoplay functionality, such as tours and presentations. Overrides from methods take priority over this. (Headless Mode skips tours and presentations.)""", ) parser.addoption( "--start_page", "--start-page", "--url", action="store", dest="start_page", default=None, help="""Designates the starting URL for the web browser when each test begins. Default: None.""", ) parser.addoption( "--time_limit", "--time-limit", "--timelimit", action="store", dest="time_limit", default=None, help="""Use this to set a time limit per test, in seconds. If a test runs beyond the limit, it fails.""", ) parser.addoption( "--slow_mode", "--slow-mode", "--slowmo", "--slow", action="store_true", dest="slow_mode", default=False, help="""Using this slows down the automation.""", ) parser.addoption( "--demo_mode", "--demo-mode", "--demo", action="store_true", dest="demo_mode", default=False, help="""Using this slows down the automation and lets you visually see what the tests are actually doing.""", ) parser.addoption( "--demo_sleep", "--demo-sleep", action="store", dest="demo_sleep", default=None, help="""Setting this overrides the Demo Mode sleep time that happens after browser actions.""", ) parser.addoption( "--highlights", action="store", dest="highlights", default=None, help="""Setting this overrides the default number of highlight animation loops to have per call.""", ) parser.addoption( "--message_duration", "--message-duration", action="store", dest="message_duration", default=None, help="""Setting this overrides the default time that messenger notifications remain visible when reaching assert statements during Demo Mode.""", ) parser.addoption( "--check_js", "--check-js", action="store_true", dest="js_checking_on", default=False, help="""The option to check for JavaScript errors after every page load.""", ) parser.addoption( "--adblock", "--ad_block", "--ad-block", "--block_ads", "--block-ads", action="store_true", dest="ad_block_on", default=False, help="""Using this makes WebDriver block display ads that are defined in ad_block_list.AD_BLOCK_LIST.""", ) parser.addoption( "--host_resolver_rules", "--host-resolver-rules", action="store", dest="host_resolver_rules", default=None, help="""Use this option to set "host-resolver-rules". This lets you re-map traffic from any domain. Eg. "MAP www.google-analytics.com 0.0.0.0". Eg. "MAP * ~NOTFOUND , EXCLUDE myproxy". Eg. "MAP * 0.0.0.0 , EXCLUDE 127.0.0.1". Eg. "MAP *.google.com myproxy". Find more examples on these pages: (https://www.electronjs.org/docs/ latest/api/command-line-switches) (https://www.chromium.org/developers/ design-documents/network-stack/socks-proxy/) Use comma-separation for multiple host rules.""", ) parser.addoption( "--block_images", "--block-images", action="store_true", dest="block_images", default=False, help="""Using this makes WebDriver block images from loading on web pages during tests.""", ) parser.addoption( "--do_not_track", "--do-not-track", action="store_true", dest="do_not_track", default=False, help="""Indicate to websites that you don't want to be tracked. The browser will send an extra HTTP header each time it requests a web page. https://support.google.com/chrome/answer/2790761""", ) parser.addoption( "--verify_delay", "--verify-delay", action="store", dest="verify_delay", default=None, help="""Setting this overrides the default wait time before each MasterQA verification pop-up.""", ) parser.addoption( "--esc-end", "--esc_end", "--ee", action="store_true", dest="esc_end", default=False, help="""End the current test early via the ESC key. The test will be marked as skipped.""", ) parser.addoption( "--recorder", "--record", "--rec", "--codegen", action="store_true", dest="recorder_mode", default=False, help="""Using this enables the SeleniumBase Recorder, which records browser actions for converting into SeleniumBase scripts.""", ) parser.addoption( "--rec-behave", "--rec-gherkin", action="store_true", dest="rec_behave", default=False, help="""Not only enables the SeleniumBase Recorder, but also saves recorded actions into the behave-gerkin format, which includes a feature file, an imported steps file, and the environment.py file.""", ) parser.addoption( "--rec-sleep", "--record-sleep", action="store_true", dest="record_sleep", default=False, help="""If Recorder Mode is enabled, records sleep(seconds) calls.""", ) parser.addoption( "--rec-print", action="store_true", dest="rec_print", default=False, help="""If Recorder Mode is enabled, prints output after tests end.""", ) parser.addoption( "--disable_js", "--disable-js", action="store_true", dest="disable_js", default=False, help="""The option to disable JavaScript on web pages. Warning: Most web pages will stop working!""", ) parser.addoption( "--disable_cookies", "--disable-cookies", action="store_true", dest="disable_cookies", default=False, help="""The option to disable Cookies on web pages. Warning: Several pages may stop working!""", ) parser.addoption( "--disable_csp", "--disable-csp", "--no_csp", "--no-csp", "--dcsp", action="store_true", dest="disable_csp", default=False, help="""Using this disables the Content Security Policy of websites, which may interfere with some features of SeleniumBase, such as loading custom JavaScript libraries for various testing actions. Setting this to True (--disable-csp) overrides the value set in seleniumbase/config/settings.py""", ) parser.addoption( "--disable_ws", "--disable-ws", "--dws", "--disable-web-security", action="store_true", dest="disable_ws", default=False, help="""Using this disables the "Web Security" feature of Chrome and Chromium-based browsers such as Edge.""", ) parser.addoption( "--enable_ws", "--enable-ws", "--enable-web-security", action="store_true", dest="enable_ws", default=False, help="""Using this enables the "Web Security" feature of Chrome and Chromium-based browsers such as Edge.""", ) parser.addoption( "--enable_sync", "--enable-sync", action="store_true", dest="enable_sync", default=False, help="""Using this enables the "Chrome Sync" feature.""", ) parser.addoption( "--use_auto_ext", "--use-auto-ext", "--auto-ext", action="store_true", dest="use_auto_ext", default=False, help="""(DEPRECATED) - Enable the automation extension. It's not required, but some commands & advanced features may need it.""", ) parser.addoption( "--undetected", "--undetectable", "--uc", # undetected-chromedriver action="store_true", dest="undetectable", default=False, help="""Using this option makes chromedriver undetectable to websites that use anti-bot services to block automation tools from navigating them freely.""", ) parser.addoption( "--uc_cdp_events", "--uc-cdp-events", "--uc-cdp", # For capturing CDP events during UC Mode action="store_true", dest="uc_cdp_events", default=None, help="""Captures CDP events during Undetectable Mode runs. Then you can add a listener to perform actions on received data, such as printing it to the console: from pprint import pformat self.driver.add_cdp_listener( "*", lambda data: print(pformat(data)) ) self.open(URL)""", ) parser.addoption( "--uc_subprocess", "--uc-subprocess", "--uc-sub", # undetected-chromedriver subprocess mode action="store_true", dest="uc_subprocess", default=None, help="""(DEPRECATED) - (UC Mode always uses this now.) Use undetectable-chromedriver as a subprocess, which can help avoid issues that might result.""", ) parser.addoption( "--no_sandbox", "--no-sandbox", action="store_true", dest="no_sandbox", default=False, help="""(DEPRECATED) - "--no-sandbox" is always used now. Using this enables the "No Sandbox" feature. (This setting is now always enabled by default.)""", ) parser.addoption( "--disable_gpu", "--disable-gpu", action="store_true", dest="disable_gpu", default=False, help="""(DEPRECATED) - GPU is disabled if no swiftshader. Using this enables the "Disable GPU" feature. (GPU is disabled by default if swiftshader off.)""", ) parser.addoption( "--log_cdp", "--log-cdp", "--log_cdp_events", "--log-cdp-events", action="store_true", dest="log_cdp_events", default=None, help="""Capture CDP events. Then you can print them. Eg. print(driver.get_log("performance"))""", ) parser.addoption( "--remote_debug", "--remote-debug", "--remote-debugger", "--remote_debugger", action="store_true", dest="remote_debug", default=False, help="""This syncs the browser to Chromium's remote debugger. To access the remote debugging interface, go to: chrome://inspect/#devices while tests are running. The previous URL was at: http://localhost:9222/ Info: chromedevtools.github.io/devtools-protocol/""", ) parser.addoption( "--enable_3d_apis", "--enable-3d-apis", action="store_true", dest="enable_3d_apis", default=False, help="""Using this enables WebGL and 3D APIs.""", ) parser.addoption( "--swiftshader", action="store_true", dest="swiftshader", default=False, help="""Using this enables the "--use-gl=swiftshader" feature when running tests on Chrome.""", ) parser.addoption( "--incognito", "--incognito_mode", "--incognito-mode", action="store_true", dest="incognito", default=False, help="""Using this enables Chrome's Incognito mode.""", ) parser.addoption( "--guest", "--guest_mode", "--guest-mode", action="store_true", dest="guest_mode", default=False, help="""Using this enables Chrome's Guest mode.""", ) parser.addoption( "--dark", "--dark_mode", "--dark-mode", action="store_true", dest="dark_mode", default=False, help="""Using this enables Chrome's Dark mode.""", ) parser.addoption( "--devtools", "--open_devtools", "--open-devtools", action="store_true", dest="devtools", default=False, help="""Using this opens Chrome's DevTools.""", ) parser.addoption( "--disable-beforeunload", "--disable_beforeunload", action="store_true", dest="_disable_beforeunload", default=False, help="""The option to disable the "beforeunload" event on Chromium browsers (Chrome or Edge). This is already the default Firefox option.""", ) parser.addoption( "--window-position", "--window_position", action="store", dest="window_position", default=None, help="""The option to set the starting window x,y position. Format: A comma-separated string with the 2 values. Example: "55,66" Default: None. (Will use default values if None)""", ) parser.addoption( "--window-size", "--window_size", action="store", dest="window_size", default=None, help="""The option to set the default window "width,height". Format: A comma-separated string with the 2 values. Example: "1200,800" Default: None. (Will use default values if None)""", ) parser.addoption( "--maximize_window", "--maximize-window", "--maximize", "--fullscreen", action="store_true", dest="maximize_option", default=False, help="""The option to start with a maximized browser window. (Overrides the "window-size" option if used.)""", ) parser.addoption( "--screenshot", "--save_screenshot", "--save-screenshot", "--ss", action="store_true", dest="save_screenshot", default=False, help="""Save a screenshot at the end of every test. By default, this is only done for failures. Will be saved in the "latest_logs/" folder.""", ) parser.addoption( "--no-screenshot", "--no_screenshot", "--ns", action="store_true", dest="no_screenshot", default=False, help="""No screenshots saved unless tests directly ask it. This changes default behavior where screenshots are saved for test failures and pytest-html reports.""", ) parser.addoption( "--visual_baseline", "--visual-baseline", action="store_true", dest="visual_baseline", default=False, help="""Setting this resets the visual baseline for Automated Visual Testing with SeleniumBase. When a test calls self.check_window(), it will rebuild its files in the visual_baseline folder.""", ) parser.addoption( "--wire", action="store_true", dest="use_wire", default=False, help="""Use selenium-wire's webdriver for selenium webdriver.""", ) parser.addoption( "--external_pdf", "--external-pdf", action="store_true", dest="external_pdf", default=False, help="""This option sets the following on Chromium: "plugins.always_open_pdf_externally": True, which causes opened PDF URLs to download immediately, instead of being displayed in the browser window.""", ) parser.addoption( "--timeout_multiplier", "--timeout-multiplier", action="store", dest="timeout_multiplier", default=None, help="""Setting this overrides the default timeout by the multiplier when waiting for page elements. Unused when tests override the default value.""", ) def configure(self, options, conf): super().configure(options, conf) self.enabled = True # Used if test class inherits BaseCase self.options = options self.headless_active = False # Default setting sb_config.headless_active = False sb_config.is_nosetest = True proxy_helper.remove_proxy_zip_if_present() def beforeTest(self, test): browser = self.options.browser test.test.browser = browser test.test.headless = None test.test.headless1 = None test.test.headless2 = None # As a shortcut, you can use "--edge" instead of "--browser=edge", etc, # but you can only specify one default browser. (Default: chrome) sb_config._browser_shortcut = None sys_argv = sys.argv browser_changes = 0 browser_set = None browser_text = None browser_list = [] # Check if binary-location in options bin_loc_in_options = False for arg in sys_argv: if arg in ["--binary-location", "--binary_location", "--bl"]: bin_loc_in_options = True if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv: browser_changes += 1 browser_set = "chrome" browser_list.append("--browser=chrome") if "--browser=edge" in sys_argv or "--browser edge" in sys_argv: browser_changes += 1 browser_set = "edge" browser_list.append("--browser=edge") if "--browser=firefox" in sys_argv or "--browser firefox" in sys_argv: browser_changes += 1 browser_set = "firefox" browser_list.append("--browser=firefox") if "--browser=safari" in sys_argv or "--browser safari" in sys_argv: browser_changes += 1 browser_set = "safari" browser_list.append("--browser=safari") if "--browser=ie" in sys_argv or "--browser ie" in sys_argv: browser_changes += 1 browser_set = "ie" browser_list.append("--browser=ie") if "--browser=remote" in sys_argv or "--browser remote" in sys_argv: browser_changes += 1 browser_set = "remote" browser_list.append("--browser=remote") if "--browser=opera" in sys_argv or "--browser opera" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=opera") if "--browser=brave" in sys_argv or "--browser brave" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=brave") if "--browser=comet" in sys_argv or "--browser comet" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=comet") if "--browser=atlas" in sys_argv or "--browser atlas" in sys_argv: if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_set = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--browser=atlas") browser_text = browser_set if "--chrome" in sys_argv and not browser_set == "chrome": browser_changes += 1 browser_text = "chrome" sb_config._browser_shortcut = "chrome" browser_list.append("--chrome") if "--edge" in sys_argv and not browser_set == "edge": browser_changes += 1 browser_text = "edge" sb_config._browser_shortcut = "edge" browser_list.append("--edge") if "--firefox" in sys_argv and not browser_set == "firefox": browser_changes += 1 browser_text = "firefox" sb_config._browser_shortcut = "firefox" browser_list.append("--firefox") if "--ie" in sys_argv and not browser_set == "ie": browser_changes += 1 browser_text = "ie" sb_config._browser_shortcut = "ie" browser_list.append("--ie") if "--safari" in sys_argv and not browser_set == "safari": browser_changes += 1 browser_text = "safari" sb_config._browser_shortcut = "safari" browser_list.append("--safari") if "--opera" in sys_argv and not browser_set == "opera": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("opera") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "opera" sb_config._browser_shortcut = "opera" sb_config._cdp_browser = "opera" sb_config._cdp_bin_loc = bin_loc browser_list.append("--opera") if "--brave" in sys_argv and not browser_set == "brave": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("brave") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "brave" sb_config._browser_shortcut = "brave" sb_config._cdp_browser = "brave" sb_config._cdp_bin_loc = bin_loc browser_list.append("--brave") if "--comet" in sys_argv and not browser_set == "comet": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("comet") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "comet" sb_config._browser_shortcut = "comet" sb_config._cdp_browser = "comet" sb_config._cdp_bin_loc = bin_loc browser_list.append("--comet") if "--atlas" in sys_argv and not browser_set == "atlas": if not bin_loc_in_options: bin_loc = detect_b_ver.get_binary_location("atlas") if os.path.exists(bin_loc): browser_changes += 1 browser_text = "atlas" sb_config._browser_shortcut = "atlas" sb_config._cdp_browser = "atlas" sb_config._cdp_bin_loc = bin_loc browser_list.append("--atlas") if browser_changes > 1: message = "\n\n TOO MANY browser types were entered!" message += "\n There were %s found:\n > %s" % ( browser_changes, ", ".join(browser_list), ) message += "\n ONLY ONE default browser is allowed!" message += "\n Select a single browser & try again!\n" raise Exception(message) if browser_text: browser = browser_text if self.options.recorder_mode and browser not in [ "chrome", "edge", "opera", "brave", "comet", "atlas", "chromium" ]: message = ( "\n\n Recorder Mode ONLY supports Chromium browsers!" '\n (Your browser choice was: "%s")\n' % browser ) raise Exception(message) window_position = self.options.window_position if window_position: if window_position.count(",") != 1: message = ( '\n\n window_position expects an "x,y" string!' '\n (Your input was: "%s")\n' % window_position ) raise Exception(message) window_position = window_position.replace(" ", "") win_x = None win_y = None try: win_x = int(window_position.split(",")[0]) win_y = int(window_position.split(",")[1]) except Exception: message = ( '\n\n Expecting integer values for "x,y"!' '\n (window_position input was: "%s")\n' % window_position ) raise Exception(message) settings.WINDOW_START_X = win_x settings.WINDOW_START_Y = win_y window_size = self.options.window_size if window_size: if window_size.count(",") != 1: message = ( '\n\n window_size expects a "width,height" string!' '\n (Your input was: "%s")\n' % window_size ) raise Exception(message) window_size = window_size.replace(" ", "") width = None height = None try: width = int(window_size.split(",")[0]) height = int(window_size.split(",")[1]) except Exception: message = ( '\n\n Expecting integer values for "width,height"!' '\n (window_size input was: "%s")\n' % window_size ) raise Exception(message) settings.CHROME_START_WIDTH = width settings.CHROME_START_HEIGHT = height settings.HEADLESS_START_WIDTH = width settings.HEADLESS_START_HEIGHT = height test.test.is_nosetest = True test.test.is_behave = False test.test.is_pytest = False test.test.is_context_manager = False sb_config.is_nosetest = True sb_config.is_behave = False sb_config.is_pytest = False sb_config.is_context_manager = False test.test.browser = self.options.browser if sb_config._browser_shortcut: self.options.browser = sb_config._browser_shortcut test.test.browser = sb_config._browser_shortcut test.test.cap_file = self.options.cap_file test.test.cap_string = self.options.cap_string test.test.headless = self.options.headless test.test.headless1 = self.options.headless1 if test.test.headless1: test.test.headless = True test.test.headless2 = self.options.headless2 if test.test.headless and test.test.browser == "safari": test.test.headless = False # Safari doesn't use headless test.test.headless1 = False if test.test.headless2 and test.test.browser == "firefox": test.test.headless2 = False # Only for Chromium browsers test.test.headless = True # Firefox has regular headless self.options.headless2 = False self.options.headless = True elif test.test.browser not in ["chrome", "edge"]: test.test.headless2 = False # Only for Chromium browsers self.options.headless2 = False test.test.headed = self.options.headed test.test.xvfb = self.options.xvfb test.test.xvfb_metrics = self.options.xvfb_metrics test.test.locale_code = self.options.locale_code test.test.interval = self.options.interval test.test.start_page = self.options.start_page if self.options.skip_js_waits: settings.SKIP_JS_WAITS = True if self.options.wait_for_angularjs: settings.WAIT_FOR_ANGULARJS = True test.test.protocol = self.options.protocol test.test.servername = self.options.servername test.test.port = self.options.port test.test.user_data_dir = self.options.user_data_dir test.test.extension_zip = self.options.extension_zip test.test.extension_dir = self.options.extension_dir test.test.disable_features = self.options.disable_features test.test.binary_location = self.options.binary_location if getattr(sb_config, "_cdp_bin_loc", None): test.test.binary_location = sb_config._cdp_bin_loc if self.options.use_chromium and not test.test.binary_location: test.test.binary_location = "_chromium_" elif self.options.use_cft and not test.test.binary_location: test.test.binary_location = "cft" elif self.options.use_chs and not test.test.binary_location: test.test.binary_location = "chs" sb_config.binary_location = test.test.binary_location if ( test.test.binary_location and test.test.binary_location.lower() == "chs" and test.test.browser == "chrome" ): test.test.headless = True test.test.headless1 = False test.test.headless2 = False if test.test.browser in constants.ChromiumSubs.chromium_subs: if not sb_config.binary_location: test.test.browser = "chrome" # Still uses chromedriver sb_config._browser_shortcut = test.test.browser test.test.driver_version = self.options.driver_version test.test.page_load_strategy = self.options.page_load_strategy test.test.chromium_arg = self.options.chromium_arg test.test.firefox_arg = self.options.firefox_arg test.test.firefox_pref = self.options.firefox_pref test.test.proxy_string = self.options.proxy_string test.test.proxy_bypass_list = self.options.proxy_bypass_list test.test.proxy_pac_url = self.options.proxy_pac_url test.test.multi_proxy = self.options.multi_proxy test.test.user_agent = self.options.user_agent test.test.mobile_emulator = self.options.mobile_emulator test.test.device_metrics = self.options.device_metrics test.test.time_limit = self.options.time_limit test.test.slow_mode = self.options.slow_mode test.test.demo_mode = self.options.demo_mode test.test.demo_sleep = self.options.demo_sleep test.test.highlights = self.options.highlights test.test.message_duration = self.options.message_duration test.test.js_checking_on = self.options.js_checking_on test.test.ad_block_on = self.options.ad_block_on test.test.host_resolver_rules = self.options.host_resolver_rules test.test.block_images = self.options.block_images test.test.do_not_track = self.options.do_not_track test.test.verify_delay = self.options.verify_delay # MasterQA test.test.esc_end = self.options.esc_end test.test.recorder_mode = self.options.recorder_mode test.test.recorder_ext = self.options.recorder_mode # Again test.test.rec_behave = self.options.rec_behave test.test.rec_print = self.options.rec_print test.test.record_sleep = self.options.record_sleep if self.options.rec_print: test.test.recorder_mode = True test.test.recorder_ext = True elif self.options.rec_behave: test.test.recorder_mode = True test.test.recorder_ext = True elif self.options.record_sleep: test.test.recorder_mode = True test.test.recorder_ext = True test.test.disable_cookies = self.options.disable_cookies test.test.disable_js = self.options.disable_js test.test.disable_csp = self.options.disable_csp test.test.disable_ws = self.options.disable_ws test.test.enable_ws = self.options.enable_ws if not self.options.disable_ws: test.test.enable_ws = True test.test.enable_sync = self.options.enable_sync test.test.use_auto_ext = self.options.use_auto_ext test.test.undetectable = self.options.undetectable test.test.uc_cdp_events = self.options.uc_cdp_events test.test.log_cdp_events = self.options.log_cdp_events if test.test.uc_cdp_events and not test.test.undetectable: test.test.undetectable = True test.test.uc_subprocess = self.options.uc_subprocess if test.test.uc_subprocess and not test.test.undetectable: test.test.undetectable = True test.test.no_sandbox = self.options.no_sandbox test.test.disable_gpu = self.options.disable_gpu test.test.remote_debug = self.options.remote_debug test.test.enable_3d_apis = self.options.enable_3d_apis test.test._swiftshader = self.options.swiftshader test.test.incognito = self.options.incognito test.test.guest_mode = self.options.guest_mode test.test.dark_mode = self.options.dark_mode test.test.devtools = self.options.devtools test.test._disable_beforeunload = self.options._disable_beforeunload test.test.window_position = self.options.window_position test.test.window_size = self.options.window_size test.test.maximize_option = self.options.maximize_option if self.options.save_screenshot and self.options.no_screenshot: self.options.save_screenshot = False # no_screenshot has priority test.test.save_screenshot_after_test = self.options.save_screenshot test.test.no_screenshot_after_test = self.options.no_screenshot test.test.visual_baseline = self.options.visual_baseline test.test.use_wire = self.options.use_wire test.test.external_pdf = self.options.external_pdf test.test.timeout_multiplier = self.options.timeout_multiplier test.test.dashboard = False test.test._multithreaded = False test.test._reuse_session = False sb_config.recorder_mode = test.test.recorder_mode sb_config.no_screenshot = test.test.no_screenshot_after_test if test.test.servername != "localhost": # Using Selenium Grid # (Set --server="127.0.0.1" for localhost Grid) if str(self.options.port) == "443": test.test.protocol = "https" if ( shared_utils.is_linux() and not self.options.headed and not self.options.headless and not self.options.headless2 and not self.options.xvfb ): if not self.options.undetectable: print( "(Linux uses --headless by default. " "To override, use --headed / --gui. " "For Xvfb mode instead, use --xvfb. " "Or you can hide this info by using " "--headless / --headless2 / --uc.)" ) self.options.headless = True test.test.headless = True else: self.options.xvfb = True test.test.xvfb = True if self.options.use_wire and self.options.undetectable: print( "\n" "SeleniumBase doesn't support mixing --uc with --wire mode.\n" "If you need both, override get_new_driver() from BaseCase:\n" "https://seleniumbase.io/help_docs/syntax_formats/#sb_sf_09\n" "(Only UC Mode without Wire Mode will be used for this run)\n" ) self.options.use_wire = False test.test.use_wire = False # Recorder Mode can still optimize scripts in --headless2 mode. if self.options.recorder_mode and self.options.headless: self.options.headless = False self.options.headless1 = False self.options.headless2 = True test.test.headless = False test.test.headless1 = False test.test.headless2 = True if not self.options.headless and not self.options.headless2: self.options.headed = True test.test.headed = True sb_config._virtual_display = None sb_config.headless_active = False self.headless_active = False if ( shared_utils.is_linux() and (not self.options.headed or self.options.xvfb) ): width = settings.HEADLESS_START_WIDTH height = settings.HEADLESS_START_HEIGHT with suppress(Exception): from sbvirtualdisplay import Display self._xvfb_display = Display(visible=0, size=(width, height)) self._xvfb_display.start() sb_config._virtual_display = self._xvfb_display self.headless_active = True sb_config.headless_active = True sb_config._is_timeout_changed = False sb_config._SMALL_TIMEOUT = settings.SMALL_TIMEOUT sb_config._LARGE_TIMEOUT = settings.LARGE_TIMEOUT sb_config._context_of_runner = False # Context Manager Compatibility sb_config.mobile_emulator = self.options.mobile_emulator sb_config.proxy_driver = self.options.proxy_driver sb_config.multi_proxy = self.options.multi_proxy # The driver will be received later self.driver = None test.test.driver = self.driver def finalize(self, result): """This runs after all tests have completed with nosetests.""" if not getattr(sb_config, "multi_proxy", None): proxy_helper.remove_proxy_zip_if_present() def afterTest(self, test): try: # If the browser window is still open, close it now. if ( not shared_utils.is_windows() or test.test.browser == "ie" or self.driver.service.process ): self.driver.quit() except AttributeError: pass except Exception: pass with suppress(Exception): if ( getattr(self, "_xvfb_display", None) and hasattr(self._xvfb_display, "stop") ): self.headless_active = False sb_config.headless_active = False self._xvfb_display.stop() self._xvfb_display = None if ( getattr(sb_config, "_virtual_display", None) and hasattr(sb_config._virtual_display, "stop") ): sb_config._virtual_display.stop() sb_config._virtual_display = None ================================================ FILE: seleniumbase/resources/ReadMe.md ================================================ ## [](https://github.com/seleniumbase/SeleniumBase/) Resource Files SeleniumBase uses JavaScript libraries for bonus features such as the Website Tour Maker, Presentation Maker, Chart Maker, Demo Mode, HTML Inspector, and more. In general, SeleniumBase retrieves these resources via CDN link. **favicon.ico** - This file is used by [style_sheet.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/core/style_sheet.py) for the favicon icon. Currently, SeleniumBase uses the version at [https://raw.githubusercontent.com/seleniumbase/SeleniumBase/master/seleniumbase/resources/favicon.ico](https://raw.githubusercontent.com/seleniumbase/SeleniumBase/master/seleniumbase/resources/favicon.ico). -------- The remaining resources have been moved into [github.com/seleniumbase/resource-files](https://github.com/seleniumbase/resource-files) in order to reduce the size of SeleniumBase: **messenger/** - Files in this folder are used for creating JavaScript notifications during test runs in Demo Mode. **jquery_confirm/** - Files in this folder are used for creating JavaScript confirmation prompts during test runs when using MasterQA. **html_inspector/** - Files in this folder are used for the HTML Inspector, which validates website pages. **reveal/** - Files in this folder are used for the HTML Presentation Maker. **prettify/** - Files in this folder are used to assist the HTML Presentation Maker. **shepherd/** - Files in this folder are used for creating website tours using the Shepherd JavaScript library. **bootstrap_tour/** - Files in this folder are used for creating website tours using the Bootstrap Tour JavaScript library. **introjs/** - Files in this folder are used for creating website tours using the IntroJS JavaScript library. **driverjs/** - Files in this folder are used for creating website tours using the DriverJS JavaScript library. **hopscotch/** - Files in this folder are used for creating website tours using the Hopscotch JavaScript library. ================================================ FILE: seleniumbase/resources/__init__.py ================================================ ================================================ FILE: seleniumbase/translate/__init__.py ================================================ from seleniumbase.translate import chinese # noqa from seleniumbase.translate import dutch # noqa from seleniumbase.translate import french # noqa from seleniumbase.translate import italian # noqa from seleniumbase.translate import japanese # noqa from seleniumbase.translate import korean # noqa from seleniumbase.translate import portuguese # noqa from seleniumbase.translate import russian # noqa from seleniumbase.translate import spanish # noqa ================================================ FILE: seleniumbase/translate/chinese.py ================================================ # Chinese / 中文 - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class 硒测试用例(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Chinese" def 开启(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def 开启网址(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def 单击(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def 双击(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def 上下文点击(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def 慢单击(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def 如果可见请单击(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def JS如果存在请单击(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def 单击链接文本(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def 鼠标点击偏移(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def 更新文本(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def 输入文本(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def 添加文本(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def 获取文本(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def 断言文本(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def 确切断言文本(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def 断言链接文本(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def 断言非空文本(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def 断言文本不可见(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def 断言元素(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def 断言元素可见(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def 断言元素不可见(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def 断言元素存在(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def 断言元素不存在(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def 断言属性(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def 断言URL(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def 断言URL包含(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def 断言标题(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def 断言标题包含(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def 获取标题(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def 断言为真(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def 断言为假(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def 断言等于(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def 断言不等于(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def 刷新页面(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def 获取当前网址(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def 获取页面源代码(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def 回去(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def 向前(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def 文本是否显示(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def 确切文本是否显示(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def 元素是否可见(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def 元素是否启用(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def 元素是否存在(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def 等待文本(self, *args, **kwargs): # wait_for_text(text, selector="html") return self.wait_for_text(*args, **kwargs) def 等待元素(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def 等待元素可见(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def 等待元素不可见(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def 等待元素存在(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def 等待元素不存在(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def 等待属性(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def 等待页面加载完成(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def 睡(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def 等待(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def 提交(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def 清除(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def 专注于(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def JS单击(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def JS更新文本(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def JS输入文本(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def JQUERY单击(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def JQUERY更新文本(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def JQUERY输入文本(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def 检查HTML(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def 保存截图(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def 保存截图到日志(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def 选择文件(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def 执行脚本(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def 安全执行脚本(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def 加载JQUERY(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def 加载RECORDER(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def 开启如果不网址(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def 阻止广告(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def 跳过(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def 检查断开的链接(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def 检查JS错误(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def 切换到帧(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def 切换到默认内容(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def 切换到父框架(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def 打开新窗口(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def 切换到窗口(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def 切换到默认窗口(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def 切换到最新的窗口(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def 最大化窗口(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def 亮点(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def 亮点单击(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def 滚动到(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def 滚动到顶部(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def 滚动到底部(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def 鼠标悬停并单击(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def 鼠标悬停(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def 是否被选中(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def 按向上箭头(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def 按向下箭头(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def 按向左箭头(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def 按向右箭头(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def 单击可见元素(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def 按文本选择选项(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def 按索引选择选项(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def 按值选择选项(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def 创建演示文稿(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def 添加幻灯片(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def 保存演示文稿(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def 开始演示文稿(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def 创建饼图(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def 创建条形图(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def 创建柱形图(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def 创建折线图(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def 创建面积图(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def 将系列添加到图表(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def 添加数据点(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def 保存图表(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def 显示图表(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def 提取图表(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def 创建游览(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def 创建SHEPHERD游览(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def 创建BOOTSTRAP游览(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def 创建DRIVERJS游览(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def 创建HOPSCOTCH游览(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def 创建INTROJS游览(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def 添加游览步骤(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def 播放游览(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def 导出游览(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def 获取PDF文本(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def 断言PDF文本(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def 下载文件(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def 下载的文件是否存在(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def 获取下载的文件路径(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def 检查下载的文件(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def 删除下载的文件(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def 失败(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def 获取(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def 访问(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def 访问网址(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def 获取元素(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def 查找元素(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def 删除第一个元素(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def 删除所有元素(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def 查找文本(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def 设置文本(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def 获取属性(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def 设置属性(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def 设置所有属性(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def 写文本(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def 设置消息主题(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def 显示讯息(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def 打印(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def 推迟断言元素(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def 推迟断言文本(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def 处理推迟断言(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def 接受警报(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def 解除警报(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def 切换到警报(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def 拖放(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def 设置HTML(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def 加载HTML文件(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def 打开HTML文件(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def 删除所有COOKIE(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def 获取用户代理(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def 获取语言代码(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_中文(MasterQA, 硒测试用例): def 校验(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "手动检查" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "页面是否看起来不错?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/dutch.py ================================================ # Dutch / Nederlands - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class Testgeval(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Dutch" def openen(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def url_openen(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def klik(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def dubbelklik(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def contextklik(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def klik_langzaam(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def klik_indien_zichtbaar(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def js_klik_indien_aanwezig(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def klik_linktekst(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def klik_op_locatie(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def tekst_bijwerken(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def typ(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def tekst_toevoegen(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def tekst_ophalen(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def controleren_tekst(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def controleren_exacte_tekst(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def controleren_linktekst(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def controleren_niet_lege_tekst(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def controleren_tekst_niet_zichtbaar(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def controleren_element(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def controleren_element_zichtbaar(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def controleren_element_niet_zichtbaar(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def controleren_element_aanwezig(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def controleren_element_afwezig(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def controleren_attribuut(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def controleren_url(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def controleren_url_bevat(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def controleren_titel(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def controleren_titel_bevat(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def titel_ophalen(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def controleren_ware(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def controleren_valse(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def controleren_gelijk(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def controleren_niet_gelijk(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def ververs_pagina(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def huidige_url_ophalen(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def broncode_ophalen(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def terug(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def vooruit(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def tekst_zichtbaar(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def exacte_tekst_zichtbaar(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def element_zichtbaar(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def element_ingeschakeld(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def element_aanwezig(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def wachten_op_tekst(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def wachten_op_element(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def wachten_op_element_zichtbaar(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def wachten_op_element_niet_zichtbaar(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def wachten_op_element_aanwezig(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def wachten_op_element_afwezig(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def wachten_op_attribuut(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def wacht_tot_de_pagina_is_geladen(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def slapen(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def wachten(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def verzenden(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def wissen(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def focussen(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def js_klik(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def js_tekst_bijwerken(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def js_typ(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def jquery_klik(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def jquery_tekst_bijwerken(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def jquery_typ(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def html_inspecteren(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def bewaar_screenshot(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def bewaar_screenshot_om_te_loggen(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def selecteer_bestand(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def script_uitvoeren(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def script_veilig_uitvoeren(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def activeer_jquery(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def activeer_recorder(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def openen_zo_niet_url(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def blokkeer_advertenties(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def overslaan(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def controleren_op_gebroken_links(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def controleren_op_js_fouten(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def overschakelen_naar_frame(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def overschakelen_naar_standaardcontent(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def overschakelen_naar_bovenliggend_frame(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def nieuw_venster_openen(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def overschakelen_naar_venster(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def overschakelen_naar_standaardvenster(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def overschakelen_naar_nieuwste_venster(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def venster_maximaliseren(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def markeren(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def markeren_klik(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def scrollen_naar(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def naar_boven_scrollen(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def naar_beneden_scrollen(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def zweven_en_klik(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def zweven(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def is_het_geselecteerd(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def druk_op_pijl_omhoog(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def druk_op_pijl_omlaag(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def druk_op_pijl_links(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def druk_op_pijl_rechts(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def klik_zichtbare_elementen(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def optie_selecteren_op_tekst(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def optie_selecteren_op_index(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def optie_selecteren_op_waarde(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def maak_een_presentatie(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def een_dia_toevoegen(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def de_presentatie_opslaan(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def de_presentatie_starten(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def maak_een_cirkeldiagram(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def maak_een_staafdiagram(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def maak_een_kolomdiagram(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def maak_een_lijndiagram(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def maak_een_vlakdiagram(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def reeksen_toevoegen_aan_grafiek(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def gegevenspunt_toevoegen(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def grafiek_opslaan(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def grafiek_weergeven(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def grafiek_uitpakken(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def maak_een_tour(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def maak_een_shepherd_tour(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def maak_een_bootstrap_tour(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def maak_een_driverjs_tour(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def maak_een_hopscotch_tour(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def maak_een_introjs_tour(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def toevoegen_tour_stap(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def speel_de_tour(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def de_tour_exporteren(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def pdf_tekst_ophalen(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def controleren_pdf_tekst(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def bestand_downloaden(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def gedownloade_bestand_aanwezig(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def pad_gedownloade_bestand_ophalen(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def controleren_gedownloade_bestand(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def verwijder_gedownloade_bestand(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def mislukken(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def ophalen(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def bezoek(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def bezoek_url(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def element_ophalen(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def vind_element(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def verwijder_element(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def verwijder_elementen(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def vind_tekst(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def tekst_instellen(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def attribuut_ophalen(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def attribuut_instellen(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def attributen_instellen(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def schrijven(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def thema_van_bericht_instellen(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def bericht_weergeven(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def afdrukken(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def uitgestelde_controleren_element(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def uitgestelde_controleren_tekst(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def verwerken_uitgestelde_controleren(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def waarschuwing_accepteren(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def waarschuwing_wegsturen(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def overschakelen_naar_waarschuwing(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def slepen_en_neerzetten(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def html_instellen(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def html_bestand_laden(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def html_bestand_openen(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def alle_cookies_verwijderen(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def gebruikersagent_ophalen(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def taalcode_ophalen(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_Nederlands(MasterQA, Testgeval): def controleren(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "Handmatige controle" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "Ziet de pagina er goed uit?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/french.py ================================================ # French / Français - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class CasDeBase(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "French" def ouvrir(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def ouvrir_url(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def cliquer(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def double_cliquer(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def contextuel_cliquer(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def cliquer_lentement(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def cliquer_si_affiché(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def js_cliquer_si_présent(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def cliquer_texte_du_lien(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def cliquer_emplacement(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def modifier_texte(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def taper(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def ajouter_texte(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def obtenir_texte(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def vérifier_texte(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def vérifier_texte_exactement(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def vérifier_texte_du_lien(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def vérifier_texte_non_vide(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def vérifier_texte_pas_affiché(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def vérifier_élément(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def vérifier_élément_affiché(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def vérifier_élément_pas_affiché(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def vérifier_élément_présent(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def vérifier_élément_pas_présent(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def vérifier_attribut(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def vérifier_url(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def vérifier_url_contient(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def vérifier_titre(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def vérifier_titre_contient(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def obtenir_titre(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def vérifier_vrai(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def vérifier_faux(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def vérifier_égal(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def vérifier_non_égal(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def rafraîchir_la_page(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def obtenir_url_actuelle(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def obtenir_html_de_la_page(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def retour(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def en_avant(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def est_texte_affiché(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def est_texte_exactement_affiché(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def est_un_élément_affiché(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def est_un_élément_activé(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def est_un_élément_présent(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def attendre_le_texte(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def attendre_un_élément(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def attendre_un_élément_affiché(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def attendre_un_élément_pas_affiché(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def attendre_un_élément_présent(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def attendre_un_élément_pas_présent(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def attendre_un_attribut(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def attendre_que_la_page_se_charge(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def dormir(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def attendre(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def soumettre(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def effacer(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def concentrer(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def js_cliquer(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def js_modifier_texte(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def js_taper(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def jquery_cliquer(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def jquery_modifier_texte(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def jquery_taper(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def vérifier_html(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def enregistrer_capture_d_écran(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def enregistrer_capture_d_écran_aux_logs(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def sélectionner_fichier(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def exécuter_script(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def exécuter_script_sans_risque(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def activer_jquery(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def activer_recorder(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def ouvrir_si_non_url(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def annonces_de_bloc(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def sauter(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def vérifier_les_liens_rompus(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def vérifier_les_erreurs_js(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def passer_au_cadre(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def passer_au_contenu_par_défaut(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def passer_au_cadre_parent(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def ouvrir_une_nouvelle_fenêtre(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def passer_à_fenêtre(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def passer_à_fenêtre_par_défaut(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def passer_à_fenêtre_dernière(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def maximiser_fenêtre(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def illuminer(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def illuminer_cliquer(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def déménager_à(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def faites_défiler_vers_le_haut(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def faites_défiler_vers_le_bas(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def passer_la_souris_et_cliquer(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def survol_de_la_souris(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def est_il_sélectionné(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def appuyer_sur_flèche_haut(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def appuyer_sur_flèche_bas(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def appuyer_sur_flèche_gauche(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def appuyer_sur_flèche_droite(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def cliquer_éléments_visibles(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def sélectionner_option_par_texte(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def sélectionner_option_par_index(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def sélectionner_option_par_valeur(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def créer_une_présentation(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def ajouter_une_diapositive(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def enregistrer_la_présentation(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def démarrer_la_présentation(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def créer_un_graphique_à_secteurs(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def créer_un_graphique_à_barres(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def créer_un_graphique_à_colonnes(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def créer_un_graphique_linéaire(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def créer_un_graphique_en_aires(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def ajouter_séries_au_graphique(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def ajouter_un_point_de_données(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def enregistrer_le_graphique(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def afficher_le_graphique(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def extraire_le_graphique(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def créer_une_visite(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def créer_une_visite_shepherd(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def créer_une_visite_bootstrap(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def créer_une_visite_driverjs(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def créer_une_visite_hopscotch(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def créer_une_visite_introjs(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def ajouter_étape_à_la_visite(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def jouer_la_visite(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def exporter_la_visite(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def obtenir_texte_pdf(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def vérifier_texte_pdf(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def télécharger_fichier(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def est_un_fichier_téléchargé_présent(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def obtenir_chemin_du_fichier_téléchargé(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def vérifier_fichier_téléchargé(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def supprimer_fichier_téléchargé(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def échouer(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def obtenir(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def visiter(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def visiter_url(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def obtenir_élément(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def trouver_élément(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def supprimer_élément(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def supprimer_éléments(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def trouver_texte(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def définir_texte(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def obtenir_attribut(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def définir_attribut(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def définir_attributs(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def écriver(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def définir_thème_du_message(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def afficher_message(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def imprimer(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def reporté_vérifier_élément(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def reporté_vérifier_texte(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def effectuer_vérifications_reportées(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def accepter_alerte(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def rejeter_alerte(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def passer_à_alerte(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def glisser_et_déposer(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def définir_html(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def charger_html_fichier(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def ouvrir_html_fichier(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def supprimer_tous_les_cookies(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def obtenir_agent_utilisateur(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def obtenir_code_de_langue(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_Français(MasterQA, CasDeBase): def vérifier(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "Vérification manuelle" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "La page est-elle bonne?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/italian.py ================================================ # Italian / Italiano - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class CasoDiProva(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Italian" def apri(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def apri_url(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def fare_clic(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def doppio_clic(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def clic_contestuale(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def clic_lentamente(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def clic_se_visto(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def js_clic_se_presente(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def clic_testo_del_collegamento(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def clic_su_posizione(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def aggiornare_testo(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def digitare(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def aggiungi_testo(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def ottenere_testo(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def verificare_testo(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def verificare_testo_esatto(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def verificare_testo_del_collegamento(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def verificare_testo_non_vuoto(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def verificare_testo_non_visto(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def verificare_elemento(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def verificare_elemento_visto(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def verificare_elemento_non_visto(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def verificare_elemento_presente(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def verificare_elemento_assente(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def verificare_attributo(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def verificare_url(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def verificare_url_contiene(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def verificare_titolo(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def verificare_titolo_contiene(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def ottenere_titolo(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def verificare_vero(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def verificare_falso(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def verificare_uguale(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def verificare_non_uguale(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def aggiorna_la_pagina(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def ottenere_url_corrente(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def ottenere_la_pagina_html(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def indietro(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def avanti(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def è_testo_visto(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def è_testo_esatto_visto(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def è_elemento_visto(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def è_elemento_abilitato(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def è_elemento_presente(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def attendere_il_testo(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def attendere_un_elemento(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def attendere_un_elemento_visto(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def attendere_un_elemento_non_visto(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def attendere_un_elemento_presente(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def attendere_un_elemento_assente(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def attendere_un_attributo(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def attendere_il_caricamento_della_pagina(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def dormire(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def attendere(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def inviare(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def cancellare(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def focalizzare(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def js_fare_clic(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def js_aggiornare_testo(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def js_digitare(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def jquery_fare_clic(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def jquery_aggiornare_testo(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def jquery_digitare(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def controlla_html(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def salva_screenshot(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def salva_screenshot_nei_logs(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def seleziona_file(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def eseguire_script(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def eseguire_script_sicuro(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def attiva_jquery(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def attiva_recorder(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def apri_se_non_url(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def bloccare_gli_annunci(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def saltare(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def verificare_i_collegamenti(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def controlla_errori_js(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def passa_alla_cornice(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def passa_al_contenuto_predefinito(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def passa_alla_cornice_principale(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def apri_una_nuova_finestra(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def passa_alla_finestra(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def passa_alla_finestra_predefinita(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def passa_alla_finestra_ultimo(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def ingrandisci_finestra(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def illuminare(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def illuminare_clic(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def scorrere_fino_a(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def scorri_verso_alto(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def scorri_verso_il_basso(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def passare_il_mouse_e_fare_clic(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def passaggio_del_mouse(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def è_selezionato(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def premere_la_freccia_su(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def premere_la_freccia_giù(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def premere_la_freccia_sinistra(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def premere_la_freccia_destra(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def clic_sugli_elementi_visibili(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def selezionare_opzione_per_testo(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def selezionare_opzione_per_indice(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def selezionare_opzione_per_valore(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def creare_una_presentazione(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def aggiungere_una_diapositiva(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def salva_la_presentazione(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def avviare_la_presentazione(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def creare_un_grafico_a_torta(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def creare_un_grafico_a_barre(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def creare_un_grafico_a_colonne(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def creare_un_grafico_a_linee(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def creare_un_grafico_ad_area(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def aggiungere_serie_al_grafico(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def aggiungi_punto_dati(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def salva_il_grafico(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def mostra_il_grafico(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def estrarre_il_grafico(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def creare_un_tour(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def creare_un_tour_shepherd(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def creare_un_tour_bootstrap(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def creare_un_tour_driverjs(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def creare_un_tour_hopscotch(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def creare_un_tour_introjs(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def aggiungere_passo_al_tour(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def riprodurre_il_tour(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def esportare_il_tour(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def ottenere_testo_pdf(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def verificare_testo_pdf(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def scaricare_file(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def è_file_scaricato_presente(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def ottenere_percorso_del_file_scaricato(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def verificare_file_scaricato(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def eliminare_file_scaricato(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def fallire(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def ottenere(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def visita(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def visita_url(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def ottenere_elemento(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def trovare_elemento(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def rimuovere_elemento(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def rimuovere_elementi(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def trovare_testo(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def impostare_testo(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def ottenere_attributo(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def imposta_attributo(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def impostare_gli_attributi(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def scrivere(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def impostare_tema_del_messaggio(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def visualizza_messaggio(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def stampare(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def differita_verificare_elemento(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def differita_verificare_testo(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def elaborare_differita_verificari(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def accetta_avviso(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def elimina_avviso(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def passa_al_avviso(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def trascinare_e_rilasciare(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def impostare_html(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def caricare_html_file(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def apri_html_file(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def elimina_tutti_i_cookie(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def ottenere_agente_utente(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def ottenere_codice_lingua(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_Italiano(MasterQA, CasoDiProva): def verificare(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "Controllo manuale" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "La pagina ha un bell'aspetto?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/japanese.py ================================================ # Japanese / 日本語 - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class セレニウムテストケース(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Japanese" def を開く(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def URLを開く(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def クリックして(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def ダブルクリックして(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def コンテキストクリック(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def ゆっくりクリックして(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def 表示されている場合はクリック(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def 存在する場合はJSクリック(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def リンクテキストをクリックします(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def オフセットでクリック(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def テキストを更新(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def 入力(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def テキストを追加(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def テキストを取得(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def テキストを確認する(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def 正確なテキストを確認する(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def リンクテキストを確認する(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def 空ではないテキストを確認する(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def テキが表示されていないことを確認します(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def 要素を確認する(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def 要素が表示されていることを確認(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def 要素が表示されていないことを確認します(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def 要素が存在することを確認します(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def 要素が存在しないことを確認します(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def 属性を確認する(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def URLを確認する(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def URL部分文字列を確認する(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def タイトルを確認(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def タイトル部分文字列を確認する(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def タイトルを取得する(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def 検証が正しい(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def 検証は偽です(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def 検証が等しい(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def 検証が等しくない(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def ページを更新する(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def 現在のURLを取得(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def ページのソースコードを取得する(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def 戻る(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def 進む(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def テキストが表示されています(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def 正確なテキストが表示されています(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def 要素は表示されますか(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def 要素が有効かどうか(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def 要素が存在するかどうか(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def テキストを待つ(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def 要素を待つ(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def 要素が表示されるのを待ちます(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def 要素が表示されなくなるまで待ちます(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def 要素が存在するのを待つ(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def 要素が存在しないのを待つ(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def 属性を待つ(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def ページがロードされるのを待ちます(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def 眠る(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def 待つ(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def を提出す(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def クリアする(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def 集中する(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def JSクリックして(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def JSテキストを更新(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def JS入力(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def JQUERYクリックして(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def JQUERYテキストを更新(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def JQUERY入力(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def HTMLをチェック(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def スクリーンショットを保存(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def ログにスクリーンショットを保存(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def ファイルを選択(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def スクリプトを実行する(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def スクリプトを安全に実行する(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def JQUERYを読み込む(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def RECORDERを読み込む(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def URLでない場合は開く(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def ブロック広告(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def スキップ(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def リンク切れを確認する(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def JSエラーを確認する(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def フレームに切り替えます(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def デフォルトのコンテンツに切り替える(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def 親フレームに切り替えます(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def 新しいウィンドウを開く(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def ウィンドウに切り替え(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def デフォルトのウィンドウに切り替える(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def 最新のウィンドウに切り替えます(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def ウィンドウを最大化する(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def ハイライト(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def ハイライトしてクリックして(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def スクロールして(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def 一番上までスクロール(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def 一番下までスクロール(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def マウスオーバーしてクリック(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def マウスオーバー(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def 選択されていることを(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def 上矢印を押します(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def 下矢印を押します(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def 左矢印を押します(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def 右矢印を押します(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def 表示要素をクリックします(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def テキストでオプションを選択(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def インデックスでオプションを選択(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def 値でオプションを選択(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def プレゼンテーションを作成する(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def スライドを追加する(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def プレゼンテーションを保存する(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def プレゼンテーションを開始する(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def 円グラフを作成する(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def 棒グラフを作成する(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def 縦棒グラフを作成する(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def 折れ線グラフを作成する(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def 面グラフを作成する(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def グラフに系列を追加する(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def データポイントを追加する(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def グラフを保存する(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def グラフを表示する(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def グラフを抽出する(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def ツアーを作成する(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def SHEPHERDツアーを作成する(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def BOOTSTRAPツアーを作成する(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def DRIVERJSツアーを作成する(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def HOPSCOTCHツアーを作成する(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def INTROJSツアーを作成する(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def ツアーステップを追加する(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def ツアーを再生する(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def ツアーをエクスポートする(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def PDFテキストを取得(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def PDFテキストを確認する(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def ファイルをダウンロード(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def ダウンロードしたファイルが存在するかどうか(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def ダウンロードしたファイルパスを取得する(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def ダウンロードしたファイルを確認する(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def ダウンロードしたファイルを削除する(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def 失敗(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def を取得する(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def を訪問(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def URLを訪問(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def 要素を取得する(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def 要素を見つける(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def 最初の要素を削除(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def すべての要素を削除(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def テキストを見つける(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def テキストを設定する(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def 属性を取得する(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def 属性を設定する(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def すべての属性を設定(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def 書く(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def メッセージのスタイルを設定する(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def メッセージを表示する(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def 印刷(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def を延期する要素を確認する(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def を延期するテキストを確認する(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def 遅延アサーションの処理(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def アラートを受け入れる(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def アラートを却下(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def アラートに切り替え(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def ドラッグアンドドロップ(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def HTML設定する(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def HTMLファイルを読み込む(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def HTMLファイルを開く(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def すべてのクッキーを削除する(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def ユーザーエージェントの取得(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def 言語コードを取得する(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_日本語(MasterQA, セレニウムテストケース): def を確認する(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "手動チェック" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "ページは見栄えが良いですか?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/korean.py ================================================ # Korean / 한국어 - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class 셀레늄_테스트_케이스(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Korean" def 열기(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def URL_열기(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def 클릭(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def 더블_클릭(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def 컨텍스트_클릭(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def 천천히_클릭(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def 보이는_경우_클릭(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def JS_존재하는지_경우_클릭(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def 링크_텍스트를_클릭합니다(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def 위치를_클릭(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def 텍스트를_업데이트(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def 입력(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def 텍스트를_추가(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def 텍스트를_검색(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def 텍스트_확인(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def 정확한_텍스트를_확인하는(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def 링크_텍스트_확인(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def 비어_있지_않은_텍스트_확인하는(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def 텍스트_보이지_않는지_확인(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def 요소_확인(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def 요소가_보이는지_확인(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def 요소가_보이지_않는지_확인(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def 요소가_존재하는지_확인(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def 요소가_존재하지_않는지_확인(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def 특성_확인(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def URL_확인(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def URL_부분_확인(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def 제목_확인(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def 제목_부분_확인(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def 제목_검색(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def 올바른지_확인(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def 거짓인지_확인(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def 동일한지_확인(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def 동일하지_않다고_어설션(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def 페이지_새로_고침(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def 현재의_URL을_가져(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def 페이지의_소스_코드를_얻을(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def 뒤로(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def 앞으로(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def 텍스트가_표시됩니다(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def 정확한_텍스트가_표시됩니다(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def 요소가_표시됩니다(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def 요소가_활성화돼(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def 요소가_있습니다(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def 텍스트가_나타날_때까지_기다립니다(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def 요소가_나타날_때까지_기다립니다(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def 요소가_표시_될_때까지_기다립니다(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def 요소가_사라질_때까지_기다리십시오(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def 요소가_존재할_때까지_기다립니다(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def 요소가_나타날_때까지_기다리십시오(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def 특성_때까지_기다립니다(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def 페이지가_로드될_때까지_기다립니다(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def 잠을(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def 기다림(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def 제출(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def 지우려면(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def 집중하다(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def JS_클릭(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def JS_텍스트를_업데이트(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def JS_입력(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def JQUERY_클릭(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def JQUERY_텍스트를_업데이트(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def JQUERY_입력(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def HTML_확인(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def 스크린_샷_저장(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def 로그에_스크린_샷_저장(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def 파일을_선택(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def 스크립트를_실행하려면(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def 스크립트를_안전하게_실행(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def JQUERY_로드(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def RECORDER_로드(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def URL_이_아닌_경우_열기(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def 광고_차단(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def 건너뛸(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def 끊어진_링크_확인(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def JS_오류_확인(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def 프레임으로_전환(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def 기본_콘텐츠로_전환(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def 상위_프레임으로_전환(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def 새_창_열기(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def 창으로_전환(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def 기본_창으로_전환(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def 최신_창으로_전환(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def 창_최대화(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def 강조(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def 강조_클릭(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def 요소로_스크롤(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def 맨_위로_스크롤(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def 하단으로_스크롤(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def 마우스오버_및_클릭(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def 마우스오버(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def 선택되어_있는지(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def 위쪽_화살표를_누릅니다(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def 아래쪽_화살표를_누르십시오(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def 왼쪽_화살표를_누르십시오(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def 오른쪽_화살표를_누르십시오(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def 페이지_요소를_클릭_합니다(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def 텍스트로_옵션_선택(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def 인덱스별로_옵션_선택(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def 값별로_옵션_선택(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def 프레젠테이션_만들기(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def 슬라이드_추가(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def 프레젠테이션_저장(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def 프레젠테이션_시작(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def 원형_차트_만들기(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def 막대_차트_만들기(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def 열_차트_만들기(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def 선_차트_만들기(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def 영역_차트_만들기(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def 차트에_시리즈_추가(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def 데이터_포인트_추가(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def 차트_저장(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def 차트_표시(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def 차트_추출(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def 가이드_투어_만들기(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def 가이드_SHEPHERD_투어_만들기(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def 가이드_BOOTSTRAP_투어_만들기(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def 가이드_DRIVERJS_투어_만들기(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def 가이드_HOPSCOTCH_투어_만들기(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def 가이드_INTROJS_투어_만들기(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def 둘러보기_단계_추가(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def 가이드_투어를하다(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def 가이드_투어_내보내기(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def PDF_텍스트를_검색(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def PDF_텍스트_확인(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def 파일_다운로드(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def 다운로드한_파일이_있습니다(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def 다운로드한_파일_경로_가져_오기(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def 다운로드한_파일_확인(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def 다운로드한_파일_삭제(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def 실패(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def 받기(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def 방문(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def 방문_URL(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def 요소_검색(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def 요소를_찾을(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def 첫_번째_요소_제거(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def 모든_요소_제거(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def 텍스트_찾기(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def 텍스트_설정(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def 특성_검색(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def 특성_설정(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def 모든_특성_설정(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def 쓰다(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def 메시지_테마_설정(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def 메시지를_표시(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def 인쇄(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def 연기된_요소_확인(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def 연기된_텍스트_확인(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def 연기된_검증_처리(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def 경고를_수락(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def 경고를_거부(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def 경고로_전환(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def 드래그_앤_드롭(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def HTML_설정(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def HTML_파일_로드(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def HTML_파일_열기(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def 모든_쿠키_삭제(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def 사용자_에이전트_가져_오기(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def 언어_코드를_얻을(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_한국어(MasterQA, 셀레늄_테스트_케이스): def 확인(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "수동 검사" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "페이지가 잘 보이나요?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/master_dict.py ================================================ """ Master Dictionary Translations 0: English 1: Chinese 2: Dutch 3: French 4: Italian 5: Japanese 6: Korean 7: Portuguese 8: Russian 9: Spanish """ class MD_F: # Master Dictionary Functions def get_languages_list(): languages = [] languages.append("English") languages.append("Chinese") languages.append("Dutch") languages.append("French") languages.append("Italian") languages.append("Japanese") languages.append("Korean") languages.append("Portuguese") languages.append("Russian") languages.append("Spanish") return languages def get_parent_classes_list(): parent_classes = [] parent_classes.append("BaseCase") parent_classes.append("硒测试用例") parent_classes.append("Testgeval") parent_classes.append("CasDeBase") parent_classes.append("CasoDiProva") parent_classes.append("セレニウムテストケース") parent_classes.append("셀레늄_테스트_케이스") parent_classes.append("CasoDeTeste") parent_classes.append("ТестНаСелен") parent_classes.append("CasoDePrueba") return parent_classes def get_masterqa_parent_classes_list(): parent_classes = [] parent_classes.append("MasterQA") parent_classes.append("MasterQA_中文") parent_classes.append("MasterQA_Nederlands") parent_classes.append("MasterQA_Français") parent_classes.append("MasterQA_Italiano") parent_classes.append("MasterQA_日本語") parent_classes.append("MasterQA_한국어") parent_classes.append("MasterQA_Português") parent_classes.append("MasterQA_Русский") parent_classes.append("MasterQA_Español") return parent_classes def get_parent_class_lang(parent_class): parent_class_lang = {} parent_class_lang["BaseCase"] = "English" parent_class_lang["硒测试用例"] = "Chinese" parent_class_lang["Testgeval"] = "Dutch" parent_class_lang["CasDeBase"] = "French" parent_class_lang["CasoDiProva"] = "Italian" parent_class_lang["セレニウムテストケース"] = "Japanese" parent_class_lang["셀레늄_테스트_케이스"] = "Korean" parent_class_lang["CasoDeTeste"] = "Portuguese" parent_class_lang["ТестНаСелен"] = "Russian" parent_class_lang["CasoDePrueba"] = "Spanish" if parent_class not in parent_class_lang.keys(): raise Exception( "Invalid parent_class {%s} not in {%s}!" % (parent_class, parent_class_lang.keys()) ) return parent_class_lang[parent_class] def get_mqa_par_class_lang(parent_class): parent_class_lang = {} parent_class_lang["MasterQA"] = "English" parent_class_lang["MasterQA_中文"] = "Chinese" parent_class_lang["MasterQA_Nederlands"] = "Dutch" parent_class_lang["MasterQA_Français"] = "French" parent_class_lang["MasterQA_Italiano"] = "Italian" parent_class_lang["MasterQA_日本語"] = "Japanese" parent_class_lang["MasterQA_한국어"] = "Korean" parent_class_lang["MasterQA_Português"] = "Portuguese" parent_class_lang["MasterQA_Русский"] = "Russian" parent_class_lang["MasterQA_Español"] = "Spanish" if parent_class not in parent_class_lang.keys(): raise Exception( "Invalid parent_class {%s} not in {%s}!" % (parent_class, parent_class_lang.keys()) ) return parent_class_lang[parent_class] def get_lang_parent_class(language): lang_parent_class = {} lang_parent_class["English"] = "BaseCase" lang_parent_class["Chinese"] = "硒测试用例" lang_parent_class["Dutch"] = "Testgeval" lang_parent_class["French"] = "CasDeBase" lang_parent_class["Italian"] = "CasoDiProva" lang_parent_class["Japanese"] = "セレニウムテストケース" lang_parent_class["Korean"] = "셀레늄_테스트_케이스" lang_parent_class["Portuguese"] = "CasoDeTeste" lang_parent_class["Russian"] = "ТестНаСелен" lang_parent_class["Spanish"] = "CasoDePrueba" if language not in lang_parent_class.keys(): raise Exception( "Invalid language {%s} not in {%s}!" % (language, lang_parent_class.keys()) ) return lang_parent_class[language] def get_mqa_lang_par_class(language): lang_parent_class = {} lang_parent_class["English"] = "MasterQA" lang_parent_class["Chinese"] = "MasterQA_中文" lang_parent_class["Dutch"] = "MasterQA_Nederlands" lang_parent_class["French"] = "MasterQA_Français" lang_parent_class["Italian"] = "MasterQA_Italiano" lang_parent_class["Japanese"] = "MasterQA_日本語" lang_parent_class["Korean"] = "MasterQA_한국어" lang_parent_class["Portuguese"] = "MasterQA_Português" lang_parent_class["Russian"] = "MasterQA_Русский" lang_parent_class["Spanish"] = "MasterQA_Español" if language not in lang_parent_class.keys(): raise Exception( "Invalid language {%s} not in {%s}!" % (language, lang_parent_class.keys()) ) return lang_parent_class[language] def get_import_line(language): import_line = {} # - The Default Import Line: import_line["English"] = "from seleniumbase import BaseCase" # - Translated Import Lines: import_line[ "Chinese" ] = "from seleniumbase.translate.chinese import 硒测试用例" import_line[ "Dutch" ] = "from seleniumbase.translate.dutch import Testgeval" import_line[ "French" ] = "from seleniumbase.translate.french import CasDeBase" import_line[ "Italian" ] = "from seleniumbase.translate.italian import CasoDiProva" import_line[ "Japanese" ] = "from seleniumbase.translate.japanese import セレニウムテストケース" import_line[ "Korean" ] = "from seleniumbase.translate.korean import 셀레늄_테스트_케이스" import_line[ "Portuguese" ] = "from seleniumbase.translate.portuguese import CasoDeTeste" import_line[ "Russian" ] = "from seleniumbase.translate.russian import ТестНаСелен" import_line[ "Spanish" ] = "from seleniumbase.translate.spanish import CasoDePrueba" if language not in import_line.keys(): raise Exception( "Invalid language {%s} not in {%s}!" % (language, import_line.keys()) ) return import_line[language] def get_mqa_im_line(language): import_line = {} # - The Default Import Line: import_line["English"] = "from seleniumbase import MasterQA" # - Translated Import Lines: import_line[ "Chinese" ] = "from seleniumbase.translate.chinese import MasterQA_中文" import_line[ "Dutch" ] = "from seleniumbase.translate.dutch import MasterQA_Nederlands" import_line[ "French" ] = "from seleniumbase.translate.french import MasterQA_Français" import_line[ "Italian" ] = "from seleniumbase.translate.italian import MasterQA_Italiano" import_line[ "Japanese" ] = "from seleniumbase.translate.japanese import MasterQA_日本語" import_line[ "Korean" ] = "from seleniumbase.translate.korean import MasterQA_한국어" import_line[ "Portuguese" ] = "from seleniumbase.translate.portuguese import MasterQA_Português" import_line[ "Russian" ] = "from seleniumbase.translate.russian import MasterQA_Русский" import_line[ "Spanish" ] = "from seleniumbase.translate.spanish import MasterQA_Español" if language not in import_line.keys(): raise Exception( "Invalid language {%s} not in {%s}!" % (language, import_line.keys()) ) return import_line[language] def get_locale_code(language): locale_codes = {} locale_codes["English"] = "en" locale_codes["Chinese"] = "zh" locale_codes["Dutch"] = "nl" locale_codes["French"] = "fr" locale_codes["Italian"] = "it" locale_codes["Japanese"] = "ja" locale_codes["Korean"] = "ko" locale_codes["Portuguese"] = "pt" locale_codes["Russian"] = "ru" locale_codes["Spanish"] = "es" if language not in locale_codes.keys(): raise Exception( "Invalid language {%s} not in {%s}!" % (language, locale_codes.keys()) ) return locale_codes[language] def get_locale_list(): locale_list = [] locale_list.append("en") locale_list.append("zh") locale_list.append("nl") locale_list.append("fr") locale_list.append("it") locale_list.append("ja") locale_list.append("ko") locale_list.append("pt") locale_list.append("ru") locale_list.append("es") return locale_list class MD_L_Codes: # Master Dictionary Language Codes lang = {} lang["English"] = 0 lang["Chinese"] = 1 lang["Dutch"] = 2 lang["French"] = 3 lang["Italian"] = 4 lang["Japanese"] = 5 lang["Korean"] = 6 lang["Portuguese"] = 7 lang["Russian"] = 8 lang["Spanish"] = 9 class MD: # Master Dictionary md = {} num_langs = len(MD_L_Codes.lang) md["open"] = ["*"] * num_langs md["open"][0] = "open" md["open"][1] = "开启" md["open"][2] = "openen" md["open"][3] = "ouvrir" md["open"][4] = "apri" md["open"][5] = "を開く" md["open"][6] = "열기" md["open"][7] = "abrir" md["open"][8] = "открыть" md["open"][9] = "abrir" md["open_url"] = ["*"] * num_langs md["open_url"][0] = "open_url" md["open_url"][1] = "开启网址" md["open_url"][2] = "url_openen" md["open_url"][3] = "ouvrir_url" md["open_url"][4] = "apri_url" md["open_url"][5] = "URLを開く" md["open_url"][6] = "URL_열기" md["open_url"][7] = "abrir_url" md["open_url"][8] = "открыть_URL" md["open_url"][9] = "abrir_url" md["click"] = ["*"] * num_langs md["click"][0] = "click" md["click"][1] = "单击" md["click"][2] = "klik" md["click"][3] = "cliquer" md["click"][4] = "fare_clic" md["click"][5] = "クリックして" md["click"][6] = "클릭" md["click"][7] = "clique" md["click"][8] = "нажмите" md["click"][9] = "haga_clic" md["double_click"] = ["*"] * num_langs md["double_click"][0] = "double_click" md["double_click"][1] = "双击" md["double_click"][2] = "dubbelklik" md["double_click"][3] = "double_cliquer" md["double_click"][4] = "doppio_clic" md["double_click"][5] = "ダブルクリックして" md["double_click"][6] = "더블_클릭" md["double_click"][7] = "clique_duas_vezes" md["double_click"][8] = "дважды_нажмите" md["double_click"][9] = "doble_clic" md["context_click"] = ["*"] * num_langs md["context_click"][0] = "context_click" md["context_click"][1] = "上下文点击" md["context_click"][2] = "contextklik" md["context_click"][3] = "contextuel_cliquer" md["context_click"][4] = "clic_contestuale" md["context_click"][5] = "コンテキストクリック" md["context_click"][6] = "컨텍스트_클릭" md["context_click"][7] = "clique_de_contexto" md["context_click"][8] = "контекстный_щелчок" md["context_click"][9] = "clic_de_contexto" md["slow_click"] = ["*"] * num_langs md["slow_click"][0] = "slow_click" md["slow_click"][1] = "慢单击" md["slow_click"][2] = "klik_langzaam" md["slow_click"][3] = "cliquer_lentement" md["slow_click"][4] = "clic_lentamente" md["slow_click"][5] = "ゆっくりクリックして" md["slow_click"][6] = "천천히_클릭" md["slow_click"][7] = "clique_devagar" md["slow_click"][8] = "нажмите_медленно" md["slow_click"][9] = "clic_lentamente" md["click_if_visible"] = ["*"] * num_langs md["click_if_visible"][0] = "click_if_visible" md["click_if_visible"][1] = "如果可见请单击" md["click_if_visible"][2] = "klik_indien_zichtbaar" md["click_if_visible"][3] = "cliquer_si_affiché" md["click_if_visible"][4] = "clic_se_visto" md["click_if_visible"][5] = "表示されている場合はクリック" md["click_if_visible"][6] = "보이는_경우_클릭" md["click_if_visible"][7] = "clique_se_está_visível" md["click_if_visible"][8] = "нажмите_если_виден" md["click_if_visible"][9] = "clic_si_está_muestra" md["js_click_if_present"] = ["*"] * num_langs md["js_click_if_present"][0] = "js_click_if_present" md["js_click_if_present"][1] = "JS如果存在请单击" md["js_click_if_present"][2] = "js_klik_indien_aanwezig" md["js_click_if_present"][3] = "js_cliquer_si_présent" md["js_click_if_present"][4] = "js_clic_se_presente" md["js_click_if_present"][5] = "存在する場合はJSクリック" md["js_click_if_present"][6] = "JS_존재하는지_경우_클릭" md["js_click_if_present"][7] = "js_clique_se_está_presente" md["js_click_if_present"][8] = "JS_нажмите_если_присутствует" md["js_click_if_present"][9] = "js_clic_si_está_presente" md["click_link"] = ["*"] * num_langs md["click_link"][0] = "click_link" md["click_link"][1] = "单击链接文本" md["click_link"][2] = "klik_linktekst" md["click_link"][3] = "cliquer_texte_du_lien" md["click_link"][4] = "clic_testo_del_collegamento" md["click_link"][5] = "リンクテキストをクリックします" md["click_link"][6] = "링크_텍스트를_클릭합니다" md["click_link"][7] = "clique_texto_do_link" md["click_link"][8] = "нажмите_ссылку" md["click_link"][9] = "clic_texto_del_enlace" md["click_with_offset"] = ["*"] * num_langs md["click_with_offset"][0] = "click_with_offset" md["click_with_offset"][1] = "鼠标点击偏移" md["click_with_offset"][2] = "klik_op_locatie" md["click_with_offset"][3] = "cliquer_emplacement" md["click_with_offset"][4] = "clic_su_posizione" md["click_with_offset"][5] = "オフセットでクリック" md["click_with_offset"][6] = "위치를_클릭" md["click_with_offset"][7] = "clique_com_deslocamento" md["click_with_offset"][8] = "нажмите_на_местоположение" md["click_with_offset"][9] = "clic_con_desplazamiento" md["update_text"] = ["*"] * num_langs md["update_text"][0] = "update_text" md["update_text"][1] = "更新文本" md["update_text"][2] = "tekst_bijwerken" md["update_text"][3] = "modifier_texte" md["update_text"][4] = "aggiornare_testo" md["update_text"][5] = "テキストを更新" md["update_text"][6] = "텍스트를_업데이트" md["update_text"][7] = "atualizar_texto" md["update_text"][8] = "обновить_текст" md["update_text"][9] = "actualizar_texto" md["add_text"] = ["*"] * num_langs md["add_text"][0] = "add_text" md["add_text"][1] = "添加文本" md["add_text"][2] = "tekst_toevoegen" md["add_text"][3] = "ajouter_texte" md["add_text"][4] = "aggiungi_testo" md["add_text"][5] = "テキストを追加" md["add_text"][6] = "텍스트를_추가" md["add_text"][7] = "adicionar_texto" md["add_text"][8] = "добавить_текст" md["add_text"][9] = "agregar_texto" md["get_text"] = ["*"] * num_langs md["get_text"][0] = "get_text" md["get_text"][1] = "获取文本" md["get_text"][2] = "tekst_ophalen" md["get_text"][3] = "obtenir_texte" md["get_text"][4] = "ottenere_testo" md["get_text"][5] = "テキストを取得" md["get_text"][6] = "텍스트를_검색" md["get_text"][7] = "obter_texto" md["get_text"][8] = "получить_текст" md["get_text"][9] = "obtener_texto" md["assert_text"] = ["*"] * num_langs md["assert_text"][0] = "assert_text" md["assert_text"][1] = "断言文本" md["assert_text"][2] = "controleren_tekst" md["assert_text"][3] = "vérifier_texte" md["assert_text"][4] = "verificare_testo" md["assert_text"][5] = "テキストを確認する" md["assert_text"][6] = "텍스트_확인" md["assert_text"][7] = "verificar_texto" md["assert_text"][8] = "подтвердить_текст" md["assert_text"][9] = "verificar_texto" md["assert_exact_text"] = ["*"] * num_langs md["assert_exact_text"][0] = "assert_exact_text" md["assert_exact_text"][1] = "确切断言文本" md["assert_exact_text"][2] = "controleren_exacte_tekst" md["assert_exact_text"][3] = "vérifier_texte_exactement" md["assert_exact_text"][4] = "verificare_testo_esatto" md["assert_exact_text"][5] = "正確なテキストを確認する" md["assert_exact_text"][6] = "정확한_텍스트를_확인하는" md["assert_exact_text"][7] = "verificar_texto_exato" md["assert_exact_text"][8] = "подтвердить_текст_точно" md["assert_exact_text"][9] = "verificar_texto_exacto" md["assert_link_text"] = ["*"] * num_langs md["assert_link_text"][0] = "assert_link_text" md["assert_link_text"][1] = "断言链接文本" md["assert_link_text"][2] = "controleren_linktekst" md["assert_link_text"][3] = "vérifier_texte_du_lien" md["assert_link_text"][4] = "verificare_testo_del_collegamento" md["assert_link_text"][5] = "リンクテキストを確認する" md["assert_link_text"][6] = "링크_텍스트_확인" md["assert_link_text"][7] = "verificar_texto_do_link" md["assert_link_text"][8] = "подтвердить_ссылку" md["assert_link_text"][9] = "verificar_texto_del_enlace" md["assert_non_empty_text"] = ["*"] * num_langs md["assert_non_empty_text"][0] = "assert_non_empty_text" md["assert_non_empty_text"][1] = "断言非空文本" md["assert_non_empty_text"][2] = "controleren_niet_lege_tekst" md["assert_non_empty_text"][3] = "vérifier_texte_non_vide" md["assert_non_empty_text"][4] = "verificare_testo_non_vuoto" md["assert_non_empty_text"][5] = "空ではないテキストを確認する" md["assert_non_empty_text"][6] = "비어_있지_않은_텍스트_확인하는" md["assert_non_empty_text"][7] = "verificar_texto_não_vazio" md["assert_non_empty_text"][8] = "подтвердить_непустой_текст" md["assert_non_empty_text"][9] = "verificar_texto_no_vacío" md["assert_text_not_visible"] = ["*"] * num_langs md["assert_text_not_visible"][0] = "assert_text_not_visible" md["assert_text_not_visible"][1] = "断言文本不可见" md["assert_text_not_visible"][2] = "controleren_tekst_niet_zichtbaar" md["assert_text_not_visible"][3] = "vérifier_texte_pas_affiché" md["assert_text_not_visible"][4] = "verificare_testo_non_visto" md["assert_text_not_visible"][5] = "テキが表示されていないことを確認します" md["assert_text_not_visible"][6] = "텍스트_보이지_않는지_확인" md["assert_text_not_visible"][7] = "verificar_texto_não_visível" md["assert_text_not_visible"][8] = "подтвердить_текст_не_виден" md["assert_text_not_visible"][9] = "verificar_texto_no_se_muestra" md["assert_element"] = ["*"] * num_langs md["assert_element"][0] = "assert_element" md["assert_element"][1] = "断言元素" md["assert_element"][2] = "controleren_element" md["assert_element"][3] = "vérifier_élément" md["assert_element"][4] = "verificare_elemento" md["assert_element"][5] = "要素を確認する" md["assert_element"][6] = "요소_확인" md["assert_element"][7] = "verificar_elemento" md["assert_element"][8] = "подтвердить_элемент" md["assert_element"][9] = "verificar_elemento" md["assert_element_visible"] = ["*"] * num_langs md["assert_element_visible"][0] = "assert_element_visible" md["assert_element_visible"][1] = "断言元素可见" md["assert_element_visible"][2] = "controleren_element_zichtbaar" md["assert_element_visible"][3] = "vérifier_élément_affiché" md["assert_element_visible"][4] = "verificare_elemento_visto" md["assert_element_visible"][5] = "要素が表示されていることを確認" md["assert_element_visible"][6] = "요소가_보이는지_확인" md["assert_element_visible"][7] = "verificar_elemento_visível" md["assert_element_visible"][8] = "подтвердить_элемент_виден" md["assert_element_visible"][9] = "verificar_elemento_se_muestra" md["assert_element_not_visible"] = ["*"] * num_langs md["assert_element_not_visible"][0] = "assert_element_not_visible" md["assert_element_not_visible"][1] = "断言元素不可见" md["assert_element_not_visible"][2] = "controleren_element_niet_zichtbaar" md["assert_element_not_visible"][3] = "vérifier_élément_pas_affiché" md["assert_element_not_visible"][4] = "verificare_elemento_non_visto" md["assert_element_not_visible"][5] = "要素が表示されていないことを確認します" md["assert_element_not_visible"][6] = "요소가_보이지_않는지_확인" md["assert_element_not_visible"][7] = "verificar_elemento_não_visível" md["assert_element_not_visible"][8] = "подтвердить_элемент_не_виден" md["assert_element_not_visible"][9] = "verificar_elemento_no_se_muestra" md["assert_element_present"] = ["*"] * num_langs md["assert_element_present"][0] = "assert_element_present" md["assert_element_present"][1] = "断言元素存在" md["assert_element_present"][2] = "controleren_element_aanwezig" md["assert_element_present"][3] = "vérifier_élément_présent" md["assert_element_present"][4] = "verificare_elemento_presente" md["assert_element_present"][5] = "要素が存在することを確認します" md["assert_element_present"][6] = "요소가_존재하는지_확인" md["assert_element_present"][7] = "verificar_elemento_presente" md["assert_element_present"][8] = "подтвердить_элемент_присутствует" md["assert_element_present"][9] = "verificar_elemento_presente" md["assert_element_absent"] = ["*"] * num_langs md["assert_element_absent"][0] = "assert_element_absent" md["assert_element_absent"][1] = "断言元素不存在" md["assert_element_absent"][2] = "controleren_element_afwezig" md["assert_element_absent"][3] = "vérifier_élément_pas_présent" md["assert_element_absent"][4] = "verificare_elemento_assente" md["assert_element_absent"][5] = "要素が存在しないことを確認します" md["assert_element_absent"][6] = "요소가_존재하지_않는지_확인" md["assert_element_absent"][7] = "verificar_elemento_ausente" md["assert_element_absent"][8] = "подтвердить_элемент_отсутствует" md["assert_element_absent"][9] = "verificar_elemento_ausente" md["assert_attribute"] = ["*"] * num_langs md["assert_attribute"][0] = "assert_attribute" md["assert_attribute"][1] = "断言属性" md["assert_attribute"][2] = "controleren_attribuut" md["assert_attribute"][3] = "vérifier_attribut" md["assert_attribute"][4] = "verificare_attributo" md["assert_attribute"][5] = "属性を確認する" md["assert_attribute"][6] = "특성_확인" md["assert_attribute"][7] = "verificar_atributo" md["assert_attribute"][8] = "подтвердить_атрибут" md["assert_attribute"][9] = "verificar_atributo" md["assert_url"] = ["*"] * num_langs md["assert_url"][0] = "assert_url" md["assert_url"][1] = "断言URL" md["assert_url"][2] = "controleren_url" md["assert_url"][3] = "vérifier_url" md["assert_url"][4] = "verificare_url" md["assert_url"][5] = "URLを確認する" md["assert_url"][6] = "URL_확인" md["assert_url"][7] = "verificar_url" md["assert_url"][8] = "подтвердить_URL" md["assert_url"][9] = "verificar_url" md["assert_url_contains"] = ["*"] * num_langs md["assert_url_contains"][0] = "assert_url_contains" md["assert_url_contains"][1] = "断言URL包含" md["assert_url_contains"][2] = "controleren_url_bevat" md["assert_url_contains"][3] = "vérifier_url_contient" md["assert_url_contains"][4] = "verificare_url_contiene" md["assert_url_contains"][5] = "URL部分文字列を確認する" md["assert_url_contains"][6] = "URL_부분_확인" md["assert_url_contains"][7] = "verificar_url_contém" md["assert_url_contains"][8] = "подтвердить_URL_содержит" md["assert_url_contains"][9] = "verificar_url_contiene" md["assert_title"] = ["*"] * num_langs md["assert_title"][0] = "assert_title" md["assert_title"][1] = "断言标题" md["assert_title"][2] = "controleren_titel" md["assert_title"][3] = "vérifier_titre" md["assert_title"][4] = "verificare_titolo" md["assert_title"][5] = "タイトルを確認" md["assert_title"][6] = "제목_확인" md["assert_title"][7] = "verificar_título" md["assert_title"][8] = "подтвердить_название" md["assert_title"][9] = "verificar_título" md["assert_title_contains"] = ["*"] * num_langs md["assert_title_contains"][0] = "assert_title_contains" md["assert_title_contains"][1] = "断言标题包含" md["assert_title_contains"][2] = "controleren_titel_bevat" md["assert_title_contains"][3] = "vérifier_titre_contient" md["assert_title_contains"][4] = "verificare_titolo_contiene" md["assert_title_contains"][5] = "タイトル部分文字列を確認する" md["assert_title_contains"][6] = "제목_부분_확인" md["assert_title_contains"][7] = "verificar_título_contém" md["assert_title_contains"][8] = "подтвердить_название_содержит" md["assert_title_contains"][9] = "verificar_título_contiene" md["get_title"] = ["*"] * num_langs md["get_title"][0] = "get_title" md["get_title"][1] = "获取标题" md["get_title"][2] = "titel_ophalen" md["get_title"][3] = "obtenir_titre" md["get_title"][4] = "ottenere_titolo" md["get_title"][5] = "タイトルを取得する" md["get_title"][6] = "제목_검색" md["get_title"][7] = "obter_título" md["get_title"][8] = "получить_название" md["get_title"][9] = "obtener_título" md["assert_true"] = ["*"] * num_langs md["assert_true"][0] = "assert_true" md["assert_true"][1] = "断言为真" md["assert_true"][2] = "controleren_ware" md["assert_true"][3] = "vérifier_vrai" md["assert_true"][4] = "verificare_vero" md["assert_true"][5] = "検証が正しい" md["assert_true"][6] = "올바른지_확인" md["assert_true"][7] = "verificar_verdade" md["assert_true"][8] = "подтвердить_правду" md["assert_true"][9] = "verificar_verdad" md["assert_false"] = ["*"] * num_langs md["assert_false"][0] = "assert_false" md["assert_false"][1] = "断言为假" md["assert_false"][2] = "controleren_valse" md["assert_false"][3] = "vérifier_faux" md["assert_false"][4] = "verificare_falso" md["assert_false"][5] = "検証は偽です" md["assert_false"][6] = "거짓인지_확인" md["assert_false"][7] = "verificar_falso" md["assert_false"][8] = "подтвердить_ложные" md["assert_false"][9] = "verificar_falso" md["assert_equal"] = ["*"] * num_langs md["assert_equal"][0] = "assert_equal" md["assert_equal"][1] = "断言等于" md["assert_equal"][2] = "controleren_gelijk" md["assert_equal"][3] = "vérifier_égal" md["assert_equal"][4] = "verificare_uguale" md["assert_equal"][5] = "検証が等しい" md["assert_equal"][6] = "동일한지_확인" md["assert_equal"][7] = "verificar_igual" md["assert_equal"][8] = "подтвердить_одинаковый" md["assert_equal"][9] = "verificar_igual" md["assert_not_equal"] = ["*"] * num_langs md["assert_not_equal"][0] = "assert_not_equal" md["assert_not_equal"][1] = "断言不等于" md["assert_not_equal"][2] = "controleren_niet_gelijk" md["assert_not_equal"][3] = "vérifier_non_égal" md["assert_not_equal"][4] = "verificare_non_uguale" md["assert_not_equal"][5] = "検証が等しくない" md["assert_not_equal"][6] = "동일하지_않다고_어설션" md["assert_not_equal"][7] = "verificar_não_igual" md["assert_not_equal"][8] = "подтвердить_не_одинаковый" md["assert_not_equal"][9] = "verificar_diferente" md["refresh_page"] = ["*"] * num_langs md["refresh_page"][0] = "refresh_page" md["refresh_page"][1] = "刷新页面" md["refresh_page"][2] = "ververs_pagina" md["refresh_page"][3] = "rafraîchir_la_page" md["refresh_page"][4] = "aggiorna_la_pagina" md["refresh_page"][5] = "ページを更新する" md["refresh_page"][6] = "페이지_새로_고침" md["refresh_page"][7] = "atualizar_a_página" md["refresh_page"][8] = "обновить_страницу" md["refresh_page"][9] = "actualizar_la_página" md["get_current_url"] = ["*"] * num_langs md["get_current_url"][0] = "get_current_url" md["get_current_url"][1] = "获取当前网址" md["get_current_url"][2] = "huidige_url_ophalen" md["get_current_url"][3] = "obtenir_url_actuelle" md["get_current_url"][4] = "ottenere_url_corrente" md["get_current_url"][5] = "現在のURLを取得" md["get_current_url"][6] = "현재의_URL을_가져" md["get_current_url"][7] = "obter_url_atual" md["get_current_url"][8] = "получить_текущий_URL" md["get_current_url"][9] = "obtener_url_actual" md["get_page_source"] = ["*"] * num_langs md["get_page_source"][0] = "get_page_source" md["get_page_source"][1] = "获取页面源代码" md["get_page_source"][2] = "broncode_ophalen" md["get_page_source"][3] = "obtenir_html_de_la_page" md["get_page_source"][4] = "ottenere_la_pagina_html" md["get_page_source"][5] = "ページのソースコードを取得する" md["get_page_source"][6] = "페이지의_소스_코드를_얻을" md["get_page_source"][7] = "obter_a_página_html" md["get_page_source"][8] = "получить_источник_страницы" md["get_page_source"][9] = "obtener_html_de_la_página" md["go_back"] = ["*"] * num_langs md["go_back"][0] = "go_back" md["go_back"][1] = "回去" md["go_back"][2] = "terug" md["go_back"][3] = "retour" md["go_back"][4] = "indietro" md["go_back"][5] = "戻る" md["go_back"][6] = "뒤로" md["go_back"][7] = "voltar" md["go_back"][8] = "назад" md["go_back"][9] = "volver" md["go_forward"] = ["*"] * num_langs md["go_forward"][0] = "go_forward" md["go_forward"][1] = "向前" md["go_forward"][2] = "vooruit" md["go_forward"][3] = "en_avant" md["go_forward"][4] = "avanti" md["go_forward"][5] = "進む" md["go_forward"][6] = "앞으로" md["go_forward"][7] = "avançar" md["go_forward"][8] = "вперед" md["go_forward"][9] = "adelante" md["is_text_visible"] = ["*"] * num_langs md["is_text_visible"][0] = "is_text_visible" md["is_text_visible"][1] = "文本是否显示" md["is_text_visible"][2] = "tekst_zichtbaar" md["is_text_visible"][3] = "est_texte_affiché" md["is_text_visible"][4] = "è_testo_visto" md["is_text_visible"][5] = "テキストが表示されています" md["is_text_visible"][6] = "텍스트가_표시됩니다" md["is_text_visible"][7] = "o_texto_está_visível" md["is_text_visible"][8] = "текст_виден" md["is_text_visible"][9] = "se_muestra_el_texto" md["is_exact_text_visible"] = ["*"] * num_langs md["is_exact_text_visible"][0] = "is_exact_text_visible" md["is_exact_text_visible"][1] = "确切文本是否显示" md["is_exact_text_visible"][2] = "exacte_tekst_zichtbaar" md["is_exact_text_visible"][3] = "est_texte_exactement_affiché" md["is_exact_text_visible"][4] = "è_testo_esatto_visto" md["is_exact_text_visible"][5] = "正確なテキストが表示されています" md["is_exact_text_visible"][6] = "정확한_텍스트가_표시됩니다" md["is_exact_text_visible"][7] = "o_texto_exato_está_visível" md["is_exact_text_visible"][8] = "точный_текст_виден" md["is_exact_text_visible"][9] = "se_muestra_el_texto_exacto" md["is_element_visible"] = ["*"] * num_langs md["is_element_visible"][0] = "is_element_visible" md["is_element_visible"][1] = "元素是否可见" md["is_element_visible"][2] = "element_zichtbaar" md["is_element_visible"][3] = "est_un_élément_affiché" md["is_element_visible"][4] = "è_elemento_visto" md["is_element_visible"][5] = "要素は表示されますか" md["is_element_visible"][6] = "요소가_표시됩니다" md["is_element_visible"][7] = "o_elemento_está_visível" md["is_element_visible"][8] = "элемент_виден" md["is_element_visible"][9] = "se_muestra_el_elemento" md["is_element_enabled"] = ["*"] * num_langs md["is_element_enabled"][0] = "is_element_enabled" md["is_element_enabled"][1] = "元素是否启用" md["is_element_enabled"][2] = "element_ingeschakeld" md["is_element_enabled"][3] = "est_un_élément_activé" md["is_element_enabled"][4] = "è_elemento_abilitato" md["is_element_enabled"][5] = "要素が有効かどうか" md["is_element_enabled"][6] = "요소가_활성화돼" md["is_element_enabled"][7] = "o_elemento_está_habilitado" md["is_element_enabled"][8] = "элемент_включен" md["is_element_enabled"][9] = "está_habilitado_el_elemento" md["is_element_present"] = ["*"] * num_langs md["is_element_present"][0] = "is_element_present" md["is_element_present"][1] = "元素是否存在" md["is_element_present"][2] = "element_aanwezig" md["is_element_present"][3] = "est_un_élément_présent" md["is_element_present"][4] = "è_elemento_presente" md["is_element_present"][5] = "要素が存在するかどうか" md["is_element_present"][6] = "요소가_있습니다" md["is_element_present"][7] = "o_elemento_está_presente" md["is_element_present"][8] = "элемент_присутствует" md["is_element_present"][9] = "está_presente_el_elemento" md["wait_for_text"] = ["*"] * num_langs md["wait_for_text"][0] = "wait_for_text" md["wait_for_text"][1] = "等待文本" md["wait_for_text"][2] = "wachten_op_tekst" md["wait_for_text"][3] = "attendre_le_texte" md["wait_for_text"][4] = "attendere_il_testo" md["wait_for_text"][5] = "テキストを待つ" md["wait_for_text"][6] = "텍스트가_나타날_때까지_기다립니다" md["wait_for_text"][7] = "aguardar_o_texto" md["wait_for_text"][8] = "ждать_текста" md["wait_for_text"][9] = "espera_el_texto" md["wait_for_element"] = ["*"] * num_langs md["wait_for_element"][0] = "wait_for_element" md["wait_for_element"][1] = "等待元素" md["wait_for_element"][2] = "wachten_op_element" md["wait_for_element"][3] = "attendre_un_élément" md["wait_for_element"][4] = "attendere_un_elemento" md["wait_for_element"][5] = "要素を待つ" md["wait_for_element"][6] = "요소가_나타날_때까지_기다립니다" md["wait_for_element"][7] = "aguardar_o_elemento" md["wait_for_element"][8] = "ждать_элемента" md["wait_for_element"][9] = "espera_el_elemento" md["wait_for_element_visible"] = ["*"] * num_langs md["wait_for_element_visible"][0] = "wait_for_element_visible" md["wait_for_element_visible"][1] = "等待元素可见" md["wait_for_element_visible"][2] = "wachten_op_element_zichtbaar" md["wait_for_element_visible"][3] = "attendre_un_élément_affiché" md["wait_for_element_visible"][4] = "attendere_un_elemento_visto" md["wait_for_element_visible"][5] = "要素が表示されるのを待ちます" md["wait_for_element_visible"][6] = "요소가_표시_될_때까지_기다립니다" md["wait_for_element_visible"][7] = "aguardar_o_elemento_visível" md["wait_for_element_visible"][8] = "ждать_элемента_виден" md["wait_for_element_visible"][9] = "espera_el_elemento_se_muestra" md["wait_for_element_not_visible"] = ["*"] * num_langs md["wait_for_element_not_visible"][0] = "wait_for_element_not_visible" md["wait_for_element_not_visible"][1] = "等待元素不可见" md["wait_for_element_not_visible"][2] = "wachten_op_element_niet_zichtbaar" md["wait_for_element_not_visible"][3] = "attendre_un_élément_pas_affiché" md["wait_for_element_not_visible"][4] = "attendere_un_elemento_non_visto" md["wait_for_element_not_visible"][5] = "要素が表示されなくなるまで待ちます" md["wait_for_element_not_visible"][6] = "요소가_사라질_때까지_기다리십시오" md["wait_for_element_not_visible"][7] = "aguardar_o_elemento_não_visível" md["wait_for_element_not_visible"][8] = "ждать_элемента_не_виден" md["wait_for_element_not_visible"][9] = "espera_el_elemento_no_se_muestra" md["wait_for_element_present"] = ["*"] * num_langs md["wait_for_element_present"][0] = "wait_for_element_present" md["wait_for_element_present"][1] = "等待元素存在" md["wait_for_element_present"][2] = "wachten_op_element_aanwezig" md["wait_for_element_present"][3] = "attendre_un_élément_présent" md["wait_for_element_present"][4] = "attendere_un_elemento_presente" md["wait_for_element_present"][5] = "要素が存在するのを待つ" md["wait_for_element_present"][6] = "요소가_존재할_때까지_기다립니다" md["wait_for_element_present"][7] = "aguardar_o_elemento_presente" md["wait_for_element_present"][8] = "ждать_элемента_присутствует" md["wait_for_element_present"][9] = "espera_el_elemento_presente" md["wait_for_element_absent"] = ["*"] * num_langs md["wait_for_element_absent"][0] = "wait_for_element_absent" md["wait_for_element_absent"][1] = "等待元素不存在" md["wait_for_element_absent"][2] = "wachten_op_element_afwezig" md["wait_for_element_absent"][3] = "attendre_un_élément_pas_présent" md["wait_for_element_absent"][4] = "attendere_un_elemento_assente" md["wait_for_element_absent"][5] = "要素が存在しないのを待つ" md["wait_for_element_absent"][6] = "요소가_나타날_때까지_기다리십시오" md["wait_for_element_absent"][7] = "aguardar_o_elemento_ausente" md["wait_for_element_absent"][8] = "ждать_элемента_отсутствует" md["wait_for_element_absent"][9] = "espera_el_elemento_ausente" md["wait_for_attribute"] = ["*"] * num_langs md["wait_for_attribute"][0] = "wait_for_attribute" md["wait_for_attribute"][1] = "等待属性" md["wait_for_attribute"][2] = "wachten_op_attribuut" md["wait_for_attribute"][3] = "attendre_un_attribut" md["wait_for_attribute"][4] = "attendere_un_attributo" md["wait_for_attribute"][5] = "属性を待つ" md["wait_for_attribute"][6] = "특성_때까지_기다립니다" md["wait_for_attribute"][7] = "aguardar_o_atributo" md["wait_for_attribute"][8] = "ждать_атрибут" md["wait_for_attribute"][9] = "espera_el_atributo" md["wait_for_ready_state_complete"] = ["*"] * num_langs md["wait_for_ready_state_complete"][0] = "wait_for_ready_state_complete" md["wait_for_ready_state_complete"][1] = "等待页面加载完成" md["wait_for_ready_state_complete"][2] = "wacht_tot_de_pagina_is_geladen" md["wait_for_ready_state_complete"][3] = "attendre_que_la_page_se_charge" wfrsc_it = "attendere_il_caricamento_della_pagina" md["wait_for_ready_state_complete"][4] = wfrsc_it md["wait_for_ready_state_complete"][5] = "ページがロードされるのを待ちます" md["wait_for_ready_state_complete"][6] = "페이지가_로드될_때까지_기다립니다" md["wait_for_ready_state_complete"][7] = "aguardar_a_página_carregar" md["wait_for_ready_state_complete"][8] = "ждать_загрузки_страницы" md["wait_for_ready_state_complete"][9] = "esperar_a_que_cargue_la_página" md["sleep"] = ["*"] * num_langs md["sleep"][0] = "sleep" md["sleep"][1] = "睡" md["sleep"][2] = "slapen" md["sleep"][3] = "dormir" md["sleep"][4] = "dormire" md["sleep"][5] = "眠る" md["sleep"][6] = "잠을" md["sleep"][7] = "dormir" md["sleep"][8] = "спать" md["sleep"][9] = "dormir" md["wait"] = ["*"] * num_langs md["wait"][0] = "wait" md["wait"][1] = "等待" md["wait"][2] = "wachten" md["wait"][3] = "attendre" md["wait"][4] = "attendere" md["wait"][5] = "待つ" md["wait"][6] = "기다림" md["wait"][7] = "aguardar" md["wait"][8] = "ждать" md["wait"][9] = "espera" md["submit"] = ["*"] * num_langs md["submit"][0] = "submit" md["submit"][1] = "提交" md["submit"][2] = "verzenden" md["submit"][3] = "soumettre" md["submit"][4] = "inviare" md["submit"][5] = "を提出す" md["submit"][6] = "제출" md["submit"][7] = "enviar" md["submit"][8] = "отправить" md["submit"][9] = "enviar" md["clear"] = ["*"] * num_langs md["clear"][0] = "clear" md["clear"][1] = "清除" md["clear"][2] = "wissen" md["clear"][3] = "effacer" md["clear"][4] = "cancellare" md["clear"][5] = "クリアする" md["clear"][6] = "지우려면" md["clear"][7] = "limpar" md["clear"][8] = "очистить" md["clear"][9] = "despejar" md["focus"] = ["*"] * num_langs md["focus"][0] = "focus" md["focus"][1] = "专注于" md["focus"][2] = "focussen" md["focus"][3] = "concentrer" md["focus"][4] = "focalizzare" md["focus"][5] = "集中する" md["focus"][6] = "집중하다" md["focus"][7] = "focar" md["focus"][8] = "сосредоточиться" md["focus"][9] = "centrarse" md["js_click"] = ["*"] * num_langs md["js_click"][0] = "js_click" md["js_click"][1] = "JS单击" md["js_click"][2] = "js_klik" md["js_click"][3] = "js_cliquer" md["js_click"][4] = "js_fare_clic" md["js_click"][5] = "JSクリックして" md["js_click"][6] = "JS_클릭" md["js_click"][7] = "js_clique" md["js_click"][8] = "JS_нажмите" md["js_click"][9] = "js_haga_clic" md["js_update_text"] = ["*"] * num_langs md["js_update_text"][0] = "js_update_text" md["js_update_text"][1] = "JS更新文本" md["js_update_text"][2] = "js_tekst_bijwerken" md["js_update_text"][3] = "js_modifier_texte" md["js_update_text"][4] = "js_aggiornare_testo" md["js_update_text"][5] = "JSテキストを更新" md["js_update_text"][6] = "JS_텍스트를_업데이트" md["js_update_text"][7] = "js_atualizar_texto" md["js_update_text"][8] = "JS_обновить_текст" md["js_update_text"][9] = "js_actualizar_texto" md["js_type"] = ["*"] * num_langs md["js_type"][0] = "js_type" md["js_type"][1] = "JS输入文本" md["js_type"][2] = "js_typ" md["js_type"][3] = "js_taper" md["js_type"][4] = "js_digitare" md["js_type"][5] = "JS入力" md["js_type"][6] = "JS_입력" md["js_type"][7] = "js_digitar" md["js_type"][8] = "JS_введите" md["js_type"][9] = "js_escriba" md["jquery_click"] = ["*"] * num_langs md["jquery_click"][0] = "jquery_click" md["jquery_click"][1] = "JQUERY单击" md["jquery_click"][2] = "jquery_klik" md["jquery_click"][3] = "jquery_cliquer" md["jquery_click"][4] = "jquery_fare_clic" md["jquery_click"][5] = "JQUERYクリックして" md["jquery_click"][6] = "JQUERY_클릭" md["jquery_click"][7] = "jquery_clique" md["jquery_click"][8] = "JQUERY_нажмите" md["jquery_click"][9] = "jquery_haga_clic" md["jquery_update_text"] = ["*"] * num_langs md["jquery_update_text"][0] = "jquery_update_text" md["jquery_update_text"][1] = "JQUERY更新文本" md["jquery_update_text"][2] = "jquery_tekst_bijwerken" md["jquery_update_text"][3] = "jquery_modifier_texte" md["jquery_update_text"][4] = "jquery_aggiornare_testo" md["jquery_update_text"][5] = "JQUERYテキストを更新" md["jquery_update_text"][6] = "JQUERY_텍스트를_업데이트" md["jquery_update_text"][7] = "jquery_atualizar_texto" md["jquery_update_text"][8] = "JQUERY_обновить_текст" md["jquery_update_text"][9] = "jquery_actualizar_texto" md["jquery_type"] = ["*"] * num_langs md["jquery_type"][0] = "jquery_type" md["jquery_type"][1] = "JQUERY输入文本" md["jquery_type"][2] = "jquery_typ" md["jquery_type"][3] = "jquery_taper" md["jquery_type"][4] = "jquery_digitare" md["jquery_type"][5] = "JQUERY入力" md["jquery_type"][6] = "JQUERY_입력" md["jquery_type"][7] = "jquery_digitar" md["jquery_type"][8] = "JQUERY_введите" md["jquery_type"][9] = "jquery_escriba" md["inspect_html"] = ["*"] * num_langs md["inspect_html"][0] = "inspect_html" md["inspect_html"][1] = "检查HTML" md["inspect_html"][2] = "html_inspecteren" md["inspect_html"][3] = "vérifier_html" md["inspect_html"][4] = "controlla_html" md["inspect_html"][5] = "HTMLをチェック" md["inspect_html"][6] = "HTML_확인" md["inspect_html"][7] = "verificar_html" md["inspect_html"][8] = "проверить_HTML" md["inspect_html"][9] = "comprobar_html" md["save_screenshot"] = ["*"] * num_langs md["save_screenshot"][0] = "save_screenshot" md["save_screenshot"][1] = "保存截图" md["save_screenshot"][2] = "bewaar_screenshot" md["save_screenshot"][3] = "enregistrer_capture_d_écran" md["save_screenshot"][4] = "salva_screenshot" md["save_screenshot"][5] = "スクリーンショットを保存" md["save_screenshot"][6] = "스크린_샷_저장" md["save_screenshot"][7] = "salvar_captura_de_tela" md["save_screenshot"][8] = "сохранить_скриншот" md["save_screenshot"][9] = "guardar_captura_de_pantalla" md["save_screenshot_to_logs"] = ["*"] * num_langs md["save_screenshot_to_logs"][0] = "save_screenshot_to_logs" md["save_screenshot_to_logs"][1] = "保存截图到日志" md["save_screenshot_to_logs"][2] = "bewaar_screenshot_om_te_loggen" md["save_screenshot_to_logs"][3] = "enregistrer_capture_d_écran_aux_logs" md["save_screenshot_to_logs"][4] = "salva_screenshot_nei_logs" md["save_screenshot_to_logs"][5] = "ログにスクリーンショットを保存" md["save_screenshot_to_logs"][6] = "로그에_스크린_샷_저장" md["save_screenshot_to_logs"][7] = "salvar_captura_de_tela_para_logs" md["save_screenshot_to_logs"][8] = "сохранить_скриншот_в_логи" md["save_screenshot_to_logs"][9] = "guardar_captura_de_pantalla_para_logs" md["choose_file"] = ["*"] * num_langs md["choose_file"][0] = "choose_file" md["choose_file"][1] = "选择文件" md["choose_file"][2] = "selecteer_bestand" md["choose_file"][3] = "sélectionner_fichier" md["choose_file"][4] = "seleziona_file" md["choose_file"][5] = "ファイルを選択" md["choose_file"][6] = "파일을_선택" md["choose_file"][7] = "selecionar_arquivo" md["choose_file"][8] = "выберите_файл" md["choose_file"][9] = "seleccionar_archivo" md["execute_script"] = ["*"] * num_langs md["execute_script"][0] = "execute_script" md["execute_script"][1] = "执行脚本" md["execute_script"][2] = "script_uitvoeren" md["execute_script"][3] = "exécuter_script" md["execute_script"][4] = "eseguire_script" md["execute_script"][5] = "スクリプトを実行する" md["execute_script"][6] = "스크립트를_실행하려면" md["execute_script"][7] = "executar_script" md["execute_script"][8] = "выполнение_скрипта" md["execute_script"][9] = "ejecutar_script" md["safe_execute_script"] = ["*"] * num_langs md["safe_execute_script"][0] = "safe_execute_script" md["safe_execute_script"][1] = "安全执行脚本" md["safe_execute_script"][2] = "script_veilig_uitvoeren" md["safe_execute_script"][3] = "exécuter_script_sans_risque" md["safe_execute_script"][4] = "eseguire_script_sicuro" md["safe_execute_script"][5] = "スクリプトを安全に実行する" md["safe_execute_script"][6] = "스크립트를_안전하게_실행" md["safe_execute_script"][7] = "executar_script_com_segurança" md["safe_execute_script"][8] = "безопасное_выполнение_скрипта" md["safe_execute_script"][9] = "ejecutar_script_de_forma_segura" md["activate_jquery"] = ["*"] * num_langs md["activate_jquery"][0] = "activate_jquery" md["activate_jquery"][1] = "加载JQUERY" md["activate_jquery"][2] = "activeer_jquery" md["activate_jquery"][3] = "activer_jquery" md["activate_jquery"][4] = "attiva_jquery" md["activate_jquery"][5] = "JQUERYを読み込む" md["activate_jquery"][6] = "JQUERY_로드" md["activate_jquery"][7] = "ativar_jquery" md["activate_jquery"][8] = "активировать_JQUERY" md["activate_jquery"][9] = "activar_jquery" md["activate_recorder"] = ["*"] * num_langs md["activate_recorder"][0] = "activate_recorder" md["activate_recorder"][1] = "加载RECORDER" md["activate_recorder"][2] = "activeer_recorder" md["activate_recorder"][3] = "activer_recorder" md["activate_recorder"][4] = "attiva_recorder" md["activate_recorder"][5] = "RECORDERを読み込む" md["activate_recorder"][6] = "RECORDER_로드" md["activate_recorder"][7] = "ativar_recorder" md["activate_recorder"][8] = "активировать_RECORDER" md["activate_recorder"][9] = "activar_recorder" md["open_if_not_url"] = ["*"] * num_langs md["open_if_not_url"][0] = "open_if_not_url" md["open_if_not_url"][1] = "开启如果不网址" md["open_if_not_url"][2] = "openen_zo_niet_url" md["open_if_not_url"][3] = "ouvrir_si_non_url" md["open_if_not_url"][4] = "apri_se_non_url" md["open_if_not_url"][5] = "URLでない場合は開く" md["open_if_not_url"][6] = "URL_이_아닌_경우_열기" md["open_if_not_url"][7] = "abrir_se_não_url" md["open_if_not_url"][8] = "открыть_если_не_URL" md["open_if_not_url"][9] = "abrir_que_no_url" md["ad_block"] = ["*"] * num_langs md["ad_block"][0] = "ad_block" md["ad_block"][1] = "阻止广告" md["ad_block"][2] = "blokkeer_advertenties" md["ad_block"][3] = "annonces_de_bloc" md["ad_block"][4] = "bloccare_gli_annunci" md["ad_block"][5] = "ブロック広告" md["ad_block"][6] = "광고_차단" md["ad_block"][7] = "bloquear_anúncios" md["ad_block"][8] = "блокировать_рекламу" md["ad_block"][9] = "bloquear_anuncios" md["skip"] = ["*"] * num_langs md["skip"][0] = "skip" md["skip"][1] = "跳过" md["skip"][2] = "overslaan" md["skip"][3] = "sauter" md["skip"][4] = "saltare" md["skip"][5] = "スキップ" md["skip"][6] = "건너뛸" md["skip"][7] = "saltar" md["skip"][8] = "пропускать" md["skip"][9] = "saltar" md["assert_no_404_errors"] = ["*"] * num_langs md["assert_no_404_errors"][0] = "assert_no_404_errors" md["assert_no_404_errors"][1] = "检查断开的链接" md["assert_no_404_errors"][2] = "controleren_op_gebroken_links" md["assert_no_404_errors"][3] = "vérifier_les_liens_rompus" md["assert_no_404_errors"][4] = "verificare_i_collegamenti" md["assert_no_404_errors"][5] = "リンク切れを確認する" md["assert_no_404_errors"][6] = "끊어진_링크_확인" md["assert_no_404_errors"][7] = "verificar_se_há_links_quebrados" md["assert_no_404_errors"][8] = "проверить_ошибки_404" md["assert_no_404_errors"][9] = "verificar_si_hay_enlaces_rotos" md["assert_no_js_errors"] = ["*"] * num_langs md["assert_no_js_errors"][0] = "assert_no_js_errors" md["assert_no_js_errors"][1] = "检查JS错误" md["assert_no_js_errors"][2] = "controleren_op_js_fouten" md["assert_no_js_errors"][3] = "vérifier_les_erreurs_js" md["assert_no_js_errors"][4] = "controlla_errori_js" md["assert_no_js_errors"][5] = "JSエラーを確認する" md["assert_no_js_errors"][6] = "JS_오류_확인" md["assert_no_js_errors"][7] = "verificar_se_há_erros_js" md["assert_no_js_errors"][8] = "проверить_ошибки_JS" md["assert_no_js_errors"][9] = "verificar_si_hay_errores_js" md["switch_to_frame"] = ["*"] * num_langs md["switch_to_frame"][0] = "switch_to_frame" md["switch_to_frame"][1] = "切换到帧" md["switch_to_frame"][2] = "overschakelen_naar_frame" md["switch_to_frame"][3] = "passer_au_cadre" md["switch_to_frame"][4] = "passa_alla_cornice" md["switch_to_frame"][5] = "フレームに切り替えます" md["switch_to_frame"][6] = "프레임으로_전환" md["switch_to_frame"][7] = "mudar_para_o_quadro" md["switch_to_frame"][8] = "переключиться_на_кадр" md["switch_to_frame"][9] = "cambiar_al_marco" md["switch_to_default_content"] = ["*"] * num_langs md["switch_to_default_content"][0] = "switch_to_default_content" md["switch_to_default_content"][1] = "切换到默认内容" md["switch_to_default_content"][2] = "overschakelen_naar_standaardcontent" md["switch_to_default_content"][3] = "passer_au_contenu_par_défaut" md["switch_to_default_content"][4] = "passa_al_contenuto_predefinito" md["switch_to_default_content"][5] = "デフォルトのコンテンツに切り替える" md["switch_to_default_content"][6] = "기본_콘텐츠로_전환" md["switch_to_default_content"][7] = "mudar_para_o_conteúdo_padrão" stdc_ru = "переключиться_на_содержимое_по_умолчанию" md["switch_to_default_content"][8] = stdc_ru md["switch_to_default_content"][9] = "cambiar_al_contenido_predeterminado" md["switch_to_parent_frame"] = ["*"] * num_langs md["switch_to_parent_frame"][0] = "switch_to_parent_frame" md["switch_to_parent_frame"][1] = "切换到父框架" md["switch_to_parent_frame"][2] = "overschakelen_naar_bovenliggend_frame" md["switch_to_parent_frame"][3] = "passer_au_cadre_parent" md["switch_to_parent_frame"][4] = "passa_alla_cornice_principale" md["switch_to_parent_frame"][5] = "親フレームに切り替えます" md["switch_to_parent_frame"][6] = "상위_프레임으로_전환" md["switch_to_parent_frame"][7] = "mudar_para_o_quadro_pai" md["switch_to_parent_frame"][8] = "переключиться_на_родительский_кадр" md["switch_to_parent_frame"][9] = "cambiar_al_marco_principal" md["open_new_window"] = ["*"] * num_langs md["open_new_window"][0] = "open_new_window" md["open_new_window"][1] = "打开新窗口" md["open_new_window"][2] = "nieuw_venster_openen" md["open_new_window"][3] = "ouvrir_une_nouvelle_fenêtre" md["open_new_window"][4] = "apri_una_nuova_finestra" md["open_new_window"][5] = "新しいウィンドウを開く" md["open_new_window"][6] = "새_창_열기" md["open_new_window"][7] = "abrir_nova_janela" md["open_new_window"][8] = "открыть_новое_окно" md["open_new_window"][9] = "abrir_una_nueva_ventana" md["switch_to_window"] = ["*"] * num_langs md["switch_to_window"][0] = "switch_to_window" md["switch_to_window"][1] = "切换到窗口" md["switch_to_window"][2] = "overschakelen_naar_venster" md["switch_to_window"][3] = "passer_à_fenêtre" md["switch_to_window"][4] = "passa_alla_finestra" md["switch_to_window"][5] = "ウィンドウに切り替え" md["switch_to_window"][6] = "창으로_전환" md["switch_to_window"][7] = "mudar_para_janela" md["switch_to_window"][8] = "переключиться_на_окно" md["switch_to_window"][9] = "cambiar_a_ventana" md["switch_to_default_window"] = ["*"] * num_langs md["switch_to_default_window"][0] = "switch_to_default_window" md["switch_to_default_window"][1] = "切换到默认窗口" md["switch_to_default_window"][2] = "overschakelen_naar_standaardvenster" md["switch_to_default_window"][3] = "passer_à_fenêtre_par_défaut" md["switch_to_default_window"][4] = "passa_alla_finestra_predefinita" md["switch_to_default_window"][5] = "デフォルトのウィンドウに切り替える" md["switch_to_default_window"][6] = "기본_창으로_전환" md["switch_to_default_window"][7] = "mudar_para_a_janela_padrão" md["switch_to_default_window"][8] = "переключиться_на_окно_по_умолчанию" md["switch_to_default_window"][9] = "cambiar_a_ventana_predeterminada" md["switch_to_newest_window"] = ["*"] * num_langs md["switch_to_newest_window"][0] = "switch_to_newest_window" md["switch_to_newest_window"][1] = "切换到最新的窗口" md["switch_to_newest_window"][2] = "overschakelen_naar_nieuwste_venster" md["switch_to_newest_window"][3] = "passer_à_fenêtre_dernière" md["switch_to_newest_window"][4] = "passa_alla_finestra_ultimo" md["switch_to_newest_window"][5] = "最新のウィンドウに切り替えます" md["switch_to_newest_window"][6] = "최신_창으로_전환" md["switch_to_newest_window"][7] = "mudar_para_a_janela_última" md["switch_to_newest_window"][8] = "переключиться_на_последнее_окно" md["switch_to_newest_window"][9] = "cambiar_a_ventana_última" md["maximize_window"] = ["*"] * num_langs md["maximize_window"][0] = "maximize_window" md["maximize_window"][1] = "最大化窗口" md["maximize_window"][2] = "venster_maximaliseren" md["maximize_window"][3] = "maximiser_fenêtre" md["maximize_window"][4] = "ingrandisci_finestra" md["maximize_window"][5] = "ウィンドウを最大化する" md["maximize_window"][6] = "창_최대화" md["maximize_window"][7] = "maximizar_janela" md["maximize_window"][8] = "максимальное_окно" md["maximize_window"][9] = "maximizar_ventana" md["highlight"] = ["*"] * num_langs md["highlight"][0] = "highlight" md["highlight"][1] = "亮点" md["highlight"][2] = "markeren" md["highlight"][3] = "illuminer" md["highlight"][4] = "illuminare" md["highlight"][5] = "ハイライト" md["highlight"][6] = "강조" md["highlight"][7] = "destaque" md["highlight"][8] = "осветить" md["highlight"][9] = "resalte" md["highlight_click"] = ["*"] * num_langs md["highlight_click"][0] = "highlight_click" md["highlight_click"][1] = "亮点单击" md["highlight_click"][2] = "markeren_klik" md["highlight_click"][3] = "illuminer_cliquer" md["highlight_click"][4] = "illuminare_clic" md["highlight_click"][5] = "ハイライトしてクリックして" md["highlight_click"][6] = "강조_클릭" md["highlight_click"][7] = "destaque_clique" md["highlight_click"][8] = "осветить_нажмите" md["highlight_click"][9] = "resalte_clic" md["scroll_to"] = ["*"] * num_langs md["scroll_to"][0] = "scroll_to" md["scroll_to"][1] = "滚动到" md["scroll_to"][2] = "scrollen_naar" md["scroll_to"][3] = "déménager_à" md["scroll_to"][4] = "scorrere_fino_a" md["scroll_to"][5] = "スクロールして" md["scroll_to"][6] = "요소로_스크롤" md["scroll_to"][7] = "rolar_para" md["scroll_to"][8] = "прокрутить_к" md["scroll_to"][9] = "desplazarse_a" md["scroll_to_top"] = ["*"] * num_langs md["scroll_to_top"][0] = "scroll_to_top" md["scroll_to_top"][1] = "滚动到顶部" md["scroll_to_top"][2] = "naar_boven_scrollen" md["scroll_to_top"][3] = "faites_défiler_vers_le_haut" md["scroll_to_top"][4] = "scorri_verso_alto" md["scroll_to_top"][5] = "一番上までスクロール" md["scroll_to_top"][6] = "맨_위로_스크롤" md["scroll_to_top"][7] = "rolar_para_o_topo" md["scroll_to_top"][8] = "пролистать_наверх" md["scroll_to_top"][9] = "desplazarse_hasta_la_parte_superior" md["scroll_to_bottom"] = ["*"] * num_langs md["scroll_to_bottom"][0] = "scroll_to_bottom" md["scroll_to_bottom"][1] = "滚动到底部" md["scroll_to_bottom"][2] = "naar_beneden_scrollen" md["scroll_to_bottom"][3] = "faites_défiler_vers_le_bas" md["scroll_to_bottom"][4] = "scorri_verso_il_basso" md["scroll_to_bottom"][5] = "一番下までスクロール" md["scroll_to_bottom"][6] = "하단으로_스크롤" md["scroll_to_bottom"][7] = "rolar_para_o_fundo" md["scroll_to_bottom"][8] = "прокрутить_вниз" md["scroll_to_bottom"][9] = "desplazarse_hasta_la_parte_inferior" md["hover_and_click"] = ["*"] * num_langs md["hover_and_click"][0] = "hover_and_click" md["hover_and_click"][1] = "鼠标悬停并单击" md["hover_and_click"][2] = "zweven_en_klik" md["hover_and_click"][3] = "passer_la_souris_et_cliquer" md["hover_and_click"][4] = "passare_il_mouse_e_fare_clic" md["hover_and_click"][5] = "マウスオーバーしてクリック" md["hover_and_click"][6] = "마우스오버_및_클릭" md["hover_and_click"][7] = "passe_o_mouse_e_clique" md["hover_and_click"][8] = "наведите_и_нажмите" md["hover_and_click"][9] = "pasar_el_ratón_y_hacer_clic" md["hover"] = ["*"] * num_langs md["hover"][0] = "hover" md["hover"][1] = "鼠标悬停" md["hover"][2] = "zweven" md["hover"][3] = "survol_de_la_souris" md["hover"][4] = "passaggio_del_mouse" md["hover"][5] = "マウスオーバー" md["hover"][6] = "마우스오버" md["hover"][7] = "passe_o_mouse" md["hover"][8] = "наведение_мыши" md["hover"][9] = "pasar_el_ratón" md["is_selected"] = ["*"] * num_langs md["is_selected"][0] = "is_selected" md["is_selected"][1] = "是否被选中" md["is_selected"][2] = "is_het_geselecteerd" md["is_selected"][3] = "est_il_sélectionné" md["is_selected"][4] = "è_selezionato" md["is_selected"][5] = "選択されていることを" md["is_selected"][6] = "선택되어_있는지" md["is_selected"][7] = "é_selecionado" md["is_selected"][8] = "выбран" md["is_selected"][9] = "está_seleccionado" md["press_up_arrow"] = ["*"] * num_langs md["press_up_arrow"][0] = "press_up_arrow" md["press_up_arrow"][1] = "按向上箭头" md["press_up_arrow"][2] = "druk_op_pijl_omhoog" md["press_up_arrow"][3] = "appuyer_sur_flèche_haut" md["press_up_arrow"][4] = "premere_la_freccia_su" md["press_up_arrow"][5] = "上矢印を押します" md["press_up_arrow"][6] = "위쪽_화살표를_누릅니다" md["press_up_arrow"][7] = "pressione_a_seta_para_cima" md["press_up_arrow"][8] = "нажмите_стрелку_вверх" md["press_up_arrow"][9] = "presione_la_flecha_hacia_arriba" md["press_down_arrow"] = ["*"] * num_langs md["press_down_arrow"][0] = "press_down_arrow" md["press_down_arrow"][1] = "按向下箭头" md["press_down_arrow"][2] = "druk_op_pijl_omlaag" md["press_down_arrow"][3] = "appuyer_sur_flèche_bas" md["press_down_arrow"][4] = "premere_la_freccia_giù" md["press_down_arrow"][5] = "下矢印を押します" md["press_down_arrow"][6] = "아래쪽_화살표를_누르십시오" md["press_down_arrow"][7] = "pressione_a_seta_para_baixo" md["press_down_arrow"][8] = "нажмите_стрелку_вниз" md["press_down_arrow"][9] = "presione_la_flecha_hacia_abajo" md["press_left_arrow"] = ["*"] * num_langs md["press_left_arrow"][0] = "press_left_arrow" md["press_left_arrow"][1] = "按向左箭头" md["press_left_arrow"][2] = "druk_op_pijl_links" md["press_left_arrow"][3] = "appuyer_sur_flèche_gauche" md["press_left_arrow"][4] = "premere_la_freccia_sinistra" md["press_left_arrow"][5] = "左矢印を押します" md["press_left_arrow"][6] = "왼쪽_화살표를_누르십시오" md["press_left_arrow"][7] = "pressione_a_seta_esquerda" md["press_left_arrow"][8] = "нажмите_стрелку_влево" md["press_left_arrow"][9] = "presione_la_flecha_izquierda" md["press_right_arrow"] = ["*"] * num_langs md["press_right_arrow"][0] = "press_right_arrow" md["press_right_arrow"][1] = "按向右箭头" md["press_right_arrow"][2] = "druk_op_pijl_rechts" md["press_right_arrow"][3] = "appuyer_sur_flèche_droite" md["press_right_arrow"][4] = "premere_la_freccia_destra" md["press_right_arrow"][5] = "右矢印を押します" md["press_right_arrow"][6] = "오른쪽_화살표를_누르십시오" md["press_right_arrow"][7] = "pressione_a_seta_direita" md["press_right_arrow"][8] = "нажмите_стрелку_вправо" md["press_right_arrow"][9] = "presione_la_flecha_derecha" md["click_visible_elements"] = ["*"] * num_langs md["click_visible_elements"][0] = "click_visible_elements" md["click_visible_elements"][1] = "单击可见元素" md["click_visible_elements"][2] = "klik_zichtbare_elementen" md["click_visible_elements"][3] = "cliquer_éléments_visibles" md["click_visible_elements"][4] = "clic_sugli_elementi_visibili" md["click_visible_elements"][5] = "表示要素をクリックします" md["click_visible_elements"][6] = "페이지_요소를_클릭_합니다" md["click_visible_elements"][7] = "clique_nos_elementos_visíveis" md["click_visible_elements"][8] = "нажмите_видимые_элементы" md["click_visible_elements"][9] = "clic_en_elementos_visibles" md["select_option_by_text"] = ["*"] * num_langs md["select_option_by_text"][0] = "select_option_by_text" md["select_option_by_text"][1] = "按文本选择选项" md["select_option_by_text"][2] = "optie_selecteren_op_tekst" md["select_option_by_text"][3] = "sélectionner_option_par_texte" md["select_option_by_text"][4] = "selezionare_opzione_per_testo" md["select_option_by_text"][5] = "テキストでオプションを選択" md["select_option_by_text"][6] = "텍스트로_옵션_선택" md["select_option_by_text"][7] = "selecionar_opção_por_texto" md["select_option_by_text"][8] = "выбрать_опцию_по_тексту" md["select_option_by_text"][9] = "seleccionar_opción_por_texto" md["select_option_by_index"] = ["*"] * num_langs md["select_option_by_index"][0] = "select_option_by_index" md["select_option_by_index"][1] = "按索引选择选项" md["select_option_by_index"][2] = "optie_selecteren_op_index" md["select_option_by_index"][3] = "sélectionner_option_par_index" md["select_option_by_index"][4] = "selezionare_opzione_per_indice" md["select_option_by_index"][5] = "インデックスでオプションを選択" md["select_option_by_index"][6] = "인덱스별로_옵션_선택" md["select_option_by_index"][7] = "selecionar_opção_por_índice" md["select_option_by_index"][8] = "выбрать_опцию_по_индексу" md["select_option_by_index"][9] = "seleccionar_opción_por_índice" md["select_option_by_value"] = ["*"] * num_langs md["select_option_by_value"][0] = "select_option_by_value" md["select_option_by_value"][1] = "按值选择选项" md["select_option_by_value"][2] = "optie_selecteren_op_waarde" md["select_option_by_value"][3] = "sélectionner_option_par_valeur" md["select_option_by_value"][4] = "selezionare_opzione_per_valore" md["select_option_by_value"][5] = "値でオプションを選択" md["select_option_by_value"][6] = "값별로_옵션_선택" md["select_option_by_value"][7] = "selecionar_opção_por_valor" md["select_option_by_value"][8] = "выбрать_опцию_по_значению" md["select_option_by_value"][9] = "seleccionar_opción_por_valor" md["create_presentation"] = ["*"] * num_langs md["create_presentation"][0] = "create_presentation" md["create_presentation"][1] = "创建演示文稿" md["create_presentation"][2] = "maak_een_presentatie" md["create_presentation"][3] = "créer_une_présentation" md["create_presentation"][4] = "creare_una_presentazione" md["create_presentation"][5] = "プレゼンテーションを作成する" md["create_presentation"][6] = "프레젠테이션_만들기" md["create_presentation"][7] = "criar_uma_apresentação" md["create_presentation"][8] = "создать_презентацию" md["create_presentation"][9] = "crear_una_presentación" md["add_slide"] = ["*"] * num_langs md["add_slide"][0] = "add_slide" md["add_slide"][1] = "添加幻灯片" md["add_slide"][2] = "een_dia_toevoegen" md["add_slide"][3] = "ajouter_une_diapositive" md["add_slide"][4] = "aggiungere_una_diapositiva" md["add_slide"][5] = "スライドを追加する" md["add_slide"][6] = "슬라이드_추가" md["add_slide"][7] = "adicionar_um_slide" md["add_slide"][8] = "добавить_слайд" md["add_slide"][9] = "agregar_una_diapositiva" md["save_presentation"] = ["*"] * num_langs md["save_presentation"][0] = "save_presentation" md["save_presentation"][1] = "保存演示文稿" md["save_presentation"][2] = "de_presentatie_opslaan" md["save_presentation"][3] = "enregistrer_la_présentation" md["save_presentation"][4] = "salva_la_presentazione" md["save_presentation"][5] = "プレゼンテーションを保存する" md["save_presentation"][6] = "프레젠테이션_저장" md["save_presentation"][7] = "salvar_apresentação" md["save_presentation"][8] = "сохранить_презентацию" md["save_presentation"][9] = "guardar_presentación" md["begin_presentation"] = ["*"] * num_langs md["begin_presentation"][0] = "begin_presentation" md["begin_presentation"][1] = "开始演示文稿" md["begin_presentation"][2] = "de_presentatie_starten" md["begin_presentation"][3] = "démarrer_la_présentation" md["begin_presentation"][4] = "avviare_la_presentazione" md["begin_presentation"][5] = "プレゼンテーションを開始する" md["begin_presentation"][6] = "프레젠테이션_시작" md["begin_presentation"][7] = "iniciar_apresentação" md["begin_presentation"][8] = "начать_презентацию" md["begin_presentation"][9] = "iniciar_presentación" md["create_pie_chart"] = ["*"] * num_langs md["create_pie_chart"][0] = "create_pie_chart" md["create_pie_chart"][1] = "创建饼图" md["create_pie_chart"][2] = "maak_een_cirkeldiagram" md["create_pie_chart"][3] = "créer_un_graphique_à_secteurs" md["create_pie_chart"][4] = "creare_un_grafico_a_torta" md["create_pie_chart"][5] = "円グラフを作成する" md["create_pie_chart"][6] = "원형_차트_만들기" md["create_pie_chart"][7] = "criar_um_gráfico_de_pizza" md["create_pie_chart"][8] = "создать_круговую_диаграмму" md["create_pie_chart"][9] = "crear_un_gráfico_circular" md["create_bar_chart"] = ["*"] * num_langs md["create_bar_chart"][0] = "create_bar_chart" md["create_bar_chart"][1] = "创建条形图" md["create_bar_chart"][2] = "maak_een_staafdiagram" md["create_bar_chart"][3] = "créer_un_graphique_à_barres" md["create_bar_chart"][4] = "creare_un_grafico_a_barre" md["create_bar_chart"][5] = "棒グラフを作成する" md["create_bar_chart"][6] = "막대_차트_만들기" md["create_bar_chart"][7] = "criar_um_gráfico_de_barras" md["create_bar_chart"][8] = "создать_бар_диаграмму" md["create_bar_chart"][9] = "crear_un_gráfico_de_barras" md["create_column_chart"] = ["*"] * num_langs md["create_column_chart"][0] = "create_column_chart" md["create_column_chart"][1] = "创建柱形图" md["create_column_chart"][2] = "maak_een_kolomdiagram" md["create_column_chart"][3] = "créer_un_graphique_à_colonnes" md["create_column_chart"][4] = "creare_un_grafico_a_colonne" md["create_column_chart"][5] = "縦棒グラフを作成する" md["create_column_chart"][6] = "열_차트_만들기" md["create_column_chart"][7] = "criar_um_gráfico_de_colunas" md["create_column_chart"][8] = "создать_столбчатую_диаграмму" md["create_column_chart"][9] = "crear_un_gráfico_de_columnas" md["create_line_chart"] = ["*"] * num_langs md["create_line_chart"][0] = "create_line_chart" md["create_line_chart"][1] = "创建折线图" md["create_line_chart"][2] = "maak_een_lijndiagram" md["create_line_chart"][3] = "créer_un_graphique_linéaire" md["create_line_chart"][4] = "creare_un_grafico_a_linee" md["create_line_chart"][5] = "折れ線グラフを作成する" md["create_line_chart"][6] = "선_차트_만들기" md["create_line_chart"][7] = "criar_um_gráfico_de_linhas" md["create_line_chart"][8] = "создать_линейную_диаграмму" md["create_line_chart"][9] = "crear_un_gráfico_de_líneas" md["create_area_chart"] = ["*"] * num_langs md["create_area_chart"][0] = "create_area_chart" md["create_area_chart"][1] = "创建面积图" md["create_area_chart"][2] = "maak_een_vlakdiagram" md["create_area_chart"][3] = "créer_un_graphique_en_aires" md["create_area_chart"][4] = "creare_un_grafico_ad_area" md["create_area_chart"][5] = "面グラフを作成する" md["create_area_chart"][6] = "영역_차트_만들기" md["create_area_chart"][7] = "criar_um_gráfico_de_área" md["create_area_chart"][8] = "создать_диаграмму_области" md["create_area_chart"][9] = "crear_un_gráfico_de_área" md["add_series_to_chart"] = ["*"] * num_langs md["add_series_to_chart"][0] = "add_series_to_chart" md["add_series_to_chart"][1] = "将系列添加到图表" md["add_series_to_chart"][2] = "reeksen_toevoegen_aan_grafiek" md["add_series_to_chart"][3] = "ajouter_séries_au_graphique" md["add_series_to_chart"][4] = "aggiungere_serie_al_grafico" md["add_series_to_chart"][5] = "グラフに系列を追加する" md["add_series_to_chart"][6] = "차트에_시리즈_추가" md["add_series_to_chart"][7] = "adicionar_séries_ao_gráfico" md["add_series_to_chart"][8] = "добавить_серии_в_диаграмму" md["add_series_to_chart"][9] = "agregar_series_al_gráfico" md["add_data_point"] = ["*"] * num_langs md["add_data_point"][0] = "add_data_point" md["add_data_point"][1] = "添加数据点" md["add_data_point"][2] = "gegevenspunt_toevoegen" md["add_data_point"][3] = "ajouter_un_point_de_données" md["add_data_point"][4] = "aggiungi_punto_dati" md["add_data_point"][5] = "データポイントを追加する" md["add_data_point"][6] = "데이터_포인트_추가" md["add_data_point"][7] = "adicionar_ponto_de_dados" md["add_data_point"][8] = "добавить_точку_данных" md["add_data_point"][9] = "agregar_punto_de_datos" md["save_chart"] = ["*"] * num_langs md["save_chart"][0] = "save_chart" md["save_chart"][1] = "保存图表" md["save_chart"][2] = "grafiek_opslaan" md["save_chart"][3] = "enregistrer_le_graphique" md["save_chart"][4] = "salva_il_grafico" md["save_chart"][5] = "グラフを保存する" md["save_chart"][6] = "차트_저장" md["save_chart"][7] = "salvar_gráfico" md["save_chart"][8] = "сохранить_диаграмму" md["save_chart"][9] = "guardar_gráfico" md["display_chart"] = ["*"] * num_langs md["display_chart"][0] = "display_chart" md["display_chart"][1] = "显示图表" md["display_chart"][2] = "grafiek_weergeven" md["display_chart"][3] = "afficher_le_graphique" md["display_chart"][4] = "mostra_il_grafico" md["display_chart"][5] = "グラフを表示する" md["display_chart"][6] = "차트_표시" md["display_chart"][7] = "exibir_gráfico" md["display_chart"][8] = "отображать_диаграмму" md["display_chart"][9] = "muestra_gráfico" md["extract_chart"] = ["*"] * num_langs md["extract_chart"][0] = "extract_chart" md["extract_chart"][1] = "提取图表" md["extract_chart"][2] = "grafiek_uitpakken" md["extract_chart"][3] = "extraire_le_graphique" md["extract_chart"][4] = "estrarre_il_grafico" md["extract_chart"][5] = "グラフを抽出する" md["extract_chart"][6] = "차트_추출" md["extract_chart"][7] = "extrair_gráfico" md["extract_chart"][8] = "извлекать_диаграмму" md["extract_chart"][9] = "extracto_gráfico" md["create_tour"] = ["*"] * num_langs md["create_tour"][0] = "create_tour" md["create_tour"][1] = "创建游览" md["create_tour"][2] = "maak_een_tour" md["create_tour"][3] = "créer_une_visite" md["create_tour"][4] = "creare_un_tour" md["create_tour"][5] = "ツアーを作成する" md["create_tour"][6] = "가이드_투어_만들기" md["create_tour"][7] = "criar_um_tour" md["create_tour"][8] = "создать_тур" md["create_tour"][9] = "crear_una_gira" md["create_shepherd_tour"] = ["*"] * num_langs md["create_shepherd_tour"][0] = "create_shepherd_tour" md["create_shepherd_tour"][1] = "创建SHEPHERD游览" md["create_shepherd_tour"][2] = "maak_een_shepherd_tour" md["create_shepherd_tour"][3] = "créer_une_visite_shepherd" md["create_shepherd_tour"][4] = "creare_un_tour_shepherd" md["create_shepherd_tour"][5] = "SHEPHERDツアーを作成する" md["create_shepherd_tour"][6] = "가이드_SHEPHERD_투어_만들기" md["create_shepherd_tour"][7] = "criar_um_tour_shepherd" md["create_shepherd_tour"][8] = "создать_SHEPHERD_тур" md["create_shepherd_tour"][9] = "crear_una_gira_shepherd" md["create_bootstrap_tour"] = ["*"] * num_langs md["create_bootstrap_tour"][0] = "create_bootstrap_tour" md["create_bootstrap_tour"][1] = "创建BOOTSTRAP游览" md["create_bootstrap_tour"][2] = "maak_een_bootstrap_tour" md["create_bootstrap_tour"][3] = "créer_une_visite_bootstrap" md["create_bootstrap_tour"][4] = "creare_un_tour_bootstrap" md["create_bootstrap_tour"][5] = "BOOTSTRAPツアーを作成する" md["create_bootstrap_tour"][6] = "가이드_BOOTSTRAP_투어_만들기" md["create_bootstrap_tour"][7] = "criar_um_tour_bootstrap" md["create_bootstrap_tour"][8] = "создать_BOOTSTRAP_тур" md["create_bootstrap_tour"][9] = "crear_una_gira_bootstrap" md["create_driverjs_tour"] = ["*"] * num_langs md["create_driverjs_tour"][0] = "create_driverjs_tour" md["create_driverjs_tour"][1] = "创建DRIVERJS游览" md["create_driverjs_tour"][2] = "maak_een_driverjs_tour" md["create_driverjs_tour"][3] = "créer_une_visite_driverjs" md["create_driverjs_tour"][4] = "creare_un_tour_driverjs" md["create_driverjs_tour"][5] = "DRIVERJSツアーを作成する" md["create_driverjs_tour"][6] = "가이드_DRIVERJS_투어_만들기" md["create_driverjs_tour"][7] = "criar_um_tour_driverjs" md["create_driverjs_tour"][8] = "создать_DRIVERJS_тур" md["create_driverjs_tour"][9] = "crear_una_gira_driverjs" md["create_hopscotch_tour"] = ["*"] * num_langs md["create_hopscotch_tour"][0] = "create_hopscotch_tour" md["create_hopscotch_tour"][1] = "创建HOPSCOTCH游览" md["create_hopscotch_tour"][2] = "maak_een_hopscotch_tour" md["create_hopscotch_tour"][3] = "créer_une_visite_hopscotch" md["create_hopscotch_tour"][4] = "creare_un_tour_hopscotch" md["create_hopscotch_tour"][5] = "HOPSCOTCHツアーを作成する" md["create_hopscotch_tour"][6] = "가이드_HOPSCOTCH_투어_만들기" md["create_hopscotch_tour"][7] = "criar_um_tour_hopscotch" md["create_hopscotch_tour"][8] = "создать_HOPSCOTCH_тур" md["create_hopscotch_tour"][9] = "crear_una_gira_hopscotch" md["create_introjs_tour"] = ["*"] * num_langs md["create_introjs_tour"][0] = "create_introjs_tour" md["create_introjs_tour"][1] = "创建INTROJS游览" md["create_introjs_tour"][2] = "maak_een_introjs_tour" md["create_introjs_tour"][3] = "créer_une_visite_introjs" md["create_introjs_tour"][4] = "creare_un_tour_introjs" md["create_introjs_tour"][5] = "INTROJSツアーを作成する" md["create_introjs_tour"][6] = "가이드_INTROJS_투어_만들기" md["create_introjs_tour"][7] = "criar_um_tour_introjs" md["create_introjs_tour"][8] = "создать_INTROJS_тур" md["create_introjs_tour"][9] = "crear_una_gira_introjs" md["add_tour_step"] = ["*"] * num_langs md["add_tour_step"][0] = "add_tour_step" md["add_tour_step"][1] = "添加游览步骤" md["add_tour_step"][2] = "toevoegen_tour_stap" md["add_tour_step"][3] = "ajouter_étape_à_la_visite" md["add_tour_step"][4] = "aggiungere_passo_al_tour" md["add_tour_step"][5] = "ツアーステップを追加する" md["add_tour_step"][6] = "둘러보기_단계_추가" md["add_tour_step"][7] = "adicionar_passo_para_o_tour" md["add_tour_step"][8] = "добавить_шаг_в_тур" md["add_tour_step"][9] = "agregar_paso_a_la_gira" md["play_tour"] = ["*"] * num_langs md["play_tour"][0] = "play_tour" md["play_tour"][1] = "播放游览" md["play_tour"][2] = "speel_de_tour" md["play_tour"][3] = "jouer_la_visite" md["play_tour"][4] = "riprodurre_il_tour" md["play_tour"][5] = "ツアーを再生する" md["play_tour"][6] = "가이드_투어를하다" md["play_tour"][7] = "jogar_o_tour" md["play_tour"][8] = "играть_тур" md["play_tour"][9] = "reproducir_la_gira" md["export_tour"] = ["*"] * num_langs md["export_tour"][0] = "export_tour" md["export_tour"][1] = "导出游览" md["export_tour"][2] = "de_tour_exporteren" md["export_tour"][3] = "exporter_la_visite" md["export_tour"][4] = "esportare_il_tour" md["export_tour"][5] = "ツアーをエクスポートする" md["export_tour"][6] = "가이드_투어_내보내기" md["export_tour"][7] = "exportar_o_tour" md["export_tour"][8] = "экспортировать_тур" md["export_tour"][9] = "exportar_la_gira" md["get_pdf_text"] = ["*"] * num_langs md["get_pdf_text"][0] = "get_pdf_text" md["get_pdf_text"][1] = "获取PDF文本" md["get_pdf_text"][2] = "pdf_tekst_ophalen" md["get_pdf_text"][3] = "obtenir_texte_pdf" md["get_pdf_text"][4] = "ottenere_testo_pdf" md["get_pdf_text"][5] = "PDFテキストを取得" md["get_pdf_text"][6] = "PDF_텍스트를_검색" md["get_pdf_text"][7] = "obter_texto_pdf" md["get_pdf_text"][8] = "получить_текст_PDF" md["get_pdf_text"][9] = "obtener_texto_pdf" md["assert_pdf_text"] = ["*"] * num_langs md["assert_pdf_text"][0] = "assert_pdf_text" md["assert_pdf_text"][1] = "断言PDF文本" md["assert_pdf_text"][2] = "controleren_pdf_tekst" md["assert_pdf_text"][3] = "vérifier_texte_pdf" md["assert_pdf_text"][4] = "verificare_testo_pdf" md["assert_pdf_text"][5] = "PDFテキストを確認する" md["assert_pdf_text"][6] = "PDF_텍스트_확인" md["assert_pdf_text"][7] = "verificar_texto_pdf" md["assert_pdf_text"][8] = "подтвердить_текст_PDF" md["assert_pdf_text"][9] = "verificar_texto_pdf" md["download_file"] = ["*"] * num_langs md["download_file"][0] = "download_file" md["download_file"][1] = "下载文件" md["download_file"][2] = "bestand_downloaden" md["download_file"][3] = "télécharger_fichier" md["download_file"][4] = "scaricare_file" md["download_file"][5] = "ファイルをダウンロード" md["download_file"][6] = "파일_다운로드" md["download_file"][7] = "baixar_arquivo" md["download_file"][8] = "скачать_файл" md["download_file"][9] = "descargar_archivo" md["is_downloaded_file_present"] = ["*"] * num_langs md["is_downloaded_file_present"][0] = "is_downloaded_file_present" md["is_downloaded_file_present"][1] = "下载的文件是否存在" md["is_downloaded_file_present"][2] = "gedownloade_bestand_aanwezig" md["is_downloaded_file_present"][3] = "est_un_fichier_téléchargé_présent" md["is_downloaded_file_present"][4] = "è_file_scaricato_presente" md["is_downloaded_file_present"][5] = "ダウンロードしたファイルが存在するかどうか" md["is_downloaded_file_present"][6] = "다운로드한_파일이_있습니다" md["is_downloaded_file_present"][7] = "o_arquivo_baixado_está_presente" md["is_downloaded_file_present"][8] = "загруженный_файл_присутствует" md["is_downloaded_file_present"][9] = "está_presente_el_archivo_descargado" md["get_path_of_downloaded_file"] = ["*"] * num_langs md["get_path_of_downloaded_file"][0] = "get_path_of_downloaded_file" md["get_path_of_downloaded_file"][1] = "获取下载的文件路径" md["get_path_of_downloaded_file"][2] = "pad_gedownloade_bestand_ophalen" gpodf_fr = "obtenir_chemin_du_fichier_téléchargé" md["get_path_of_downloaded_file"][3] = gpodf_fr gpodf_it = "ottenere_percorso_del_file_scaricato" md["get_path_of_downloaded_file"][4] = gpodf_it md["get_path_of_downloaded_file"][5] = "ダウンロードしたファイルパスを取得する" md["get_path_of_downloaded_file"][6] = "다운로드한_파일_경로_가져_오기" md["get_path_of_downloaded_file"][7] = "obter_caminho_do_arquivo_baixado" md["get_path_of_downloaded_file"][8] = "получить_путь_к_загруженному_файлу" gpodf_es = "obtener_ruta_del_archivo_descargado" md["get_path_of_downloaded_file"][9] = gpodf_es md["assert_downloaded_file"] = ["*"] * num_langs md["assert_downloaded_file"][0] = "assert_downloaded_file" md["assert_downloaded_file"][1] = "检查下载的文件" md["assert_downloaded_file"][2] = "controleren_gedownloade_bestand" md["assert_downloaded_file"][3] = "vérifier_fichier_téléchargé" md["assert_downloaded_file"][4] = "verificare_file_scaricato" md["assert_downloaded_file"][5] = "ダウンロードしたファイルを確認する" md["assert_downloaded_file"][6] = "다운로드한_파일_확인" md["assert_downloaded_file"][7] = "verificar_arquivo_baixado" md["assert_downloaded_file"][8] = "подтвердить_загруженный_файл" md["assert_downloaded_file"][9] = "verificar_archivo_descargado" md["delete_downloaded_file"] = ["*"] * num_langs md["delete_downloaded_file"][0] = "delete_downloaded_file" md["delete_downloaded_file"][1] = "删除下载的文件" md["delete_downloaded_file"][2] = "verwijder_gedownloade_bestand" md["delete_downloaded_file"][3] = "supprimer_fichier_téléchargé" md["delete_downloaded_file"][4] = "eliminare_file_scaricato" md["delete_downloaded_file"][5] = "ダウンロードしたファイルを削除する" md["delete_downloaded_file"][6] = "다운로드한_파일_삭제" md["delete_downloaded_file"][7] = "exclua_arquivo_baixado" md["delete_downloaded_file"][8] = "удалить_загруженный_файл" md["delete_downloaded_file"][9] = "eliminar_archivo_descargado" md["fail"] = ["*"] * num_langs md["fail"][0] = "fail" md["fail"][1] = "失败" md["fail"][2] = "mislukken" md["fail"][3] = "échouer" md["fail"][4] = "fallire" md["fail"][5] = "失敗" md["fail"][6] = "실패" md["fail"][7] = "falhar" md["fail"][8] = "провалить" md["fail"][9] = "fallar" md["get"] = ["*"] * num_langs md["get"][0] = "get" md["get"][1] = "获取" md["get"][2] = "ophalen" md["get"][3] = "obtenir" md["get"][4] = "ottenere" md["get"][5] = "を取得する" md["get"][6] = "받기" md["get"][7] = "obter" md["get"][8] = "получить" md["get"][9] = "obtener" md["visit"] = ["*"] * num_langs md["visit"][0] = "visit" md["visit"][1] = "访问" md["visit"][2] = "bezoek" md["visit"][3] = "visiter" md["visit"][4] = "visita" md["visit"][5] = "を訪問" md["visit"][6] = "방문" md["visit"][7] = "visitar" md["visit"][8] = "посетить" md["visit"][9] = "visita" md["visit_url"] = ["*"] * num_langs md["visit_url"][0] = "visit_url" md["visit_url"][1] = "访问网址" md["visit_url"][2] = "bezoek_url" md["visit_url"][3] = "visiter_url" md["visit_url"][4] = "visita_url" md["visit_url"][5] = "URLを訪問" md["visit_url"][6] = "방문_URL" md["visit_url"][7] = "visitar_url" md["visit_url"][8] = "посетить_URL" md["visit_url"][9] = "visita_url" md["get_element"] = ["*"] * num_langs md["get_element"][0] = "get_element" md["get_element"][1] = "获取元素" md["get_element"][2] = "element_ophalen" md["get_element"][3] = "obtenir_élément" md["get_element"][4] = "ottenere_elemento" md["get_element"][5] = "要素を取得する" md["get_element"][6] = "요소_검색" md["get_element"][7] = "obter_elemento" md["get_element"][8] = "получить_элемент" md["get_element"][9] = "obtener_elemento" md["find_element"] = ["*"] * num_langs md["find_element"][0] = "find_element" md["find_element"][1] = "查找元素" md["find_element"][2] = "vind_element" md["find_element"][3] = "trouver_élément" md["find_element"][4] = "trovare_elemento" md["find_element"][5] = "要素を見つける" md["find_element"][6] = "요소를_찾을" md["find_element"][7] = "encontrar_elemento" md["find_element"][8] = "найти_элемент" md["find_element"][9] = "encontrar_elemento" md["remove_element"] = ["*"] * num_langs md["remove_element"][0] = "remove_element" md["remove_element"][1] = "删除第一个元素" md["remove_element"][2] = "verwijder_element" md["remove_element"][3] = "supprimer_élément" md["remove_element"][4] = "rimuovere_elemento" md["remove_element"][5] = "最初の要素を削除" md["remove_element"][6] = "첫_번째_요소_제거" md["remove_element"][7] = "remover_elemento" md["remove_element"][8] = "удалить_элемент" md["remove_element"][9] = "eliminar_elemento" md["remove_elements"] = ["*"] * num_langs md["remove_elements"][0] = "remove_elements" md["remove_elements"][1] = "删除所有元素" md["remove_elements"][2] = "verwijder_elementen" md["remove_elements"][3] = "supprimer_éléments" md["remove_elements"][4] = "rimuovere_elementi" md["remove_elements"][5] = "すべての要素を削除" md["remove_elements"][6] = "모든_요소_제거" md["remove_elements"][7] = "remover_elementos" md["remove_elements"][8] = "удалить_элементы" md["remove_elements"][9] = "eliminar_elementos" md["find_text"] = ["*"] * num_langs md["find_text"][0] = "find_text" md["find_text"][1] = "查找文本" md["find_text"][2] = "vind_tekst" md["find_text"][3] = "trouver_texte" md["find_text"][4] = "trovare_testo" md["find_text"][5] = "テキストを見つける" md["find_text"][6] = "텍스트_찾기" md["find_text"][7] = "encontrar_texto" md["find_text"][8] = "найти_текст" md["find_text"][9] = "encontrar_texto" md["set_text"] = ["*"] * num_langs md["set_text"][0] = "set_text" md["set_text"][1] = "设置文本" md["set_text"][2] = "tekst_instellen" md["set_text"][3] = "définir_texte" md["set_text"][4] = "impostare_testo" md["set_text"][5] = "テキストを設定する" md["set_text"][6] = "텍스트_설정" md["set_text"][7] = "definir_texto" md["set_text"][8] = "набор_текст" md["set_text"][9] = "establecer_texto" md["get_attribute"] = ["*"] * num_langs md["get_attribute"][0] = "get_attribute" md["get_attribute"][1] = "获取属性" md["get_attribute"][2] = "attribuut_ophalen" md["get_attribute"][3] = "obtenir_attribut" md["get_attribute"][4] = "ottenere_attributo" md["get_attribute"][5] = "属性を取得する" md["get_attribute"][6] = "특성_검색" md["get_attribute"][7] = "obter_atributo" md["get_attribute"][8] = "получить_атрибут" md["get_attribute"][9] = "obtener_atributo" md["set_attribute"] = ["*"] * num_langs md["set_attribute"][0] = "set_attribute" md["set_attribute"][1] = "设置属性" md["set_attribute"][2] = "attribuut_instellen" md["set_attribute"][3] = "définir_attribut" md["set_attribute"][4] = "imposta_attributo" md["set_attribute"][5] = "属性を設定する" md["set_attribute"][6] = "특성_설정" md["set_attribute"][7] = "definir_atributo" md["set_attribute"][8] = "набор_атрибута" md["set_attribute"][9] = "establecer_atributo" md["set_attributes"] = ["*"] * num_langs md["set_attributes"][0] = "set_attributes" md["set_attributes"][1] = "设置所有属性" md["set_attributes"][2] = "attributen_instellen" md["set_attributes"][3] = "définir_attributs" md["set_attributes"][4] = "impostare_gli_attributi" md["set_attributes"][5] = "すべての属性を設定" md["set_attributes"][6] = "모든_특성_설정" md["set_attributes"][7] = "definir_atributos" md["set_attributes"][8] = "набор_атрибутов" md["set_attributes"][9] = "establecer_atributos" md["set_content"] = ["*"] * num_langs md["set_content"][0] = "set_content" md["set_content"][1] = "设置HTML" md["set_content"][2] = "html_instellen" md["set_content"][3] = "définir_html" md["set_content"][4] = "impostare_html" md["set_content"][5] = "HTML設定する" md["set_content"][6] = "HTML_설정" md["set_content"][7] = "definir_html" md["set_content"][8] = "набор_HTML" md["set_content"][9] = "establecer_html" md["type"] = ["*"] * num_langs md["type"][0] = "type" md["type"][1] = "输入文本" md["type"][2] = "typ" md["type"][3] = "taper" md["type"][4] = "digitare" md["type"][5] = "入力" md["type"][6] = "입력" md["type"][7] = "digitar" md["type"][8] = "введите" md["type"][9] = "escriba" md["write"] = ["*"] * num_langs md["write"][0] = "write" md["write"][1] = "写文本" md["write"][2] = "schrijven" md["write"][3] = "écriver" md["write"][4] = "scrivere" md["write"][5] = "書く" md["write"][6] = "쓰다" md["write"][7] = "escreva" md["write"][8] = "написать" md["write"][9] = "escribir" md["set_messenger_theme"] = ["*"] * num_langs md["set_messenger_theme"][0] = "set_messenger_theme" md["set_messenger_theme"][1] = "设置消息主题" md["set_messenger_theme"][2] = "thema_van_bericht_instellen" md["set_messenger_theme"][3] = "définir_thème_du_message" md["set_messenger_theme"][4] = "impostare_tema_del_messaggio" md["set_messenger_theme"][5] = "メッセージのスタイルを設定する" md["set_messenger_theme"][6] = "메시지_테마_설정" md["set_messenger_theme"][7] = "definir_tema_da_mensagem" md["set_messenger_theme"][8] = "набор_тему_сообщения" md["set_messenger_theme"][9] = "establecer_tema_del_mensaje" md["post_message"] = ["*"] * num_langs md["post_message"][0] = "post_message" md["post_message"][1] = "显示讯息" md["post_message"][2] = "bericht_weergeven" md["post_message"][3] = "afficher_message" md["post_message"][4] = "visualizza_messaggio" md["post_message"][5] = "メッセージを表示する" md["post_message"][6] = "메시지를_표시" md["post_message"][7] = "exibir_mensagem" md["post_message"][8] = "показать_сообщение" md["post_message"][9] = "mostrar_mensaje" md["_print"] = ["*"] * num_langs md["_print"][0] = "_print" md["_print"][1] = "打印" md["_print"][2] = "afdrukken" md["_print"][3] = "imprimer" md["_print"][4] = "stampare" md["_print"][5] = "印刷" md["_print"][6] = "인쇄" md["_print"][7] = "imprimir" md["_print"][8] = "печатать" md["_print"][9] = "imprimir" md["deferred_assert_element"] = ["*"] * num_langs md["deferred_assert_element"][0] = "deferred_assert_element" md["deferred_assert_element"][1] = "推迟断言元素" md["deferred_assert_element"][2] = "uitgestelde_controleren_element" md["deferred_assert_element"][3] = "reporté_vérifier_élément" md["deferred_assert_element"][4] = "differita_verificare_elemento" md["deferred_assert_element"][5] = "を延期する要素を確認する" md["deferred_assert_element"][6] = "연기된_요소_확인" md["deferred_assert_element"][7] = "adiada_verificar_elemento" md["deferred_assert_element"][8] = "отложенный_подтвердить_элемент" md["deferred_assert_element"][9] = "diferido_verificar_elemento" md["deferred_assert_text"] = ["*"] * num_langs md["deferred_assert_text"][0] = "deferred_assert_text" md["deferred_assert_text"][1] = "推迟断言文本" md["deferred_assert_text"][2] = "uitgestelde_controleren_tekst" md["deferred_assert_text"][3] = "reporté_vérifier_texte" md["deferred_assert_text"][4] = "differita_verificare_testo" md["deferred_assert_text"][5] = "を延期するテキストを確認する" md["deferred_assert_text"][6] = "연기된_텍스트_확인" md["deferred_assert_text"][7] = "adiada_verificar_texto" md["deferred_assert_text"][8] = "отложенный_подтвердить_текст" md["deferred_assert_text"][9] = "diferido_verificar_texto" md["process_deferred_asserts"] = ["*"] * num_langs md["process_deferred_asserts"][0] = "process_deferred_asserts" md["process_deferred_asserts"][1] = "处理推迟断言" md["process_deferred_asserts"][2] = "verwerken_uitgestelde_controleren" md["process_deferred_asserts"][3] = "effectuer_vérifications_reportées" md["process_deferred_asserts"][4] = "elaborare_differita_verificari" md["process_deferred_asserts"][5] = "遅延アサーションの処理" md["process_deferred_asserts"][6] = "연기된_검증_처리" md["process_deferred_asserts"][7] = "processar_verificações_adiada" md["process_deferred_asserts"][8] = "обработки_отложенных_подтверждений" md["process_deferred_asserts"][9] = "procesar_verificaciones_diferidas" md["accept_alert"] = ["*"] * num_langs md["accept_alert"][0] = "accept_alert" md["accept_alert"][1] = "接受警报" md["accept_alert"][2] = "waarschuwing_accepteren" md["accept_alert"][3] = "accepter_alerte" md["accept_alert"][4] = "accetta_avviso" md["accept_alert"][5] = "アラートを受け入れる" md["accept_alert"][6] = "경고를_수락" md["accept_alert"][7] = "aceitar_alerta" md["accept_alert"][8] = "принять_оповещение" md["accept_alert"][9] = "aceptar_alerta" md["dismiss_alert"] = ["*"] * num_langs md["dismiss_alert"][0] = "dismiss_alert" md["dismiss_alert"][1] = "解除警报" md["dismiss_alert"][2] = "waarschuwing_wegsturen" md["dismiss_alert"][3] = "rejeter_alerte" md["dismiss_alert"][4] = "elimina_avviso" md["dismiss_alert"][5] = "アラートを却下" md["dismiss_alert"][6] = "경고를_거부" md["dismiss_alert"][7] = "demitir_alerta" md["dismiss_alert"][8] = "увольнять_оповещение" md["dismiss_alert"][9] = "descartar_alerta" md["switch_to_alert"] = ["*"] * num_langs md["switch_to_alert"][0] = "switch_to_alert" md["switch_to_alert"][1] = "切换到警报" md["switch_to_alert"][2] = "overschakelen_naar_waarschuwing" md["switch_to_alert"][3] = "passer_à_alerte" md["switch_to_alert"][4] = "passa_al_avviso" md["switch_to_alert"][5] = "アラートに切り替え" md["switch_to_alert"][6] = "경고로_전환" md["switch_to_alert"][7] = "mudar_para_alerta" md["switch_to_alert"][8] = "переключиться_на_оповещение" md["switch_to_alert"][9] = "cambiar_a_alerta" md["drag_and_drop"] = ["*"] * num_langs md["drag_and_drop"][0] = "drag_and_drop" md["drag_and_drop"][1] = "拖放" md["drag_and_drop"][2] = "slepen_en_neerzetten" md["drag_and_drop"][3] = "glisser_et_déposer" md["drag_and_drop"][4] = "trascinare_e_rilasciare" md["drag_and_drop"][5] = "ドラッグアンドドロップ" md["drag_and_drop"][6] = "드래그_앤_드롭" md["drag_and_drop"][7] = "arrastar_e_soltar" md["drag_and_drop"][8] = "перетащить_и_падение" md["drag_and_drop"][9] = "arrastrar_y_soltar" md["load_html_file"] = ["*"] * num_langs md["load_html_file"][0] = "load_html_file" md["load_html_file"][1] = "加载HTML文件" md["load_html_file"][2] = "html_bestand_laden" md["load_html_file"][3] = "charger_html_fichier" md["load_html_file"][4] = "caricare_html_file" md["load_html_file"][5] = "HTMLファイルを読み込む" md["load_html_file"][6] = "HTML_파일_로드" md["load_html_file"][7] = "carregar_arquivo_html" md["load_html_file"][8] = "загрузить_HTML_файл" md["load_html_file"][9] = "cargar_archivo_html" md["open_html_file"] = ["*"] * num_langs md["open_html_file"][0] = "open_html_file" md["open_html_file"][1] = "打开HTML文件" md["open_html_file"][2] = "html_bestand_openen" md["open_html_file"][3] = "ouvrir_html_fichier" md["open_html_file"][4] = "apri_html_file" md["open_html_file"][5] = "HTMLファイルを開く" md["open_html_file"][6] = "HTML_파일_열기" md["open_html_file"][7] = "abrir_arquivo_html" md["open_html_file"][8] = "открыть_HTML_файл" md["open_html_file"][9] = "abrir_archivo_html" md["delete_all_cookies"] = ["*"] * num_langs md["delete_all_cookies"][0] = "delete_all_cookies" md["delete_all_cookies"][1] = "删除所有COOKIE" md["delete_all_cookies"][2] = "alle_cookies_verwijderen" md["delete_all_cookies"][3] = "supprimer_tous_les_cookies" md["delete_all_cookies"][4] = "elimina_tutti_i_cookie" md["delete_all_cookies"][5] = "すべてのクッキーを削除する" md["delete_all_cookies"][6] = "모든_쿠키_삭제" md["delete_all_cookies"][7] = "excluir_todos_os_cookies" md["delete_all_cookies"][8] = "удалить_все_куки" md["delete_all_cookies"][9] = "eliminar_todas_las_cookies" md["get_user_agent"] = ["*"] * num_langs md["get_user_agent"][0] = "get_user_agent" md["get_user_agent"][1] = "获取用户代理" md["get_user_agent"][2] = "gebruikersagent_ophalen" md["get_user_agent"][3] = "obtenir_agent_utilisateur" md["get_user_agent"][4] = "ottenere_agente_utente" md["get_user_agent"][5] = "ユーザーエージェントの取得" md["get_user_agent"][6] = "사용자_에이전트_가져_오기" md["get_user_agent"][7] = "obter_agente_do_usuário" md["get_user_agent"][8] = "получить_агента_пользователя" md["get_user_agent"][9] = "obtener_agente_de_usuario" md["get_locale_code"] = ["*"] * num_langs md["get_locale_code"][0] = "get_locale_code" md["get_locale_code"][1] = "获取语言代码" md["get_locale_code"][2] = "taalcode_ophalen" md["get_locale_code"][3] = "obtenir_code_de_langue" md["get_locale_code"][4] = "ottenere_codice_lingua" md["get_locale_code"][5] = "言語コードを取得する" md["get_locale_code"][6] = "언어_코드를_얻을" md["get_locale_code"][7] = "obter_código_de_idioma" md["get_locale_code"][8] = "получить_код_языка" md["get_locale_code"][9] = "obtener_código_de_idioma" ################ # Duplicates # "input" -> duplicate of "type" md["input"] = ["*"] * num_langs md["input"][0] = "input" md["input"][1] = "输入文本" md["input"][2] = "typ" md["input"][3] = "taper" md["input"][4] = "digitare" md["input"][5] = "入力" md["input"][6] = "입력" md["input"][7] = "digitar" md["input"][8] = "введите" md["input"][9] = "escriba" # "fill" -> duplicate of "type" md["fill"] = ["*"] * num_langs md["fill"][0] = "fill" md["fill"][1] = "输入文本" md["fill"][2] = "typ" md["fill"][3] = "taper" md["fill"][4] = "digitare" md["fill"][5] = "入力" md["fill"][6] = "입력" md["fill"][7] = "digitar" md["fill"][8] = "введите" md["fill"][9] = "escriba" # "goto" -> duplicate of "visit" md["goto"] = ["*"] * num_langs md["goto"][0] = "goto" md["goto"][1] = "访问" md["goto"][2] = "bezoek" md["goto"][3] = "visiter" md["goto"][4] = "visita" md["goto"][5] = "を訪問" md["goto"][6] = "방문" md["goto"][7] = "visitar" md["goto"][8] = "посетить" md["goto"][9] = "visita" # "go_to" -> duplicate of "visit" md["go_to"] = ["*"] * num_langs md["go_to"][0] = "go_to" md["go_to"][1] = "访问" md["go_to"][2] = "bezoek" md["go_to"][3] = "visiter" md["go_to"][4] = "visita" md["go_to"][5] = "を訪問" md["go_to"][6] = "방문" md["go_to"][7] = "visitar" md["go_to"][8] = "посетить" md["go_to"][9] = "visita" # "refresh" -> duplicate of "refresh_page" md["refresh"] = ["*"] * num_langs md["refresh"][0] = "refresh" md["refresh"][1] = "刷新页面" md["refresh"][2] = "ververs_pagina" md["refresh"][3] = "rafraîchir_la_page" md["refresh"][4] = "aggiorna_la_pagina" md["refresh"][5] = "ページを更新する" md["refresh"][6] = "페이지_새로_고침" md["refresh"][7] = "atualizar_a_página" md["refresh"][8] = "обновить_страницу" md["refresh"][9] = "actualizar_la_página" # "reload" -> duplicate of "refresh_page" md["reload"] = ["*"] * num_langs md["reload"][0] = "reload" md["reload"][1] = "刷新页面" md["reload"][2] = "ververs_pagina" md["reload"][3] = "rafraîchir_la_page" md["reload"][4] = "aggiorna_la_pagina" md["reload"][5] = "ページを更新する" md["reload"][6] = "페이지_새로_고침" md["reload"][7] = "atualizar_a_página" md["reload"][8] = "обновить_страницу" md["reload"][9] = "actualizar_la_página" # "reload_page" -> duplicate of "refresh_page" md["reload_page"] = ["*"] * num_langs md["reload_page"][0] = "reload_page" md["reload_page"][1] = "刷新页面" md["reload_page"][2] = "ververs_pagina" md["reload_page"][3] = "rafraîchir_la_page" md["reload_page"][4] = "aggiorna_la_pagina" md["reload_page"][5] = "ページを更新する" md["reload_page"][6] = "페이지_새로_고침" md["reload_page"][7] = "atualizar_a_página" md["reload_page"][8] = "обновить_страницу" md["reload_page"][9] = "actualizar_la_página" # "open_new_tab" -> duplicate of "open_new_window" md["open_new_tab"] = ["*"] * num_langs md["open_new_tab"][0] = "open_new_tab" md["open_new_tab"][1] = "打开新窗口" md["open_new_tab"][2] = "nieuw_venster_openen" md["open_new_tab"][3] = "ouvrir_une_nouvelle_fenêtre" md["open_new_tab"][4] = "apri_una_nuova_finestra" md["open_new_tab"][5] = "新しいウィンドウを開く" md["open_new_tab"][6] = "새_창_열기" md["open_new_tab"][7] = "abrir_nova_janela" md["open_new_tab"][8] = "открыть_новое_окно" md["open_new_tab"][9] = "abrir_una_nueva_ventana" # "switch_to_newest_tab" -> duplicate of "switch_to_newest_window" md["switch_to_newest_tab"] = ["*"] * num_langs md["switch_to_newest_tab"][0] = "switch_to_newest_tab" md["switch_to_newest_tab"][1] = "切换到最新的窗口" md["switch_to_newest_tab"][2] = "overschakelen_naar_nieuwste_venster" md["switch_to_newest_tab"][3] = "passer_à_fenêtre_dernière" md["switch_to_newest_tab"][4] = "passa_alla_finestra_ultimo" md["switch_to_newest_tab"][5] = "最新のウィンドウに切り替えます" md["switch_to_newest_tab"][6] = "최신_창으로_전환" md["switch_to_newest_tab"][7] = "mudar_para_a_janela_última" md["switch_to_newest_tab"][8] = "переключиться_на_последнее_окно" md["switch_to_newest_tab"][9] = "cambiar_a_ventana_última" # "get_page_title" -> duplicate of "get_title" md["get_page_title"] = ["*"] * num_langs md["get_page_title"][0] = "get_page_title" md["get_page_title"][1] = "获取标题" md["get_page_title"][2] = "titel_ophalen" md["get_page_title"][3] = "obtenir_le_titre" md["get_page_title"][4] = "ottenere_il_titolo" md["get_page_title"][5] = "タイトルを取得する" md["get_page_title"][6] = "제목_검색" md["get_page_title"][7] = "obter_título" md["get_page_title"][8] = "получить_название" md["get_page_title"][9] = "obtener_título" # "click_link_text" -> duplicate of "click_link" md["click_link_text"] = ["*"] * num_langs md["click_link_text"][0] = "click_link_text" md["click_link_text"][1] = "单击链接文本" md["click_link_text"][2] = "klik_linktekst" md["click_link_text"][3] = "cliquer_texte_du_lien" md["click_link_text"][4] = "clic_testo_del_collegamento" md["click_link_text"][5] = "リンクテキストをクリックします" md["click_link_text"][6] = "링크_텍스트를_클릭합니다" md["click_link_text"][7] = "clique_texto_do_link" md["click_link_text"][8] = "нажмите_ссылку" md["click_link_text"][9] = "clic_texto_del_enlace" # "right_click" -> duplicate of "context_click" md["right_click"] = ["*"] * num_langs md["right_click"][0] = "right_click" md["right_click"][1] = "上下文点击" md["right_click"][2] = "contextklik" md["right_click"][3] = "contextuel_cliquer" md["right_click"][4] = "clic_contestuale" md["right_click"][5] = "コンテキストクリック" md["right_click"][6] = "컨텍스트_클릭" md["right_click"][7] = "clique_de_contexto" md["right_click"][8] = "контекстный_щелчок" md["right_click"][9] = "clic_de_contexto" # "send_keys" -> duplicate of "add_text" md["send_keys"] = ["*"] * num_langs md["send_keys"][0] = "send_keys" md["send_keys"][1] = "添加文本" md["send_keys"][2] = "tekst_toevoegen" md["send_keys"][3] = "ajouter_texte" md["send_keys"][4] = "aggiungi_testo" md["send_keys"][5] = "テキストを追加" md["send_keys"][6] = "텍스트를_추가" md["send_keys"][7] = "adicionar_texto" md["send_keys"][8] = "добавить_текст" md["send_keys"][9] = "agregar_texto" # "load_html_string" -> duplicate of "set_content" md["load_html_string"] = ["*"] * num_langs md["load_html_string"][0] = "load_html_string" md["load_html_string"][1] = "设置HTML" md["load_html_string"][2] = "html_instellen" md["load_html_string"][3] = "définir_html" md["load_html_string"][4] = "impostare_html" md["load_html_string"][5] = "HTML設定する" md["load_html_string"][6] = "HTML_설정" md["load_html_string"][7] = "definir_html" md["load_html_string"][8] = "набор_HTML" md["load_html_string"][9] = "establecer_html" # "set_attribute_all" -> duplicate of "set_attributes" md["set_attribute_all"] = ["*"] * num_langs md["set_attribute_all"][0] = "set_attribute_all" md["set_attribute_all"][1] = "设置所有属性" md["set_attribute_all"][2] = "attributen_instellen" md["set_attribute_all"][3] = "définir_attributs" md["set_attribute_all"][4] = "impostare_gli_attributi" md["set_attribute_all"][5] = "すべての属性を設定" md["set_attribute_all"][6] = "모든_특성_설정" md["set_attribute_all"][7] = "definir_atributos" md["set_attribute_all"][8] = "набор_атрибутов" md["set_attribute_all"][9] = "establecer_atributos" # "is_checked" -> duplicate of "is_selected" md["is_checked"] = ["*"] * num_langs md["is_checked"][0] = "is_checked" md["is_checked"][1] = "是否被选中" md["is_checked"][2] = "is_het_geselecteerd" md["is_checked"][3] = "est_il_sélectionné" md["is_checked"][4] = "è_selezionato" md["is_checked"][5] = "選択されていることを" md["is_checked"][6] = "선택되어_있는지" md["is_checked"][7] = "é_selecionado" md["is_checked"][8] = "выбран" md["is_checked"][9] = "está_seleccionado" # "wait_for_text_visible" -> duplicate of "wait_for_text" md["wait_for_text_visible"] = ["*"] * num_langs md["wait_for_text_visible"][0] = "wait_for_text_visible" md["wait_for_text_visible"][1] = "等待文本" md["wait_for_text_visible"][2] = "wachten_op_tekst" md["wait_for_text_visible"][3] = "attendre_le_texte" md["wait_for_text_visible"][4] = "attendere_il_testo" md["wait_for_text_visible"][5] = "テキストを待つ" md["wait_for_text_visible"][6] = "텍스트가_나타날_때까지_기다립니다" md["wait_for_text_visible"][7] = "aguardar_o_texto" md["wait_for_text_visible"][8] = "ждать_текста" md["wait_for_text_visible"][9] = "espera_el_texto" # "assert_text_visible" -> duplicate of "assert_text" md["assert_text_visible"] = ["*"] * num_langs md["assert_text_visible"][0] = "assert_text_visible" md["assert_text_visible"][1] = "断言文本" md["assert_text_visible"][2] = "controleren_tekst" md["assert_text_visible"][3] = "vérifier_texte" md["assert_text_visible"][4] = "verificare_testo" md["assert_text_visible"][5] = "テキストを確認する" md["assert_text_visible"][6] = "텍스트_확인" md["assert_text_visible"][7] = "verificar_texto" md["assert_text_visible"][8] = "подтвердить_текст" md["assert_text_visible"][9] = "verificar_texto" # "assert_link" -> duplicate of "assert_link_text" md["assert_link"] = ["*"] * num_langs md["assert_link"][0] = "assert_link" md["assert_link"][1] = "断言链接文本" md["assert_link"][2] = "controleren_linktekst" md["assert_link"][3] = "vérifier_texte_du_lien" md["assert_link"][4] = "verificare_testo_del_collegamento" md["assert_link"][5] = "リンクテキストを確認する" md["assert_link"][6] = "링크_텍스트_확인" md["assert_link"][7] = "verificar_texto_do_link" md["assert_link"][8] = "подтвердить_ссылку" md["assert_link"][9] = "verificar_texto_del_enlace" # "assert_no_broken_links" -> duplicate of "assert_no_404_errors" md["assert_no_broken_links"] = ["*"] * num_langs md["assert_no_broken_links"][0] = "assert_no_broken_links" md["assert_no_broken_links"][1] = "检查断开的链接" md["assert_no_broken_links"][2] = "controleren_op_gebroken_links" md["assert_no_broken_links"][3] = "vérifier_les_liens_rompus" md["assert_no_broken_links"][4] = "verificare_i_collegamenti" md["assert_no_broken_links"][5] = "リンク切れを確認する" md["assert_no_broken_links"][6] = "끊어진_링크_확인" md["assert_no_broken_links"][7] = "verificar_se_há_links_quebrados" md["assert_no_broken_links"][8] = "проверить_ошибки_404" md["assert_no_broken_links"][9] = "verificar_si_hay_enlaces_rotos" # "block_ads" -> duplicate of "ad_block" md["block_ads"] = ["*"] * num_langs md["block_ads"][0] = "block_ads" md["block_ads"][1] = "阻止广告" md["block_ads"][2] = "blokkeer_advertenties" md["block_ads"][3] = "annonces_de_bloc" md["block_ads"][4] = "bloccare_gli_annunci" md["block_ads"][5] = "ブロック広告" md["block_ads"][6] = "광고_차단" md["block_ads"][7] = "bloquear_anúncios" md["block_ads"][8] = "блокировать_рекламу" md["block_ads"][9] = "bloquear_anuncios" # "scroll_to_element" -> duplicate of "scroll_to" md["scroll_to_element"] = ["*"] * num_langs md["scroll_to_element"][0] = "scroll_to_element" md["scroll_to_element"][1] = "滚动到" md["scroll_to_element"][2] = "scrollen_naar" md["scroll_to_element"][3] = "déménager_à" md["scroll_to_element"][4] = "scorrere_fino_a" md["scroll_to_element"][5] = "スクロールして" md["scroll_to_element"][6] = "요소로_스크롤" md["scroll_to_element"][7] = "rolar_para" md["scroll_to_element"][8] = "прокрутить_к" md["scroll_to_element"][9] = "desplazarse_a" # "start_tour" -> duplicate of "play_tour" md["start_tour"] = ["*"] * num_langs md["start_tour"][0] = "start_tour" md["start_tour"][1] = "播放游览" md["start_tour"][2] = "speel_de_tour" md["start_tour"][3] = "jouer_la_visite" md["start_tour"][4] = "riprodurre_il_tour" md["start_tour"][5] = "ツアーを再生する" md["start_tour"][6] = "가이드_투어를하다" md["start_tour"][7] = "jogar_o_tour" md["start_tour"][8] = "играть_тур" md["start_tour"][9] = "reproducir_la_gira" # "delete_downloaded_file_if_present" -> double of "delete_downloaded_file" md["delete_downloaded_file_if_present"] = ["*"] * num_langs ddfip_en = "delete_downloaded_file_if_present" md["delete_downloaded_file_if_present"][0] = ddfip_en md["delete_downloaded_file_if_present"][1] = "删除下载的文件" ddfip_nl = "verwijder_gedownloade_bestand" md["delete_downloaded_file_if_present"][2] = ddfip_nl md["delete_downloaded_file_if_present"][3] = "supprimer_fichier_téléchargé" md["delete_downloaded_file_if_present"][4] = "eliminare_file_scaricato" md["delete_downloaded_file_if_present"][5] = "ダウンロードしたファイルを削除する" md["delete_downloaded_file_if_present"][6] = "다운로드한_파일_삭제" md["delete_downloaded_file_if_present"][7] = "exclua_arquivo_baixado" md["delete_downloaded_file_if_present"][8] = "удалить_загруженный_файл" md["delete_downloaded_file_if_present"][9] = "eliminar_archivo_descargado" # "wait_for_and_accept_alert" -> duplicate of "accept_alert" md["wait_for_and_accept_alert"] = ["*"] * num_langs md["wait_for_and_accept_alert"][0] = "wait_for_and_accept_alert" md["wait_for_and_accept_alert"][1] = "接受警报" md["wait_for_and_accept_alert"][2] = "waarschuwing_accepteren" md["wait_for_and_accept_alert"][3] = "accepter_alerte" md["wait_for_and_accept_alert"][4] = "accetta_avviso" md["wait_for_and_accept_alert"][5] = "アラートを受け入れる" md["wait_for_and_accept_alert"][6] = "경고를_수락" md["wait_for_and_accept_alert"][7] = "aceitar_alerta" md["wait_for_and_accept_alert"][8] = "принять_оповещение" md["wait_for_and_accept_alert"][9] = "aceptar_alerta" # "wait_for_and_dismiss_alert" -> duplicate of "dismiss_alert" md["wait_for_and_dismiss_alert"] = ["*"] * num_langs md["wait_for_and_dismiss_alert"][0] = "wait_for_and_dismiss_alert" md["wait_for_and_dismiss_alert"][1] = "解除警报" md["wait_for_and_dismiss_alert"][2] = "waarschuwing_wegsturen" md["wait_for_and_dismiss_alert"][3] = "rejeter_alerte" md["wait_for_and_dismiss_alert"][4] = "elimina_avviso" md["wait_for_and_dismiss_alert"][5] = "アラートを却下" md["wait_for_and_dismiss_alert"][6] = "경고를_거부" md["wait_for_and_dismiss_alert"][7] = "demitir_alerta" md["wait_for_and_dismiss_alert"][8] = "увольнять_оповещение" md["wait_for_and_dismiss_alert"][9] = "descartar_alerta" # "wait_for_and_switch_to_alert" -> duplicate of "switch_to_alert" md["wait_for_and_switch_to_alert"] = ["*"] * num_langs md["wait_for_and_switch_to_alert"][0] = "wait_for_and_switch_to_alert" md["wait_for_and_switch_to_alert"][1] = "切换到警报" md["wait_for_and_switch_to_alert"][2] = "overschakelen_naar_waarschuwing" md["wait_for_and_switch_to_alert"][3] = "passer_à_alerte" md["wait_for_and_switch_to_alert"][4] = "passa_al_avviso" md["wait_for_and_switch_to_alert"][5] = "アラートに切り替え" md["wait_for_and_switch_to_alert"][6] = "경고로_전환" md["wait_for_and_switch_to_alert"][7] = "mudar_para_alerta" md["wait_for_and_switch_to_alert"][8] = "переключиться_на_оповещение" md["wait_for_and_switch_to_alert"][9] = "cambiar_a_alerta" ################ # MasterQA Only! md["verify"] = ["*"] * num_langs md["verify"][0] = "verify" md["verify"][1] = "校验" md["verify"][2] = "controleren" md["verify"][3] = "vérifier" md["verify"][4] = "verificare" md["verify"][5] = "を確認する" md["verify"][6] = "확인" md["verify"][7] = "verificar" md["verify"][8] = "подтвердить" md["verify"][9] = "verificar" ================================================ FILE: seleniumbase/translate/portuguese.py ================================================ # Portuguese / Português - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class CasoDeTeste(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Portuguese" def abrir(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def abrir_url(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def clique(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def clique_duas_vezes(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def clique_de_contexto(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def clique_devagar(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def clique_se_está_visível(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def js_clique_se_está_presente(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def clique_texto_do_link(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def clique_com_deslocamento(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def atualizar_texto(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def digitar(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def adicionar_texto(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def obter_texto(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def verificar_texto(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def verificar_texto_exato(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def verificar_texto_do_link(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def verificar_texto_não_vazio(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def verificar_texto_não_visível(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def verificar_elemento(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def verificar_elemento_visível(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def verificar_elemento_não_visível(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def verificar_elemento_presente(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def verificar_elemento_ausente(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def verificar_atributo(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def verificar_url(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def verificar_url_contém(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def verificar_título(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def verificar_título_contém(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def obter_título(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def verificar_verdade(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def verificar_falso(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def verificar_igual(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def verificar_não_igual(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def atualizar_a_página(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def obter_url_atual(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def obter_a_página_html(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def voltar(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def avançar(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def o_texto_está_visível(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def o_texto_exato_está_visível(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def o_elemento_está_visível(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def o_elemento_está_habilitado(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def o_elemento_está_presente(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def aguardar_o_texto(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def aguardar_o_elemento(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def aguardar_o_elemento_visível(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def aguardar_o_elemento_não_visível(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def aguardar_o_elemento_presente(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def aguardar_o_elemento_ausente(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def aguardar_o_atributo(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def aguardar_a_página_carregar(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def dormir(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def aguardar(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def enviar(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def limpar(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def focar(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def js_clique(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def js_atualizar_texto(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def js_digitar(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def jquery_clique(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def jquery_atualizar_texto(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def jquery_digitar(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def verificar_html(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def salvar_captura_de_tela(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def salvar_captura_de_tela_para_logs(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def selecionar_arquivo(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def executar_script(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def executar_script_com_segurança(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def ativar_jquery(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def ativar_recorder(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def abrir_se_não_url(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def bloquear_anúncios(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def saltar(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def verificar_se_há_links_quebrados(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def verificar_se_há_erros_js(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def mudar_para_o_quadro(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def mudar_para_o_conteúdo_padrão(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def mudar_para_o_quadro_pai(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def abrir_nova_janela(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def mudar_para_janela(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def mudar_para_a_janela_padrão(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def mudar_para_a_janela_última(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def maximizar_janela(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def destaque(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def destaque_clique(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def rolar_para(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def rolar_para_o_topo(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def rolar_para_o_fundo(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def passe_o_mouse_e_clique(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def passe_o_mouse(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def é_selecionado(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def pressione_a_seta_para_cima(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def pressione_a_seta_para_baixo(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def pressione_a_seta_esquerda(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def pressione_a_seta_direita(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def clique_nos_elementos_visíveis(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def selecionar_opção_por_texto(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def selecionar_opção_por_índice(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def selecionar_opção_por_valor(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def criar_uma_apresentação(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def adicionar_um_slide(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def salvar_apresentação(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def iniciar_apresentação(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def criar_um_gráfico_de_pizza(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def criar_um_gráfico_de_barras(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def criar_um_gráfico_de_colunas(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def criar_um_gráfico_de_linhas(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def criar_um_gráfico_de_área(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def adicionar_séries_ao_gráfico(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def adicionar_ponto_de_dados(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def salvar_gráfico(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def exibir_gráfico(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def extrair_gráfico(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def criar_um_tour(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def criar_um_tour_shepherd(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def criar_um_tour_bootstrap(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def criar_um_tour_driverjs(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def criar_um_tour_hopscotch(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def criar_um_tour_introjs(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def adicionar_passo_para_o_tour(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def jogar_o_tour(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def exportar_o_tour(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def obter_texto_pdf(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def verificar_texto_pdf(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def baixar_arquivo(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def o_arquivo_baixado_está_presente(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def obter_caminho_do_arquivo_baixado(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def verificar_arquivo_baixado(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def exclua_arquivo_baixado(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def falhar(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def obter(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def visitar(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def visitar_url(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def obter_elemento(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def encontrar_elemento(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def remover_elemento(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def remover_elementos(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def encontrar_texto(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def definir_texto(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def obter_atributo(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def definir_atributo(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def definir_atributos(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def escreva(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def definir_tema_da_mensagem(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def exibir_mensagem(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def imprimir(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def adiada_verificar_elemento(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def adiada_verificar_texto(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def processar_verificações_adiada(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def aceitar_alerta(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def demitir_alerta(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def mudar_para_alerta(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def arrastar_e_soltar(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def definir_html(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def carregar_arquivo_html(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def abrir_arquivo_html(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def excluir_todos_os_cookies(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def obter_agente_do_usuário(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def obter_código_de_idioma(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_Português(MasterQA, CasoDeTeste): def verificar(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "Verificação manual" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "A página parece boa?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/russian.py ================================================ # Russian / Русский - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class ТестНаСелен(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Russian" def открыть(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def открыть_URL(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def нажмите(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def дважды_нажмите(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def контекстный_щелчок(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def нажмите_медленно(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def нажмите_если_виден(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def JS_нажмите_если_присутствует(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def нажмите_ссылку(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def нажмите_на_местоположение(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def обновить_текст(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def введите(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def добавить_текст(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def получить_текст(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def подтвердить_текст(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def подтвердить_текст_точно(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def подтвердить_ссылку(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def подтвердить_непустой_текст(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def подтвердить_текст_не_виден(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def подтвердить_элемент(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def подтвердить_элемент_виден(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def подтвердить_элемент_не_виден(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def подтвердить_элемент_присутствует(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def подтвердить_элемент_отсутствует(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def подтвердить_атрибут(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def подтвердить_URL(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def подтвердить_URL_содержит(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def подтвердить_название(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def подтвердить_название_содержит(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def получить_название(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def подтвердить_правду(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def подтвердить_ложные(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def подтвердить_одинаковый(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def подтвердить_не_одинаковый(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def обновить_страницу(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def получить_текущий_URL(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def получить_источник_страницы(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def назад(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def вперед(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def текст_виден(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def точный_текст_виден(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def элемент_виден(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def элемент_включен(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def элемент_присутствует(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def ждать_текста(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def ждать_элемента(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def ждать_элемента_виден(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def ждать_элемента_не_виден(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def ждать_элемента_присутствует(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def ждать_элемента_отсутствует(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def ждать_атрибут(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def ждать_загрузки_страницы(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def спать(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def ждать(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def отправить(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def очистить(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def сосредоточиться(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def JS_нажмите(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def JS_обновить_текст(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def JS_введите(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def JQUERY_нажмите(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def JQUERY_обновить_текст(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def JQUERY_введите(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def проверить_HTML(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def сохранить_скриншот(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def сохранить_скриншот_в_логи(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def выберите_файл(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def выполнение_скрипта(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def безопасное_выполнение_скрипта(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def активировать_JQUERY(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def активировать_RECORDER(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def открыть_если_не_URL(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def блокировать_рекламу(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def пропускать(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def проверить_ошибки_404(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def проверить_ошибки_JS(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def переключиться_на_кадр(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def переключиться_на_содержимое_по_умолчанию(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def переключиться_на_родительский_кадр(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def открыть_новое_окно(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def переключиться_на_окно(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def переключиться_на_окно_по_умолчанию(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def переключиться_на_последнее_окно(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def максимальное_окно(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def осветить(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def осветить_нажмите(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def прокрутить_к(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def пролистать_наверх(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def прокрутить_вниз(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def наведите_и_нажмите(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def наведение_мыши(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def выбран(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def нажмите_стрелку_вверх(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def нажмите_стрелку_вниз(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def нажмите_стрелку_влево(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def нажмите_стрелку_вправо(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def нажмите_видимые_элементы(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def выбрать_опцию_по_тексту(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def выбрать_опцию_по_индексу(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def выбрать_опцию_по_значению(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def создать_презентацию(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def добавить_слайд(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def сохранить_презентацию(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def начать_презентацию(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def создать_круговую_диаграмму(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def создать_бар_диаграмму(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def создать_столбчатую_диаграмму(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def создать_линейную_диаграмму(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def создать_диаграмму_области(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def добавить_серии_в_диаграмму(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def добавить_точку_данных(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def сохранить_диаграмму(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def отображать_диаграмму(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def извлекать_диаграмму(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def создать_тур(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def создать_SHEPHERD_тур(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def создать_BOOTSTRAP_тур(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def создать_DRIVERJS_тур(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def создать_HOPSCOTCH_тур(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def создать_INTROJS_тур(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def добавить_шаг_в_тур(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def играть_тур(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def экспортировать_тур(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def получить_текст_PDF(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def подтвердить_текст_PDF(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def скачать_файл(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def загруженный_файл_присутствует(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def получить_путь_к_загруженному_файлу(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def подтвердить_загруженный_файл(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def удалить_загруженный_файл(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def провалить(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def получить(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def посетить(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def посетить_URL(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def получить_элемент(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def найти_элемент(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def удалить_элемент(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def удалить_элементы(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def найти_текст(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def набор_текст(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def получить_атрибут(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def набор_атрибута(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def набор_атрибутов(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def написать(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def набор_тему_сообщения(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def показать_сообщение(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def печатать(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def отложенный_подтвердить_элемент(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def отложенный_подтвердить_текст(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def обработки_отложенных_подтверждений(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def принять_оповещение(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def увольнять_оповещение(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def переключиться_на_оповещение(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def перетащить_и_падение(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def набор_HTML(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def загрузить_HTML_файл(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def открыть_HTML_файл(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def удалить_все_куки(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def получить_агента_пользователя(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def получить_код_языка(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_Русский(MasterQA, ТестНаСелен): def подтвердить(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "Ручная проверка" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "Страница хорошо выглядит?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/spanish.py ================================================ # Spanish / Español - Translations from seleniumbase import BaseCase from seleniumbase import MasterQA class CasoDePrueba(BaseCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._language = "Spanish" def abrir(self, *args, **kwargs): # open(url) return self.open(*args, **kwargs) def abrir_url(self, *args, **kwargs): # open_url(url) return self.open_url(*args, **kwargs) def haga_clic(self, *args, **kwargs): # click(selector) return self.click(*args, **kwargs) def doble_clic(self, *args, **kwargs): # double_click(selector) return self.double_click(*args, **kwargs) def clic_de_contexto(self, *args, **kwargs): # context_click(selector) return self.context_click(*args, **kwargs) def clic_lentamente(self, *args, **kwargs): # slow_click(selector) return self.slow_click(*args, **kwargs) def clic_si_está_muestra(self, *args, **kwargs): # click_if_visible(selector, by=By.CSS_SELECTOR) return self.click_if_visible(*args, **kwargs) def js_clic_si_está_presente(self, *args, **kwargs): # js_click_if_present(selector, by=By.CSS_SELECTOR) return self.js_click_if_present(*args, **kwargs) def clic_texto_del_enlace(self, *args, **kwargs): # click_link_text(link_text) return self.click_link_text(*args, **kwargs) def clic_con_desplazamiento(self, *args, **kwargs): # click_with_offset(selector, x, y, by=By.CSS_SELECTOR, # mark=None, timeout=None, center=None) return self.click_with_offset(*args, **kwargs) def actualizar_texto(self, *args, **kwargs): # update_text(selector, text) return self.update_text(*args, **kwargs) def escriba(self, *args, **kwargs): # type(selector, text) # Same as update_text() return self.type(*args, **kwargs) def agregar_texto(self, *args, **kwargs): # add_text(selector, text) return self.add_text(*args, **kwargs) def obtener_texto(self, *args, **kwargs): # get_text(selector, text) return self.get_text(*args, **kwargs) def verificar_texto(self, *args, **kwargs): # assert_text(text, selector) return self.assert_text(*args, **kwargs) def verificar_texto_exacto(self, *args, **kwargs): # assert_exact_text(text, selector) return self.assert_exact_text(*args, **kwargs) def verificar_texto_del_enlace(self, *args, **kwargs): # assert_link_text(link_text) return self.assert_link_text(*args, **kwargs) def verificar_texto_no_vacío(self, *args, **kwargs): # assert_non_empty_text(selector) return self.assert_non_empty_text(*args, **kwargs) def verificar_texto_no_se_muestra(self, *args, **kwargs): # assert_text_not_visible(text, selector) return self.assert_text_not_visible(*args, **kwargs) def verificar_elemento(self, *args, **kwargs): # assert_element(selector) return self.assert_element(*args, **kwargs) def verificar_elemento_se_muestra(self, *args, **kwargs): # assert_element_visible(selector) # Same as self.assert_element() return self.assert_element_visible(*args, **kwargs) def verificar_elemento_no_se_muestra(self, *args, **kwargs): # assert_element_not_visible(selector) return self.assert_element_not_visible(*args, **kwargs) def verificar_elemento_presente(self, *args, **kwargs): # assert_element_present(selector) return self.assert_element_present(*args, **kwargs) def verificar_elemento_ausente(self, *args, **kwargs): # assert_element_absent(selector) return self.assert_element_absent(*args, **kwargs) def verificar_atributo(self, *args, **kwargs): # assert_attribute(selector, attribute, value) return self.assert_attribute(*args, **kwargs) def verificar_url(self, *args, **kwargs): # assert_url(url) return self.assert_url(*args, **kwargs) def verificar_url_contiene(self, *args, **kwargs): # assert_url_contains(substring) return self.assert_url_contains(*args, **kwargs) def verificar_título(self, *args, **kwargs): # assert_title(title) return self.assert_title(*args, **kwargs) def verificar_título_contiene(self, *args, **kwargs): # assert_title_contains(substring) return self.assert_title_contains(*args, **kwargs) def obtener_título(self, *args, **kwargs): # get_title() return self.get_title(*args, **kwargs) def verificar_verdad(self, *args, **kwargs): # assert_true(expr) return self.assert_true(*args, **kwargs) def verificar_falso(self, *args, **kwargs): # assert_false(expr) return self.assert_false(*args, **kwargs) def verificar_igual(self, *args, **kwargs): # assert_equal(first, second) return self.assert_equal(*args, **kwargs) def verificar_diferente(self, *args, **kwargs): # assert_not_equal(first, second) return self.assert_not_equal(*args, **kwargs) def actualizar_la_página(self, *args, **kwargs): # refresh_page() return self.refresh_page(*args, **kwargs) def obtener_url_actual(self, *args, **kwargs): # get_current_url() return self.get_current_url(*args, **kwargs) def obtener_html_de_la_página(self, *args, **kwargs): # get_page_source() return self.get_page_source(*args, **kwargs) def volver(self, *args, **kwargs): # go_back() return self.go_back(*args, **kwargs) def adelante(self, *args, **kwargs): # go_forward() return self.go_forward(*args, **kwargs) def se_muestra_el_texto(self, *args, **kwargs): # is_text_visible(text, selector="html") return self.is_text_visible(*args, **kwargs) def se_muestra_el_texto_exacto(self, *args, **kwargs): # is_exact_text_visible(text, selector="html") return self.is_exact_text_visible(*args, **kwargs) def se_muestra_el_elemento(self, *args, **kwargs): # is_element_visible(selector) return self.is_element_visible(*args, **kwargs) def está_habilitado_el_elemento(self, *args, **kwargs): # is_element_enabled(selector) return self.is_element_enabled(*args, **kwargs) def está_presente_el_elemento(self, *args, **kwargs): # is_element_present(selector) return self.is_element_present(*args, **kwargs) def espera_el_texto(self, *args, **kwargs): # wait_for_text(text, selector) return self.wait_for_text(*args, **kwargs) def espera_el_elemento(self, *args, **kwargs): # wait_for_element(selector) return self.wait_for_element(*args, **kwargs) def espera_el_elemento_se_muestra(self, *args, **kwargs): # wait_for_element_visible(selector) # Same as wait_for_element() return self.wait_for_element_visible(*args, **kwargs) def espera_el_elemento_no_se_muestra(self, *args, **kwargs): # wait_for_element_not_visible(selector) return self.wait_for_element_not_visible(*args, **kwargs) def espera_el_elemento_presente(self, *args, **kwargs): # wait_for_element_present(selector) return self.wait_for_element_present(*args, **kwargs) def espera_el_elemento_ausente(self, *args, **kwargs): # wait_for_element_absent(selector) return self.wait_for_element_absent(*args, **kwargs) def espera_el_atributo(self, *args, **kwargs): # wait_for_attribute(selector, attribute, value) return self.wait_for_attribute(*args, **kwargs) def esperar_a_que_cargue_la_página(self, *args, **kwargs): # wait_for_ready_state_complete() return self.wait_for_ready_state_complete(*args, **kwargs) def dormir(self, *args, **kwargs): # sleep(seconds) return self.sleep(*args, **kwargs) def espera(self, *args, **kwargs): # wait(seconds) # Same as sleep(seconds) return self.wait(*args, **kwargs) def enviar(self, *args, **kwargs): # submit(selector) return self.submit(*args, **kwargs) def despejar(self, *args, **kwargs): # clear(selector) return self.clear(*args, **kwargs) def centrarse(self, *args, **kwargs): # focus(selector) return self.focus(*args, **kwargs) def js_haga_clic(self, *args, **kwargs): # js_click(selector) return self.js_click(*args, **kwargs) def js_actualizar_texto(self, *args, **kwargs): # js_update_text(selector, text) return self.js_update_text(*args, **kwargs) def js_escriba(self, *args, **kwargs): # js_type(selector, text) return self.js_type(*args, **kwargs) def jquery_haga_clic(self, *args, **kwargs): # jquery_click(selector) return self.jquery_click(*args, **kwargs) def jquery_actualizar_texto(self, *args, **kwargs): # jquery_update_text(selector, text) return self.jquery_update_text(*args, **kwargs) def jquery_escriba(self, *args, **kwargs): # jquery_type(selector, text) return self.jquery_type(*args, **kwargs) def comprobar_html(self, *args, **kwargs): # inspect_html() return self.inspect_html(*args, **kwargs) def guardar_captura_de_pantalla(self, *args, **kwargs): # save_screenshot(name) return self.save_screenshot(*args, **kwargs) def guardar_captura_de_pantalla_para_logs(self, *args, **kwargs): # save_screenshot_to_logs(name) return self.save_screenshot_to_logs(*args, **kwargs) def seleccionar_archivo(self, *args, **kwargs): # choose_file(selector, file_path) return self.choose_file(*args, **kwargs) def ejecutar_script(self, *args, **kwargs): # execute_script(script) return self.execute_script(*args, **kwargs) def ejecutar_script_de_forma_segura(self, *args, **kwargs): # safe_execute_script(script) return self.safe_execute_script(*args, **kwargs) def activar_jquery(self, *args, **kwargs): # activate_jquery() return self.activate_jquery(*args, **kwargs) def activar_recorder(self, *args, **kwargs): # activate_recorder() return self.activate_recorder(*args, **kwargs) def abrir_que_no_url(self, *args, **kwargs): # open_if_not_url(url) return self.open_if_not_url(*args, **kwargs) def bloquear_anuncios(self, *args, **kwargs): # ad_block() return self.ad_block(*args, **kwargs) def saltar(self, *args, **kwargs): # skip(reason="") return self.skip(*args, **kwargs) def verificar_si_hay_enlaces_rotos(self, *args, **kwargs): # assert_no_404_errors() return self.assert_no_404_errors(*args, **kwargs) def verificar_si_hay_errores_js(self, *args, **kwargs): # assert_no_js_errors() return self.assert_no_js_errors(*args, **kwargs) def cambiar_al_marco(self, *args, **kwargs): # switch_to_frame(frame) return self.switch_to_frame(*args, **kwargs) def cambiar_al_contenido_predeterminado(self, *args, **kwargs): # switch_to_default_content() return self.switch_to_default_content(*args, **kwargs) def cambiar_al_marco_principal(self, *args, **kwargs): # switch_to_parent_frame() return self.switch_to_parent_frame(*args, **kwargs) def abrir_una_nueva_ventana(self, *args, **kwargs): # open_new_window() return self.open_new_window(*args, **kwargs) def cambiar_a_ventana(self, *args, **kwargs): # switch_to_window(window) return self.switch_to_window(*args, **kwargs) def cambiar_a_ventana_predeterminada(self, *args, **kwargs): # switch_to_default_window() return self.switch_to_default_window(*args, **kwargs) def cambiar_a_ventana_última(self, *args, **kwargs): # switch_to_newest_window() return self.switch_to_newest_window(*args, **kwargs) def maximizar_ventana(self, *args, **kwargs): # maximize_window() return self.maximize_window(*args, **kwargs) def resalte(self, *args, **kwargs): # highlight(selector) return self.highlight(*args, **kwargs) def resalte_clic(self, *args, **kwargs): # highlight_click(selector) return self.highlight_click(*args, **kwargs) def desplazarse_a(self, *args, **kwargs): # scroll_to(selector) return self.scroll_to(*args, **kwargs) def desplazarse_hasta_la_parte_superior(self, *args, **kwargs): # scroll_to_top() return self.scroll_to_top(*args, **kwargs) def desplazarse_hasta_la_parte_inferior(self, *args, **kwargs): # scroll_to_bottom() return self.scroll_to_bottom(*args, **kwargs) def pasar_el_ratón_y_hacer_clic(self, *args, **kwargs): # hover_and_click(hover_selector, click_selector) return self.hover_and_click(*args, **kwargs) def pasar_el_ratón(self, *args, **kwargs): # hover(selector) return self.hover(*args, **kwargs) def está_seleccionado(self, *args, **kwargs): # is_selected(selector) return self.is_selected(*args, **kwargs) def presione_la_flecha_hacia_arriba(self, *args, **kwargs): # press_up_arrow(selector="html", times=1) return self.press_up_arrow(*args, **kwargs) def presione_la_flecha_hacia_abajo(self, *args, **kwargs): # press_down_arrow(selector="html", times=1) return self.press_down_arrow(*args, **kwargs) def presione_la_flecha_izquierda(self, *args, **kwargs): # press_left_arrow(selector="html", times=1) return self.press_left_arrow(*args, **kwargs) def presione_la_flecha_derecha(self, *args, **kwargs): # press_right_arrow(selector="html", times=1) return self.press_right_arrow(*args, **kwargs) def clic_en_elementos_visibles(self, *args, **kwargs): # click_visible_elements(selector) return self.click_visible_elements(*args, **kwargs) def seleccionar_opción_por_texto(self, *args, **kwargs): # select_option_by_text(dropdown_selector, option) return self.select_option_by_text(*args, **kwargs) def seleccionar_opción_por_índice(self, *args, **kwargs): # select_option_by_index(dropdown_selector, option) return self.select_option_by_index(*args, **kwargs) def seleccionar_opción_por_valor(self, *args, **kwargs): # select_option_by_value(dropdown_selector, option) return self.select_option_by_value(*args, **kwargs) def crear_una_presentación(self, *args, **kwargs): # create_presentation(name=None, theme="default", transition="default") return self.create_presentation(*args, **kwargs) def agregar_una_diapositiva(self, *args, **kwargs): # add_slide(content=None, image=None, code=None, iframe=None, # content2=None, notes=None, transition=None, name=None) return self.add_slide(*args, **kwargs) def guardar_presentación(self, *args, **kwargs): # save_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.save_presentation(*args, **kwargs) def iniciar_presentación(self, *args, **kwargs): # begin_presentation(name=None, filename=None, # show_notes=False, interval=0) return self.begin_presentation(*args, **kwargs) def crear_un_gráfico_circular(self, *args, **kwargs): # create_pie_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_pie_chart(*args, **kwargs) def crear_un_gráfico_de_barras(self, *args, **kwargs): # create_bar_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_bar_chart(*args, **kwargs) def crear_un_gráfico_de_columnas(self, *args, **kwargs): # create_column_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, libs=True) return self.create_column_chart(*args, **kwargs) def crear_un_gráfico_de_líneas(self, *args, **kwargs): # create_line_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_line_chart(*args, **kwargs) def crear_un_gráfico_de_área(self, *args, **kwargs): # create_area_chart(chart_name=None, title=None, subtitle=None, # data_name=None, unit=None, zero=False, libs=True) return self.create_area_chart(*args, **kwargs) def agregar_series_al_gráfico(self, *args, **kwargs): # add_series_to_chart(data_name=None, chart_name=None) return self.add_series_to_chart(*args, **kwargs) def agregar_punto_de_datos(self, *args, **kwargs): # add_data_point(label, value, color=None, chart_name=None) return self.add_data_point(*args, **kwargs) def guardar_gráfico(self, *args, **kwargs): # save_chart(chart_name=None, filename=None) return self.save_chart(*args, **kwargs) def muestra_gráfico(self, *args, **kwargs): # display_chart(chart_name=None, filename=None, interval=0) return self.display_chart(*args, **kwargs) def extracto_gráfico(self, *args, **kwargs): # extract_chart(chart_name=None) return self.extract_chart(*args, **kwargs) def crear_una_gira(self, *args, **kwargs): # create_tour(name=None, theme=None) return self.create_tour(*args, **kwargs) def crear_una_gira_shepherd(self, *args, **kwargs): # create_shepherd_tour(name=None, theme=None) return self.create_shepherd_tour(*args, **kwargs) def crear_una_gira_bootstrap(self, *args, **kwargs): # create_bootstrap_tour(name=None, theme=None) return self.create_bootstrap_tour(*args, **kwargs) def crear_una_gira_driverjs(self, *args, **kwargs): # create_driverjs_tour(name=None, theme=None) return self.create_driverjs_tour(*args, **kwargs) def crear_una_gira_hopscotch(self, *args, **kwargs): # create_hopscotch_tour(name=None, theme=None) return self.create_hopscotch_tour(*args, **kwargs) def crear_una_gira_introjs(self, *args, **kwargs): # create_introjs_tour(name=None, theme=None) return self.create_introjs_tour(*args, **kwargs) def agregar_paso_a_la_gira(self, *args, **kwargs): # add_tour_step(message, selector=None, name=None, # title=None, theme=None, alignment=None) return self.add_tour_step(*args, **kwargs) def reproducir_la_gira(self, *args, **kwargs): # play_tour(name=None) return self.play_tour(*args, **kwargs) def exportar_la_gira(self, *args, **kwargs): # export_tour(name=None, filename="my_tour.js", url=None) return self.export_tour(*args, **kwargs) def obtener_texto_pdf(self, *args, **kwargs): # get_pdf_text(pdf, page=None, maxpages=None, password=None, # codec='utf-8', wrap=False, nav=False, override=False) return self.get_pdf_text(*args, **kwargs) def verificar_texto_pdf(self, *args, **kwargs): # assert_pdf_text(pdf, text, page=None, maxpages=None, password=None, # codec='utf-8', wrap=True, nav=False, override=False) return self.assert_pdf_text(*args, **kwargs) def descargar_archivo(self, *args, **kwargs): # download_file(file) return self.download_file(*args, **kwargs) def está_presente_el_archivo_descargado(self, *args, **kwargs): # is_downloaded_file_present(file) return self.is_downloaded_file_present(*args, **kwargs) def obtener_ruta_del_archivo_descargado(self, *args, **kwargs): # get_path_of_downloaded_file(file) return self.get_path_of_downloaded_file(*args, **kwargs) def verificar_archivo_descargado(self, *args, **kwargs): # assert_downloaded_file(file) return self.assert_downloaded_file(*args, **kwargs) def eliminar_archivo_descargado(self, *args, **kwargs): # delete_downloaded_file(file) return self.delete_downloaded_file(*args, **kwargs) def fallar(self, *args, **kwargs): # fail(msg=None) # Inherited from "unittest" return self.fail(*args, **kwargs) def obtener(self, *args, **kwargs): # get(url) # Same as open(url) return self.get(*args, **kwargs) def visita(self, *args, **kwargs): # visit(url) # Same as open(url) return self.visit(*args, **kwargs) def visita_url(self, *args, **kwargs): # visit_url(url) # Same as open(url) return self.visit_url(*args, **kwargs) def obtener_elemento(self, *args, **kwargs): # get_element(selector) # Element can be hidden return self.get_element(*args, **kwargs) def encontrar_elemento(self, *args, **kwargs): # find_element(selector) # Element must be visible return self.find_element(*args, **kwargs) def eliminar_elemento(self, *args, **kwargs): # remove_element(selector) return self.remove_element(*args, **kwargs) def eliminar_elementos(self, *args, **kwargs): # remove_elements(selector) return self.remove_elements(*args, **kwargs) def encontrar_texto(self, *args, **kwargs): # find_text(text, selector="html") # Same as wait_for_text return self.find_text(*args, **kwargs) def establecer_texto(self, *args, **kwargs): # set_text(selector, text) return self.set_text(*args, **kwargs) def obtener_atributo(self, *args, **kwargs): # get_attribute(selector, attribute) return self.get_attribute(*args, **kwargs) def establecer_atributo(self, *args, **kwargs): # set_attribute(selector, attribute, value) return self.set_attribute(*args, **kwargs) def establecer_atributos(self, *args, **kwargs): # set_attributes(selector, attribute, value) return self.set_attributes(*args, **kwargs) def escribir(self, *args, **kwargs): # write(selector, text) # Same as update_text() return self.write(*args, **kwargs) def establecer_tema_del_mensaje(self, *args, **kwargs): # set_messenger_theme(theme="default", location="default") return self.set_messenger_theme(*args, **kwargs) def mostrar_mensaje(self, *args, **kwargs): # post_message(message, duration=None, pause=True, style="info") return self.post_message(*args, **kwargs) def imprimir(self, *args, **kwargs): # _print(msg) # Same as Python print() return self._print(*args, **kwargs) def diferido_verificar_elemento(self, *args, **kwargs): # deferred_assert_element(selector) return self.deferred_assert_element(*args, **kwargs) def diferido_verificar_texto(self, *args, **kwargs): # deferred_assert_text(text, selector="html") return self.deferred_assert_text(*args, **kwargs) def procesar_verificaciones_diferidas(self, *args, **kwargs): # process_deferred_asserts(print_only=False) return self.process_deferred_asserts(*args, **kwargs) def aceptar_alerta(self, *args, **kwargs): # accept_alert(timeout=None) return self.accept_alert(*args, **kwargs) def descartar_alerta(self, *args, **kwargs): # dismiss_alert(timeout=None) return self.dismiss_alert(*args, **kwargs) def cambiar_a_alerta(self, *args, **kwargs): # switch_to_alert(timeout=None) return self.switch_to_alert(*args, **kwargs) def arrastrar_y_soltar(self, *args, **kwargs): # drag_and_drop(drag_selector, drop_selector) return self.drag_and_drop(*args, **kwargs) def establecer_html(self, *args, **kwargs): # set_content(html_string, new_page=False) return self.set_content(*args, **kwargs) def cargar_archivo_html(self, *args, **kwargs): # load_html_file(html_file, new_page=True) return self.load_html_file(*args, **kwargs) def abrir_archivo_html(self, *args, **kwargs): # open_html_file(html_file) return self.open_html_file(*args, **kwargs) def eliminar_todas_las_cookies(self, *args, **kwargs): # delete_all_cookies() return self.delete_all_cookies(*args, **kwargs) def obtener_agente_de_usuario(self, *args, **kwargs): # get_user_agent() return self.get_user_agent(*args, **kwargs) def obtener_código_de_idioma(self, *args, **kwargs): # get_locale_code() return self.get_locale_code(*args, **kwargs) class MasterQA_Español(MasterQA, CasoDePrueba): def verificar(self, *args, **kwargs): # "Manual Check" self.DEFAULT_VALIDATION_TITLE = "Comprobación manual" # "Does the page look good?" self.DEFAULT_VALIDATION_MESSAGE = "¿Se ve bien la página?" # verify(QUESTION) return self.verify(*args, **kwargs) ================================================ FILE: seleniumbase/translate/translator.py ================================================ """ Translates a SeleniumBase Python file into a different language Usage: seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION] OR: sbase translate [SB_FILE.py] [LANGUAGE] [ACTION] Languages: --en / --English | --zh / --Chinese --nl / --Dutch | --fr / --French --it / --Italian | --ja / --Japanese --ko / --Korean | --pt / --Portuguese --ru / --Russian | --es / --Spanish Actions: -p / --print (Print translation output to the screen) -o / --overwrite (Overwrite the file being translated) -c / --copy (Copy the translation to a new .py file) Options: -n (include line Numbers when using the Print action) Output: Translates a SeleniumBase Python file into the language specified. Method calls and "import" lines get swapped. Both a language and an action must be specified. The "-p" action can be paired with one other action. When running with "-c" (or "--copy"), the new file name will be the original name appended with an underscore plus the 2-letter language code of the new language. (Example: Translating "test_1.py" into Japanese with "-c" will create a new file called "test_1_ja.py".) """ import colorama import os import re import sys from seleniumbase.translate import master_dict MD_F = master_dict.MD_F MD_L_Codes = master_dict.MD_L_Codes MD = master_dict.MD def invalid_run_command(msg=None): exp = " ** translate **\n\n" exp += " Usage:\n" exp += " seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n" exp += " OR: sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n" exp += " Languages:\n" exp += " --en / --English | --zh / --Chinese\n" exp += " --nl / --Dutch | --fr / --French\n" exp += " --it / --Italian | --ja / --Japanese\n" exp += " --ko / --Korean | --pt / --Portuguese\n" exp += " --ru / --Russian | --es / --Spanish\n" exp += " Actions:\n" exp += " -p / --print (Print translation output to the screen)\n" exp += " -o / --overwrite (Overwrite the file being translated)\n" exp += " -c / --copy (Copy the translation to a new .py file)\n" exp += " Options:\n" exp += " -n (include line Numbers when using the Print action)\n" exp += " Output:\n" exp += " Translates a SeleniumBase Python file into the language\n" exp += ' specified. Method calls and "import" lines get swapped.\n' exp += " Both a language and an action must be specified.\n" exp += ' The "-p" action can be paired with one other action.\n' exp += ' When running with "-c" (or "--copy"), the new file name\n' exp += " will be the original name appended with an underscore\n" exp += " plus the 2-letter language code of the new language.\n" exp += ' (Example: Translating "test_1.py" into Japanese with\n' exp += ' "-c" will create a new file called "test_1_ja.py".)\n' if not msg: raise Exception("INVALID RUN COMMAND!\n\n%s" % exp) else: raise Exception("INVALID RUN COMMAND!\n%s\n\n%s" % (msg, exp)) def sc_ranges(): # Get the ranges of special double-width characters. special_char_ranges = [ {"from": ord("\u4e00"), "to": ord("\u9FFF")}, {"from": ord("\u3040"), "to": ord("\u30ff")}, {"from": ord("\uac00"), "to": ord("\ud7a3")}, {"from": ord("\uff01"), "to": ord("\uff60")}, ] return special_char_ranges def is_cjk(char): # Returns True if the special character is Chinese, Japanese, or Korean. sc = any( [range["from"] <= ord(char) <= range["to"] for range in sc_ranges()] ) return sc def get_width(line): # Return the true width of the line. Not the same as line length. # Chinese/Japanese/Korean characters take up double width visually. line_length = len(line) for char in line: if is_cjk(char): line_length += 1 return line_length def process_test_file(code_lines, new_lang): detected_lang = None changed = False found_bc = False # Found BaseCase or a translation seleniumbase_lines = [] lang_codes = MD_L_Codes.lang nl_code = lang_codes[new_lang] # new_lang language code dl_code = None # detected_lang language code md = MD.md # Master Dictionary for line in code_lines: line = line.rstrip() # Find imports that determine the language if line.lstrip().startswith("from seleniumbase") and "import" in line: added_line = False for lang in MD_F.get_languages_list(): data = re.match( r"^\s*" + MD_F.get_import_line(lang) + r"([\S\s]*)$", line ) if data: comments = "%s" % data.group(1) new_line = None detected_lang = lang dl_code = lang_codes[detected_lang] if detected_lang != new_lang: changed = True new_line = MD_F.get_import_line(new_lang) + comments else: found_bc = True new_line = line if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] seleniumbase_lines.append(new_line) added_line = True break data = re.match( r"^\s*" + MD_F.get_mqa_im_line(lang) + r"([\S\s]*)$", line ) if data: comments = "%s" % data.group(1) new_line = None detected_lang = lang dl_code = lang_codes[detected_lang] if detected_lang != new_lang: changed = True new_line = MD_F.get_mqa_im_line(new_lang) + comments else: found_bc = True new_line = line if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] seleniumbase_lines.append(new_line) added_line = True break if not added_line: # Probably a language missing from the translator. # Add the import line as it is and move on. seleniumbase_lines.append(line) continue # Find class definitions that determine the language if line.lstrip().startswith("class ") and ":" in line: added_line = False data = re.match( r"""^(\s*)class\s+([\S]+)\(([\S]+)\):([\S\s]*)$""", line ) if data: whitespace = data.group(1) name = "%s" % data.group(2) parent_class = "%s" % data.group(3) comments = "%s" % data.group(4) if parent_class in MD_F.get_parent_classes_list(): detected_lang = MD_F.get_parent_class_lang(parent_class) dl_code = lang_codes[detected_lang] if detected_lang != new_lang: changed = True new_parent = MD_F.get_lang_parent_class(new_lang) new_line = "%sclass %s(%s):%s" "" % ( whitespace, name, new_parent, comments, ) else: found_bc = True new_line = line if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] seleniumbase_lines.append(new_line) added_line = True continue elif parent_class in MD_F.get_masterqa_parent_classes_list(): detected_lang = MD_F.get_mqa_par_class_lang(parent_class) dl_code = lang_codes[detected_lang] if detected_lang != new_lang: changed = True new_parent = MD_F.get_mqa_lang_par_class(new_lang) new_line = "%sclass %s(%s):%s" "" % ( whitespace, name, new_parent, comments, ) else: found_bc = True new_line = line if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] seleniumbase_lines.append(new_line) added_line = True continue if not added_line: # Probably a language missing from the translator. # Add the class definition line as it is and move on. seleniumbase_lines.append(line) continue if ( ".main(__name__, __file__)" in line and detected_lang and new_lang and (detected_lang != new_lang) ): old_basecase = MD_F.get_lang_parent_class(detected_lang) new_basecase = MD_F.get_lang_parent_class(new_lang) if old_basecase in line: new_line = line.replace(old_basecase, new_basecase) seleniumbase_lines.append(new_line) continue if ( "self." in line and "(" in line and detected_lang and (detected_lang != new_lang) ): found_swap = False replace_count = line.count("self.") # Total possible replacements for key in md.keys(): original = "self." + md[key][dl_code] + "(" if original in line: replacement = "self." + md[key][nl_code] + "(" new_line = line.replace(original, replacement) found_swap = True replace_count -= 1 if replace_count == 0: break # Done making replacements else: # There might be another method to replace in the line. # Example: self.assert_true("Name" in self.get_title()) line = new_line continue if found_swap: if new_line.endswith(" # noqa"): # Remove flake8 skip new_line = new_line[0 : -len(" # noqa")] seleniumbase_lines.append(new_line) continue seleniumbase_lines.append(line) return seleniumbase_lines, changed, detected_lang, found_bc def main(): c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX c2 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX c3 = colorama.Fore.RED + colorama.Back.LIGHTGREEN_EX c4 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX c6 = colorama.Fore.RED + colorama.Back.LIGHTCYAN_EX c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA cr = colorama.Style.RESET_ALL new_lang = None overwrite = False copy = False print_only = False help_me = False invalid_cmd = None line_numbers = False word_wrap = True # Always use word wrap now expected_arg = "A SeleniumBase Python file" command_args = sys.argv[2:] seleniumbase_file = command_args[0] if not seleniumbase_file.endswith(".py"): seleniumbase_file = ( c7 + ">>" + c5 + " " + seleniumbase_file + " " + c7 + "<<" + cr ) bad_file_error = ( "\n`%s` is not a Python file!\n\n" "Expecting: [%s]" % (seleniumbase_file, expected_arg) ) bad_file_error = bad_file_error.replace( "is not a Python file!", c3 + "is not a Python file!" + cr ) bad_file_error = bad_file_error.replace( expected_arg, c4 + expected_arg + cr ) bad_file_error = bad_file_error.replace( "Expecting:", c3 + "Expecting:" + cr ) print(bad_file_error) help_me = True if len(command_args) >= 2 and not help_me: options = command_args[1:] for option in options: option = option.lower() if option == "help" or option == "--help": help_me = True elif option == "-o" or option == "--overwrite": overwrite = True elif option == "-c" or option == "--copy": copy = True elif option == "-p" or option == "--print": print_only = True elif option == "-n": line_numbers = True elif option == "--en" or option == "--english": new_lang = "English" elif option == "--zh" or option == "--chinese": new_lang = "Chinese" elif option == "--nl" or option == "--dutch": new_lang = "Dutch" elif option == "--fr" or option == "--french": new_lang = "French" elif option == "--it" or option == "--italian": new_lang = "Italian" elif option == "--ja" or option == "--japanese": new_lang = "Japanese" elif option == "--ko" or option == "--korean": new_lang = "Korean" elif option == "--pt" or option == "--portuguese": new_lang = "Portuguese" elif option == "--ru" or option == "--russian": new_lang = "Russian" elif option == "--es" or option == "--spanish": new_lang = "Spanish" else: invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option invalid_cmd = invalid_cmd.replace(">> ", ">>" + c5 + " ") invalid_cmd = invalid_cmd.replace(" <<", " " + cr + "<<") invalid_cmd = invalid_cmd.replace(">>", c7 + ">>" + cr) invalid_cmd = invalid_cmd.replace("<<", c7 + "<<" + cr) help_me = True break else: help_me = True specify_lang = ( "\n>* You must specify a language to translate to! *<\n" "\n" "> ******** Language Options: ******** <\n" " --en / --English | --zh / --Chinese\n" " --nl / --Dutch | --fr / --French\n" " --it / --Italian | --ja / --Japanese\n" " --ko / --Korean | --pt / --Portuguese\n" " --ru / --Russian | --es / --Spanish\n" ) specify_action = ( "\n>* You must specify an action type! *<\n" "\n" "> *** Action Options: *** <\n" " -p / --print\n" " -o / --overwrite\n" " -c / --copy\n" ) example_run = ( "\n> *** Examples: *** <\n" "Translate test_1.py into Chinese and only print the output:\n" " >$ sbase translate test_1.py --zh -p\n" "Translate test_2.py into Portuguese and overwrite the file:\n" " >$ sbase translate test_2.py --pt -o\n" "Translate test_3.py into Dutch and make a copy of the file:\n" " >$ sbase translate test_3.py --nl -c\n" ) usage = ( "\n> *** Usage: *** <\n" " >$ sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]\n" ) specify_lang = specify_lang.replace(">*", c5 + ">*") specify_lang = specify_lang.replace("*<", "*<" + cr) specify_lang = specify_lang.replace( "Language Options:", c4 + "Language Options:" + cr ) specify_lang = specify_lang.replace( "> ******** ", c3 + "> ******** " + cr ) specify_lang = specify_lang.replace( " ******** <", c3 + " ******** <" + cr ) specify_lang = specify_lang.replace("--en", c2 + "--en" + cr) specify_lang = specify_lang.replace("--zh", c2 + "--zh" + cr) specify_lang = specify_lang.replace("--nl", c2 + "--nl" + cr) specify_lang = specify_lang.replace("--fr", c2 + "--fr" + cr) specify_lang = specify_lang.replace("--it", c2 + "--it" + cr) specify_lang = specify_lang.replace("--ja", c2 + "--ja" + cr) specify_lang = specify_lang.replace("--ko", c2 + "--ko" + cr) specify_lang = specify_lang.replace("--pt", c2 + "--pt" + cr) specify_lang = specify_lang.replace("--ru", c2 + "--ru" + cr) specify_lang = specify_lang.replace("--es", c2 + "--es" + cr) specify_lang = specify_lang.replace("--English", c2 + "--English" + cr) specify_lang = specify_lang.replace("--Chinese", c2 + "--Chinese" + cr) specify_lang = specify_lang.replace("--Dutch", c2 + "--Dutch" + cr) specify_lang = specify_lang.replace("--French", c2 + "--French" + cr) specify_lang = specify_lang.replace("--Italian", c2 + "--Italian" + cr) specify_lang = specify_lang.replace("--Japanese", c2 + "--Japanese" + cr) specify_lang = specify_lang.replace("--Korean", c2 + "--Korean" + cr) specify_lang = specify_lang.replace( "--Portuguese", c2 + "--Portuguese" + cr ) specify_lang = specify_lang.replace("--Russian", c2 + "--Russian" + cr) specify_lang = specify_lang.replace("--Spanish", c2 + "--Spanish" + cr) specify_action = specify_action.replace(">*", c6 + ">*") specify_action = specify_action.replace("*<", "*<" + cr) specify_action = specify_action.replace( "Action Options:", c4 + "Action Options:" + cr ) specify_action = specify_action.replace("> *** ", c3 + "> *** " + cr) specify_action = specify_action.replace(" *** <", c3 + " *** <" + cr) specify_action = specify_action.replace(" -p", " " + c1 + "-p" + cr) specify_action = specify_action.replace(" -o", " " + c1 + "-o" + cr) specify_action = specify_action.replace(" -c", " " + c1 + "-c" + cr) specify_action = specify_action.replace( " --print", " " + c1 + "--print" + cr ) specify_action = specify_action.replace( " --overwrite", " " + c1 + "--overwrite" + cr ) specify_action = specify_action.replace( " --copy", " " + c1 + "--copy" + cr ) example_run = example_run.replace("Examples:", c4 + "Examples:" + cr) example_run = example_run.replace("> *** ", c3 + "> *** " + cr) example_run = example_run.replace(" *** <", c3 + " *** <" + cr) example_run = example_run.replace(" -p", " " + c1 + "-p" + cr) example_run = example_run.replace(" -o", " " + c1 + "-o" + cr) example_run = example_run.replace(" -c", " " + c1 + "-c" + cr) example_run = example_run.replace("Chinese", c2 + "Chinese" + cr) example_run = example_run.replace("Portuguese", c2 + "Portuguese" + cr) example_run = example_run.replace("Dutch", c2 + "Dutch" + cr) example_run = example_run.replace(" --zh", " " + c2 + "--zh" + cr) example_run = example_run.replace(" --pt", " " + c2 + "--pt" + cr) example_run = example_run.replace(" --nl", " " + c2 + "--nl" + cr) example_run = example_run.replace("sbase", c4 + "sbase" + cr) usage = usage.replace("Usage:", c4 + "Usage:" + cr) usage = usage.replace("> *** ", c3 + "> *** " + cr) usage = usage.replace(" *** <", c3 + " *** <" + cr) usage = usage.replace("SB_FILE.py", c4 + "SB_FILE.py" + cr) usage = usage.replace("LANGUAGE", c2 + "LANGUAGE" + cr) usage = usage.replace("ACTION", c1 + "ACTION" + cr) if help_me: message = "" if invalid_cmd: message += invalid_cmd message += specify_lang + specify_action + example_run + usage print("") raise Exception(message) if not overwrite and not copy and not print_only: message = specify_action + example_run + usage if not new_lang: message = specify_lang + specify_action + example_run + usage print("") raise Exception(message) if not new_lang: print("") raise Exception(specify_lang + example_run + usage) if overwrite and copy: part_1 = ( "\n* You can choose either {-o / --overwrite} " "OR {-c / --copy}, BUT * NOT BOTH *!\n" ) part_1 = part_1.replace("-o ", c1 + "-o" + cr + " ") part_1 = part_1.replace("--overwrite", c1 + "--overwrite" + cr) part_1 = part_1.replace("-c ", c1 + "-c" + cr + " ") part_1 = part_1.replace("--copy", c1 + "--copy" + cr) part_1 = part_1.replace("* NOT BOTH *", c6 + "* NOT BOTH *" + cr) message = part_1 + example_run + usage print("") raise Exception(message) with open(seleniumbase_file, mode="r", encoding="utf-8") as f: all_code = f.read() if "def test_" not in all_code and "from seleniumbase" not in all_code: print("") raise Exception( "\n\n`%s` is not a valid SeleniumBase test file!\n" "\nExpecting: [%s]\n" % (seleniumbase_file, expected_arg) ) all_code = all_code.replace("\t", " ") code_lines = all_code.split("\n") sb_lines, changed, d_l, found_bc = process_test_file(code_lines, new_lang) seleniumbase_lines = sb_lines detected_lang = d_l found_basecase = found_bc if not changed and found_basecase: print("") msg1 = " [[[[%s]]]] was already in [[[%s]]]!\n\n" "" % ( seleniumbase_file, new_lang, ) msg1 = msg1.replace("[[[[", "" + c3).replace("]]]]", cr + "") msg1 = msg1.replace("[[[", "" + c5).replace("]]]", cr + "") msg2 = None if print_only: msg2 = "*> *** No changes to display! *** <*" elif overwrite: msg2 = "*> *** No changes were made! *** <*" else: # "copy" action msg2 = "*> *** No action was taken! *** <*" msg2 = msg2.replace("*>", " " + c6).replace("<*", cr + "\n") print(msg1 + msg2) return if not changed and not found_basecase: print("") filename = c3 + seleniumbase_file + cr from_sb = c5 + "from seleniumbase" + cr msg0 = " * In order to translate the script,\n" msg1 = ' %s requires "%s..."\n' % (filename, from_sb) msg2 = " and a BaseCase import in a supported language!\n\n" msg3 = None if print_only: msg3 = "*> *** No changes to display! *** <*" elif overwrite: msg3 = "*> *** No changes were made! *** <*" else: # "copy" action msg3 = "*> *** No action was taken! *** <*" msg3 = msg3.replace("*>", " " + c6).replace("<*", cr + "\n") print(msg0 + msg1 + msg2 + msg3) return save_line = ( " [[[[%s]]]] was translated to [[[%s]]]! " "(Previous: %s)\n" "" % (seleniumbase_file, new_lang, detected_lang) ) save_line = save_line.replace("[[[[", "" + c4) save_line = save_line.replace("]]]]", cr + "") save_line = save_line.replace("[[[", "" + c2) save_line = save_line.replace("]]]", cr + "") if print_only: from rich.console import Console from rich.syntax import Syntax console_width = None # width of console output when running script used_width = None # code_width and few spaces on right for padding magic_console = None magic_syntax = None try: console_width = os.popen("stty size", "r").read().split()[1] if console_width: console_width = int(console_width) except Exception: console_width = None python_code = "\n".join(seleniumbase_lines) code_width = 1 w = 0 # line number whitespace if line_numbers: w = 4 num_lines = len(code_lines) if num_lines >= 10: w = 5 if num_lines >= 100: w = 6 if num_lines >= 1000: w = 7 if num_lines >= 10000: w = 8 new_sb_lines = [] for line in seleniumbase_lines: if line.endswith(" # noqa") and line.count(" # noqa") == 1: line = line.replace(" # noqa", "") line_length2 = len(line) # Normal Python string length used line_length = get_width(line) # Special characters count 2X if line_length > code_width: code_width = line_length if console_width: # If line is larger than console_width, try to optimize it. # Smart Python word wrap to be used with valid indentation. if line_length + w > console_width: # 5 is line number ws if line.strip().startswith("#"): new_sb_lines.append(line) continue elif ( line.count(" # ") == 1 and get_width(line.split(" # ")[0]) + w <= console_width ): # Line is short enough once comment is removed line = line.split(" # ")[0] new_sb_lines.append(line) continue elif ( line.count(" # ") == 1 and get_width(line.split(" # ")[0]) + w <= console_width ): # L-Length good if removing bad flake8 comment line = line.split(" # ")[0] new_sb_lines.append(line) continue if line.startswith("from") and " import " in line: line1 = line.split(" import ")[0] + " \\" line2 = " import " + line.split(" import ")[1] new_sb_lines.append(line1) new_sb_lines.append(line2) continue if ( line.count("(") >= 1 and line.count("(") == line.count(")") ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " first_paren = line.find("(") line1 = line[:first_paren + 1] line2 = new_ws + line[first_paren + 1:] if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('", "') == 1: line2a = line2.split('", "')[0] + '",' line2b = ( new_ws + '"' + (line2.split('", "')[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count("', '") == 1: line2a = line2.split("', '")[0] + "'," line2b = ( new_ws + "'" + (line2.split("', '")[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count("://") == 1 and ( line2.count('")') == 1 ): line2a = line2.split("://")[0] + '://"' line2b = ( new_ws + '"' + (line2.split("://")[1]) ) new_sb_lines.append(line2a) if get_width(line2b) + w > ( console_width ): if line2b.count("/") > 0: slash_one = line2b.find("/") slash_one_p1 = slash_one + 1 line2b1 = ( line2b[:slash_one_p1] + '"' ) line2b2 = ( new_ws + '"' + (line2b[slash_one_p1:]) ) new_sb_lines.append(line2b1) if line2b2.count(") # ") == 1: line2b2 = ( line2b2.split(") # ")[ 0 ] + ")" ) new_sb_lines.append(line2b2) continue new_sb_lines.append(line2b) continue elif line2.count("://") == 1 and ( line2.count("')") == 1 ): line2a = line2.split("://")[0] + "://'" line2b = ( new_ws + "'" + (line2.split("://")[1]) ) new_sb_lines.append(line2a) if get_width(line2b) + w > ( console_width ): if line2b.count("/") > 0: slash_one = line2b.find("/") slash_one_p1 = slash_one + 1 line2b1 = ( line2b[:slash_one_p1] + "'" ) line2b2 = ( new_ws + "'" + (line2b[slash_one_p1:]) ) new_sb_lines.append(line2b1) if line2b2.count(") # ") == 1: line2b2 = ( line2b2.split(") # ")[ 0 ] + ")" ) new_sb_lines.append(line2b2) continue new_sb_lines.append(line2b) continue elif line2.count(", ") == 1: line2a = line2.split(", ")[0] + "," line2b = new_ws + line2.split(", ")[1] new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count('="') == 1 and ( line2.lstrip().startswith("'") ): line2a = line2.split('="')[0] + "='" line2b = ( new_ws + "'\"" + (line2.split('="')[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue elif line2.count("='") == 1 and ( line2.lstrip().startswith('"') ): line2a = line2.split("='")[0] + '="' line2b = ( new_ws + "\"'" + (line2.split("='")[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) elif get_width(line2) + 4 + w <= console_width: line2 = " " + line2 new_sb_lines.append(line1) new_sb_lines.append(line2) else: new_sb_lines.append(line) continue if line.count('("') == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split('("')[0] + "(" line2 = new_ws + '"' + line.split('("')[1] if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count('" in self.') == 1: line2a = ( line2.split('" in self.')[0] + '" in' ) line2b = ( new_ws + "self." + (line2.split('" in self.')[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) elif get_width(line2) + 4 + w <= console_width: line2 = " " + line2 new_sb_lines.append(line1) new_sb_lines.append(line2) else: new_sb_lines.append(line) continue if line.count("('") == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("('")[0] + "(" line2 = new_ws + "'" + line.split("('")[1] if ("):") not in line2: new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("' in self.") == 1: line2a = ( line2.split("' in self.")[0] + "' in" ) line2b = ( new_ws + "self." + (line2.split("' in self.")[1]) ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) elif get_width(line2) + 4 + w <= console_width: line2 = " " + line2 new_sb_lines.append(line1) new_sb_lines.append(line2) else: new_sb_lines.append(line) continue if line.count('= "') == 1 and line.count("://") == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("://")[0] + '://" \\' line2 = new_ws + '"' + line.split("://")[1] new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("/") > 0: slash_one = line2.find("/") slash_one_p1 = slash_one + 1 line2a = line2[:slash_one_p1] + '" \\' line2b = ( new_ws + '"' + line2[slash_one_p1:] ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) continue if line.count("= '") == 1 and line.count("://") == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("://")[0] + "://' \\" line2 = new_ws + "'" + line.split("://")[1] new_sb_lines.append(line1) if get_width(line2) + w > console_width: if line2.count("/") > 0: slash_one = line2.find("/") slash_one_p1 = slash_one + 1 line2a = line2[:slash_one_p1] + "' \\" line2b = ( new_ws + "'" + line2[slash_one_p1:] ) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue new_sb_lines.append(line2) continue if line.count("(self.") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split("(self.")[0] + "(" line2 = new_ws + "self." + line.split("(self.")[1] if get_width(line1) + w <= console_width: new_sb_lines.append(line1) new_sb_lines.append(line2) continue if line.count(" == ") == 1 and not ( line.endswith(":") or (": #") in line ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" == ")[0] + " == (" line2 = new_ws + line.split(" == ")[1] + ")" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2) continue if line.count(" == ") == 1 and line.endswith(":"): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" == ")[0] + " == (" line2 = new_ws + line.split(" == ")[1][:-1] + "):" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2) continue if ( line.count(" == ") == 1 and (line.count(": #") == 1) and (line.find(" == ") < line.find(": #")) ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " comments = " #" + line.split(": #")[1] line0 = line.split(": #")[0] + ":" line1 = line0.split(" == ")[0] + " == (" line2 = new_ws + line0.split(" == ")[1][:-1] + "):" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) if ( get_width(line2 + comments) + w <= console_width ): new_sb_lines.append(line2 + comments) else: new_sb_lines.append(line2) continue if line.count(" % ") == 1 and ("):") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" % ")[0] + " \\" line2 = new_ws + "% " + line.split(" % ")[1] if get_width(line1) + w <= console_width: new_sb_lines.append(line1) new_sb_lines.append(line2) continue if line.count(" = ") == 1 and (" # ") not in line: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " line1 = line.split(" = ")[0] + " = (" line2 = new_ws + line.split(" = ")[1] + ")" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2) continue elif get_width(line1) + w <= console_width: if line2.count(" % ") == 1 and not ( line2.endswith(":") ): whitespace = line_length2 - len( line2.lstrip() ) line2a = line2.split(" % ")[0] + " \\" line2b = ( new_ws + "% " + line2.split(" % ")[1] ) if get_width(line2a) + w <= console_width: if ( get_width(line2b) + w <= console_width ): new_sb_lines.append(line1) new_sb_lines.append(line2a) new_sb_lines.append(line2b) continue if ( line.count(" = ") == 1 and (line.count(" # ") == 1) and (line.find(" = ") < line.find(" # ")) ): whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " comments = " # " + line.split(" # ")[1] line0 = line.split(" # ")[0] line1 = line0.split(" = ")[0] + " = (" line2 = new_ws + line0.split(" = ")[1] + ")" if get_width(line1) + w <= console_width and ( get_width(line2) + w <= console_width ): new_sb_lines.append(line1) if ( get_width(line2 + comments) + w <= console_width ): new_sb_lines.append(line2 + comments) else: new_sb_lines.append(line2) continue new_sb_lines.append(line) if new_sb_lines: seleniumbase_lines = new_sb_lines python_code = "\n".join(seleniumbase_lines) extra_r_spaces = 2 if console_width and (code_width + extra_r_spaces < console_width): used_width = code_width + extra_r_spaces magic_syntax = Syntax( python_code, "python", theme="monokai", line_numbers=line_numbers, code_width=used_width, word_wrap=word_wrap, ) magic_console = Console() print("") print(save_line) print(" " + c1 + " *** Here are the results: >>> " + cr) # ---------------------------------------- dash_length = 62 # May change if used_width and used_width + w < console_width: dash_length = used_width + w elif console_width: dash_length = console_width dashes = "-" * dash_length print(dashes) print_success = False if magic_syntax: try: magic_console.print(magic_syntax) print_success = True except Exception: pass if not magic_syntax or not print_success: for line in seleniumbase_lines: print(line) print(dashes) # ---------------------------------------- new_file_name = None if copy: base_file_name = seleniumbase_file.split(".py")[0] new_locale = MD_F.get_locale_code(new_lang) new_ext = "_" + new_locale + ".py" for locale in MD_F.get_locale_list(): ext = "_" + locale + ".py" if seleniumbase_file.endswith(ext): base_file_name = seleniumbase_file.split(ext)[0] break new_file_name = base_file_name + new_ext elif overwrite: new_file_name = seleniumbase_file else: pass # Print-only run already done if not print_only: print("") print(save_line) else: pass # Print-only run already done if new_file_name: out_file = open(new_file_name, mode="w+", encoding="utf-8") out_file.writelines("\r\n".join(seleniumbase_lines)) out_file.close() results_saved = ( "The translation was saved to: [[[%s]]]\n" "" % new_file_name ) results_saved = results_saved.replace("[[[", "" + c1) results_saved = results_saved.replace("]]]", cr + "") print(results_saved) if __name__ == "__main__": invalid_run_command() ================================================ FILE: seleniumbase/undetected/__init__.py ================================================ import logging import os import re import requests import subprocess import sys import time from filelock import FileLock import selenium.webdriver.chrome.service import selenium.webdriver.chrome.webdriver import selenium.webdriver.common.service import selenium.webdriver.remote.command from contextlib import suppress from .cdp import CDP from .cdp import PageElement from .dprocess import start_detached from .options import ChromeOptions from .reactor import Reactor from .webelement import WebElement __all__ = ( "Chrome", "ChromeOptions", "Patcher", "Reactor", "CDP", "find_chrome_executable", ) IS_MAC = "darwin" in sys.platform IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux")) logger = logging.getLogger("uc") logger.setLevel(logging.getLogger().getEffectiveLevel()) class Chrome(selenium.webdriver.chrome.webdriver.WebDriver): """Controls chromedriver to drive a browser. The driver gets downloaded automatically.""" _instances = set() session_id = None debug = False def __init__( self, options=None, user_data_dir=None, driver_executable_path=None, browser_executable_path=None, port=0, enable_cdp_events=False, log_level=0, headless=False, patch_driver=True, version_main=None, patcher_force_close=False, suppress_welcome=True, use_subprocess=True, debug=False, **kw, ): """ Starts the Chrome service and creates a new instance of chromedriver. Parameters ---------- options: (default: None) Takes an instance of ChromeOptions to customize browser behavior. user_data_dir: None (default) Create a temp profile directory for the browser. If user_data_dir is a path to a valid Chrome profile directory, use it and turn off the automatic removal mechanism at exit. driver_executable_path: None (default) Downloads and patches the new binary. browser_executable_path: None (default) Use find_chrome_executable(). (If not specified, make sure Chrome is on the PATH.) port: (default: 0) Port you would like the service to run. If left as 0, a free port will be found. enable_cdp_events: (default: False) This enables the handling of wire messages. When enabled, you can subscribe to CDP events by using: driver.add_cdp_listener("Network.dataReceived", yourcallback) # yourcallback: callable that accepts exactly 1 dict parameter. log_level: (default: adapts to python global log level) headless: (default: False) Use headless mode. (Already handled by seleniumbase/core/browser_launcher.py) patch_driver: (default: True) Patches uc_driver to be undetectable if not already patched. version_main: (default: None) Overrides the browser version for older versions of Chrome. Eg: version_main=96 (Useful when you have a newer driver, but an older browser.) patcher_force_close: (default: False) Instructs patcher to access the chromedriver binary. If the file is locked, it will force shutdown all instances. Setting this is not recommended, unless you know the implications. suppress_welcome: (default: True) Suppress the Chrome welcome screen that appears on first-time runs. use_subprocess: (default: True) Subprocess chromedriver/python: Don't make Chrome a parent process. """ self.debug = debug self.patcher = None import fasteners from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils if patch_driver: uc_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with uc_lock: from .patcher import Patcher self.patcher = Patcher( executable_path=driver_executable_path, force=patcher_force_close, version_main=version_main, ) self.patcher.auto() if not options: options = ChromeOptions() try: if hasattr(options, "_session") and options._session is not None: # Prevent reuse of options. # (Probably a port overlap. Quit existing driver and continue.) logger.debug("You cannot reuse the ChromeOptions object") with suppress(Exception): options._session.quit() except AttributeError: pass options._session = self debug_host = "127.0.0.1" debug_port = 9222 special_port_free = False # If the port isn't free, don't use 9222 try: with requests.Session() as session: res = session.get( "http://127.0.0.1:9222", headers={"Connection": "close"}, timeout=2, ) if res.status_code != 200: raise Exception("The port is free! It will be used!") except Exception: # Use port 9222, which outputs to chrome://inspect/#devices special_port_free = True sys_argv = sys.argv arg_join = " ".join(sys_argv) from seleniumbase import config as sb_config if ( (("-n" in sys.argv) or (" -n=" in arg_join) or ("-c" in sys.argv)) or getattr(sb_config, "multi_proxy", None) or not special_port_free ): debug_port = selenium.webdriver.common.service.utils.free_port() if hasattr(options, "_remote_debugging_port"): # The user chooses the port. Errors happen if the port is taken. debug_port = options._remote_debugging_port if not options.debugger_address: options.debugger_address = "%s:%d" % (debug_host, debug_port) if enable_cdp_events: options.set_capability( "goog:loggingPrefs", {"performance": "ALL", "browser": "ALL"} ) options.add_argument("--remote-debugging-host=%s" % debug_host) options.add_argument("--remote-debugging-port=%s" % debug_port) if user_data_dir: user_data_dir = os.path.abspath(user_data_dir) options.add_argument("--user-data-dir=%s" % user_data_dir) language, keep_user_data_dir = None, bool(user_data_dir) # See if a custom user profile is specified in options for arg in options.arguments: if "lang" in arg: m = re.search("(?:--)?lang(?:[ =])?(.*)", arg) try: language = m[1] except IndexError: language = "en-US,en;q=0.9" if "user-data-dir" in arg: m = re.search("(?:--)?user-data-dir(?:[ =])?(.*)", arg) try: user_data_dir = m[1] keep_user_data_dir = True except IndexError: pass if not user_data_dir: if getattr(options, "user_data_dir", None): options.add_argument( "--user-data-dir=%s" % options.user_data_dir ) keep_user_data_dir = True else: import tempfile user_data_dir = os.path.normpath(tempfile.mkdtemp()) keep_user_data_dir = False arg = "--user-data-dir=%s" % user_data_dir # Create a temporary folder for the user-data profile. options.add_argument(arg) if not language: with suppress(Exception): import locale language = locale.getlocale()[0].replace("_", "-") if ( not language or "English" in language or "United States" in language ): language = "en-US" options.add_argument("--lang=%s" % language) if not options.binary_location: binary_location = ( browser_executable_path or find_chrome_executable() ) if binary_location: options.binary_location = binary_location else: # Improve the default error message in this situation. # Setting options.binary_location to None results in: # "TypeError: Binary Location Must be a String" raise Exception("Chrome not found! Install it first!") self._delay = constants.UC.RECONNECT_TIME self.user_data_dir = user_data_dir self.keep_user_data_dir = keep_user_data_dir if suppress_welcome: options.arguments.extend( [ "--no-default-browser-check", "--no-first-run", "--no-service-autorun", "--password-store=basic", "--profile-directory=Default", ] ) options.add_argument( "--log-level=%d" % log_level or divmod(logging.getLogger().getEffectiveLevel(), 10)[0] ) if hasattr(options, 'handle_prefs'): options.handle_prefs(user_data_dir) with suppress(Exception): import json with open( os.path.join( os.path.abspath(user_data_dir), "Default", "Preferences", ), encoding="utf-8", mode="r+", errors="ignore", ) as fs: config = json.load(fs) if ( "exit_type" not in config["profile"].keys() or config["profile"]["exit_type"] is not None ): config["profile"]["exit_type"] = None fs.seek(0, 0) fs.truncate() json.dump(config, fs) creationflags = 0 if "win32" in sys.platform: creationflags = subprocess.CREATE_NO_WINDOW self.options = options uc_lock = fasteners.InterProcessLock( constants.MultiBrowser.DRIVER_FIXING_LOCK ) with uc_lock: if not use_subprocess: self.browser_pid = start_detached( options.binary_location, *options.arguments ) else: gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK) with gui_lock: shared_utils.make_writable( constants.MultiBrowser.PYAUTOGUILOCK ) browser = subprocess.Popen( [options.binary_location, *options.arguments], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=IS_POSIX, creationflags=creationflags, ) self.browser_pid = browser.pid service_ = None log_output = subprocess.PIPE if patch_driver: service_ = selenium.webdriver.chrome.service.Service( executable_path=self.patcher.executable_path, service_args=["--disable-build-check"], port=port, log_output=log_output, ) else: service_ = selenium.webdriver.chrome.service.Service( executable_path=driver_executable_path, service_args=["--disable-build-check"], port=port, log_output=log_output, ) if hasattr(service_, "creationflags"): setattr(service_, "creationflags", creationflags) if hasattr(service_, "creation_flags"): setattr(service_, "creation_flags", creationflags) try: super().__init__(options=options, service=service_) except OSError as e: if IS_MAC and "Bad CPU type in executable" in str(e): print(str(e)) message = ( "Missing a macOS dependency:\n" "Your Mac needs Rosetta 2 to use UC Mode!\n" 'Run: "softwareupdate --install-rosetta"\n' "Info: " "https://apple.stackexchange.com/a/408379/607628" ) raise Exception(message) else: raise self.reactor = None if enable_cdp_events: if logging.getLogger().getEffectiveLevel() == logging.DEBUG: logging.getLogger( "selenium.webdriver.remote.remote_connection" ).setLevel(20) reactor = Reactor(self) reactor.start() self.reactor = reactor self._web_element_cls = WebElement def __getattribute__(self, item): if not super().__getattribute__("debug"): return super().__getattribute__(item) else: import inspect original = super().__getattribute__(item) if inspect.ismethod(original) and not inspect.isclass(original): def newfunc(*args, **kwargs): return original(*args, **kwargs) return newfunc return original def __dir__(self): return object.__dir__(self) def _get_cdc_props(self): cdc_props = [] with suppress(Exception): cdc_props = self.execute_script( """ let objectToInspect = window, result = []; while(objectToInspect !== null) { result = result.concat( Object.getOwnPropertyNames(objectToInspect) ); objectToInspect = Object.getPrototypeOf(objectToInspect); } return result.filter(i => i.match(/^[a-z]{3}_[a-z]{22}_.*/i)) """ ) return cdc_props def _hook_remove_cdc_props(self, cdc_props): if len(cdc_props) < 1: return cdc_props_js_array = "[" + ", ".join( '"' + p + '"' for p in cdc_props ) + "]" self.execute_cdp_cmd( "Page.addScriptToEvaluateOnNewDocument", { "source": cdc_props_js_array + ( ".forEach(p => delete window[p]);" ) }, ) def remove_cdc_props_as_needed(self): cdc_props = self._get_cdc_props() if len(cdc_props) > 0: self._hook_remove_cdc_props(cdc_props) time.sleep(0.05) def get(self, url): self.remove_cdc_props_as_needed() return super().get(url) def add_cdp_listener(self, event_name, callback): if ( getattr(self, "reactor", None) and isinstance(self.reactor, Reactor) ): self.reactor.add_event_handler(event_name, callback) return self.reactor.handlers return False def clear_cdp_listeners(self): if ( getattr(self, "reactor", None) and isinstance(self.reactor, Reactor) ): self.reactor.handlers.clear() def window_new(self, url=None): self.execute( selenium.webdriver.remote.command.Command.NEW_WINDOW, {"type": "window"}, ) if url: self.remove_cdc_props_as_needed() return super().get(url) return None def tab_new(self, url): """Open url in a new tab.""" if not hasattr(self, "cdp"): self.cdp = CDP(self.options) self.cdp.tab_new(str(url)) def tab_list(self): if not hasattr(self, "cdp"): self.cdp = CDP(self.options) retval = self.get(self.cdp.endpoints["list"]) return [PageElement(o) for o in retval] def reconnect(self, timeout=0.1): """This can be useful when sites use heavy detection methods: - Stops the chromedriver service that runs in the background. - Starts the chromedriver service that runs in the background. - Recreates the session.""" if hasattr(self, "service"): with suppress(Exception): if self.service.is_connectable(): self.stop_client() try: self.service.send_remote_shutdown_command() except TypeError: pass finally: with suppress(Exception): self.service._terminate_process() if isinstance(timeout, str): if timeout.lower() == "breakpoint": breakpoint() # To continue: pass # Type "c" & press ENTER! else: time.sleep(timeout) with suppress(Exception): self.service.start() with suppress(Exception): self.start_session() time.sleep(0.0075) with suppress(Exception): for window_handle in self.window_handles: self.switch_to.window(window_handle) if self.current_url.startswith("chrome-extension://"): # https://issues.chromium.org/issues/396611138 # (Remove the Linux conditional when resolved) # (So that close() is always called) if "linux" in sys.platform: self.close() if self.service.is_connectable(): self.stop_client() try: self.service.send_remote_shutdown_command() except TypeError: pass finally: with suppress(Exception): self.service._terminate_process() self.service.start() self.start_session() time.sleep(0.003) with suppress(Exception): self.switch_to.window(self.window_handles[-1]) self._is_connected = True def disconnect(self): """Stops the chromedriver service that runs in the background. To use driver methods again, you MUST call driver.connect()""" if hasattr(self, "service"): with suppress(Exception): if self.service.is_connectable(): self.stop_client() time.sleep(0.003) try: self.service.send_remote_shutdown_command() except TypeError: pass finally: with suppress(Exception): self.service._terminate_process() self._is_connected = False def connect(self): """Starts the chromedriver service that runs in the background and recreates the session.""" if hasattr(self, "service"): with suppress(Exception): self.service.start() with suppress(Exception): self.start_session() time.sleep(0.0075) with suppress(Exception): for window_handle in self.window_handles: self.switch_to.window(window_handle) current_url = None if hasattr(self, "cdp") and hasattr(self.cdp, "driver"): with suppress(Exception): current_url = self.cdp.get_current_url() if not current_url: current_url = self.current_url if current_url.startswith("chrome-extension://"): # https://issues.chromium.org/issues/396611138 # (Remove the Linux conditional when resolved) # (So that close() is always called) if "linux" in sys.platform: self.close() if self.service.is_connectable(): self.stop_client() try: self.service.send_remote_shutdown_command() except TypeError: pass finally: with suppress(Exception): self.service._terminate_process() self.service.start() self.start_session() time.sleep(0.003) with suppress(Exception): self.switch_to.window(self.window_handles[-1]) self._is_connected = True def start_session(self, capabilities=None): if not capabilities: capabilities = self.options.to_capabilities() super().start_session(capabilities) def quit(self): try: logger.debug("Terminating the UC browser") os.kill(self.browser_pid, 15) if "linux" in sys.platform: os.waitpid(self.browser_pid, 0) time.sleep(0.02) else: time.sleep(0.04) except (AttributeError, ChildProcessError, RuntimeError, OSError): time.sleep(0.05) except TimeoutError as e: logger.debug(e, exc_info=True) except Exception: pass with suppress(Exception): self.stop_client() with suppress(Exception): if hasattr(self, "command_executor") and self.command_executor: self.command_executor.close() # Remove instance reference to allow garbage collection Chrome._instances.discard(self) if hasattr(self, "service") and getattr(self.service, "process", None): logger.debug("Stopping webdriver service") with suppress(Exception): try: self.service.send_remote_shutdown_command() except TypeError: pass finally: with suppress(Exception): self.service._terminate_process() if ( hasattr(self, "reactor") and self.reactor and hasattr(self.reactor, "event") ): logger.debug("Shutting down Reactor") with suppress(Exception): self.reactor.event.set() self.reactor.join(timeout=2) self.reactor = None if ( hasattr(self, "keep_user_data_dir") and hasattr(self, "user_data_dir") and not self.keep_user_data_dir ): import shutil for _ in range(5): try: shutil.rmtree(self.user_data_dir, ignore_errors=False) except FileNotFoundError: pass except (RuntimeError, OSError, PermissionError) as e: logger.debug( "When removing the temp profile, a %s occured: " "%s\nRetrying..." % (e.__class__.__name__, e) ) else: logger.debug( "Successfully removed %s" % self.user_data_dir ) break time.sleep(0.1) # Dereference Patcher so that it can start cleaning up as well. # This must come last, otherwise it will throw "in use" errors. self.patcher = None def __del__(self): with suppress(Exception): if "win32" in sys.platform: self.stop_client() self.command_executor.close() else: super().quit() with suppress(Exception): self.quit() def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.reconnect(timeout=self._delay) def __hash__(self): return hash(self.options.debugger_address) def find_chrome_executable(): from seleniumbase.core import detect_b_ver binary_location = detect_b_ver.get_binary_location("google-chrome", True) if os.path.exists(binary_location) and os.access(binary_location, os.X_OK): return os.path.normpath(binary_location) candidates = set() if IS_POSIX: for item in os.environ.get("PATH").split(os.pathsep): for subitem in ( "google-chrome", "google-chrome-stable", "google-chrome-beta", "google-chrome-dev", "google-chrome-unstable", "chrome", "chromium", "chromium-browser", ): candidates.add(os.sep.join((item, subitem))) if "darwin" in sys.platform: gc = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" candidates.update( [ gc, "/Applications/Chromium.app/Contents/MacOS/Chromium" ] ) else: for item in map( os.environ.get, ( "PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA", "PROGRAMW6432", ), ): for subitem in ( "Google/Chrome/Application", "Google/Chrome Beta/Application", "Google/Chrome Canary/Application", ): candidates.add(os.sep.join((item, subitem, "chrome.exe"))) for candidate in candidates: if os.path.exists(candidate) and os.access(candidate, os.X_OK): return os.path.normpath(candidate) return None # Browser not found! ================================================ FILE: seleniumbase/undetected/cdp.py ================================================ import json import logging import requests import websockets log = logging.getLogger(__name__) class CDPObject(dict): def __init__(self, *a, **k): super().__init__(*a, **k) self.__dict__ = self for k in self.__dict__: if isinstance(self.__dict__[k], dict): self.__dict__[k] = CDPObject(self.__dict__[k]) elif isinstance(self.__dict__[k], list): for i in range(len(self.__dict__[k])): if isinstance(self.__dict__[k][i], dict): self.__dict__[k][i] = CDPObject(self) def __repr__(self): tpl = "%s(\n\t{{}}\n\t)" % self.__class__.__name__ return tpl.format( "\n ".join("%s = %s" % (k, v) for k, v in self.items()) ) class PageElement(CDPObject): pass class CDP: log = logging.getLogger("CDP") endpoints = CDPObject( { "json": "/json", "protocol": "/json/protocol", "list": "/json/list", "new": "/json/new?{url}", "activate": "/json/activate/{id}", "close": "/json/close/{id}", } ) def __init__(self, options): self.server_addr = "http://{0}:{1}".format( *options.debugger_address.split(":") ) self._reqid = 0 self._session = requests.Session() self._last_resp = None self._last_json = None with requests.Session() as session: resp = session.get( self.server_addr + self.endpoints.json, headers={"Connection": "close"}, timeout=2, ) self.sessionId = resp.json()[0]["id"] self.wsurl = resp.json()[0]["webSocketDebuggerUrl"] def tab_activate(self, id=None): if not id: active_tab = self.tab_list()[0] id = active_tab.id self.wsurl = active_tab.webSocketDebuggerUrl with requests.Session() as session: resp = session.post( self.server_addr + self.endpoints["activate"].format(id=id), headers={"Connection": "close"}, timeout=2, ) return resp.json() def tab_list(self): with requests.Session() as session: resp = session.get( self.server_addr + self.endpoints["list"], headers={"Connection": "close"}, timeout=2, ) retval = resp.json() return [PageElement(o) for o in retval] def tab_new(self, url): with requests.Session() as session: resp = session.post( self.server_addr + self.endpoints["new"].format(url=url), headers={"Connection": "close"}, timeout=2, ) return resp.json() def tab_close_last_opened(self): sessions = self.tab_list() opentabs = [s for s in sessions if s["type"] == "page"] with requests.Session() as session: endp_close = self.endpoints["close"] resp = session.post( self.server_addr + endp_close.format(id=opentabs[-1]["id"]), headers={"Connection": "close"}, timeout=2, ) return resp.json() async def send(self, method, params): self._reqid += 1 async with websockets.connect(self.wsurl) as ws: await ws.send( json.dumps( {"method": method, "params": params, "id": self._reqid} ) ) self._last_resp = await ws.recv() self._last_json = json.loads(self._last_resp) self.log.info(self._last_json) def get(self, uri): from urllib.parse import unquote uri = unquote(uri, errors="strict") with requests.Session() as session: resp = session.get( self.server_addr + uri, headers={"Connection": "close"}, timeout=2, ) try: self._last_resp = resp self._last_json = resp.json() except Exception: return else: return self._last_json def post(self, uri, data=None): from urllib.parse import unquote uri = unquote(uri, errors="strict") if not data: data = {} with requests.Session() as session: resp = session.post( self.server_addr + uri, json=data, headers={"Connection": "close"}, timeout=2, ) try: self._last_resp = resp self._last_json = resp.json() except Exception: return self._last_resp @property def last_json(self): return self._last_json ================================================ FILE: seleniumbase/undetected/cdp_driver/__init__.py ================================================ from seleniumbase.undetected.cdp_driver import cdp_util # noqa from seleniumbase.undetected.cdp_driver.cdp_util import start_async # noqa from seleniumbase.undetected.cdp_driver.cdp_util import start_sync # noqa ================================================ FILE: seleniumbase/undetected/cdp_driver/_contradict.py ================================================ import warnings as _warnings from collections.abc import Mapping as _Mapping, Sequence as _Sequence import logging __logger__ = logging.getLogger(__name__) __all__ = ["cdict", "ContraDict"] def cdict(*args, **kwargs): """Factory function""" return ContraDict(*args, **kwargs) class ContraDict(dict): """ Directly inherited from dict. Accessible by attribute. o.x == o['x'] This works also for all corner cases. Native json.dumps and json.loads work with it. Names like "keys", "update", "values" etc won't overwrite the methods, but will just be available using dict lookup notation obj['items'] instead of obj.items. All key names are converted to snake_case. Hyphen's (-), dot's (.) or whitespaces are replaced by underscore (_). Autocomplete works even if the objects comes from a list. Recursive action. Dict assignments will be converted too. """ __module__ = None def __init__(self, *args, **kwargs): super().__init__() # silent = kwargs.pop("silent", False) _ = dict(*args, **kwargs) super().__setattr__("__dict__", self) for k, v in _.items(): _check_key(k, self, False, True) super().__setitem__(k, _wrap(self.__class__, v)) def __setitem__(self, key, value): super().__setitem__(key, _wrap(self.__class__, value)) def __setattr__(self, key, value): super().__setitem__(key, _wrap(self.__class__, value)) def __getattribute__(self, attribute): if attribute in self: return self[attribute] if not _check_key(attribute, self, True, silent=True): return getattr(super(), attribute) return object.__getattribute__(self, attribute) def _wrap(cls, v): if isinstance(v, _Mapping): v = cls(v) elif isinstance(v, _Sequence) and not isinstance( v, (str, bytes, bytearray, set, tuple) ): v = list([_wrap(cls, x) for x in v]) return v _warning_names = ( "items", "keys", "values", "update", "clear", "copy", "fromkeys", "get", "items", "keys", "pop", "popitem", "setdefault", "update", "values", "class", ) _warning_names_message = """\n\ While creating a ContraDict object, a key offending key name '{0}' has been found, which might behave unexpected. You will only be able to look it up using key, eg. myobject['{0}']. myobject.{0} will not work with that name.""" def _check_key( key: str, mapping: _Mapping, boolean: bool = False, silent=True ): """Checks `key` and warns if needed. :param key: :param boolean: return True or False instead of passthrough """ e = None if not isinstance(key, (str,)): if boolean: return True return key if key.lower() in _warning_names or any(_ in key for _ in ("-", ".")): if not silent: _warnings.warn(_warning_names_message.format(key)) e = True if not boolean: return key return not e ================================================ FILE: seleniumbase/undetected/cdp_driver/browser.py ================================================ """CDP-Driver is based on NoDriver""" from __future__ import annotations import asyncio import atexit import fasteners import http.cookiejar import json import logging import os import pathlib import pickle import re import shutil import time import urllib.parse import urllib.request import warnings from collections import defaultdict from contextlib import suppress from seleniumbase import config as sb_config from seleniumbase.fixtures import constants from seleniumbase.fixtures import shared_utils from typing import List, Optional, Set, Tuple, Union import mycdp as cdp from . import cdp_util as util from . import tab from ._contradict import ContraDict from .config import PathLike, Config, is_posix from .connection import Connection logger = logging.getLogger(__name__) def get_registered_instances(): return __registered__instances__ def deconstruct_browser(): for _ in __registered__instances__: if not _.stopped: _.stop(deconstruct=True) max_attempts = 5 for attempt in range(max_attempts): try: if _.config and not _.config.uses_custom_data_dir: if os.path.exists(_.config.user_data_dir): shutil.rmtree( _.config.user_data_dir, ignore_errors=False ) if not os.path.exists(_.config.user_data_dir): break else: time.sleep(0.12) except FileNotFoundError: break except (PermissionError, OSError) as e: if attempt == max_attempts - 1: logger.debug( "Problem removing data dir %s\n" "Consider checking whether it's there " "and remove it by hand\nerror: %s" % (_.config.user_data_dir, e) ) break time.sleep(0.12) continue logging.debug("Temp profile %s was removed." % _.config.user_data_dir) class Browser: """ The Browser object is the "root" of the hierarchy and contains a reference to the browser parent process. There should usually be only 1 instance of this. All opened tabs, extra browser screens, and resources will not cause a new Browser process, but rather create additional :class:`Tab` objects. So, besides starting your instance and first/additional tabs, you don't actively use it a lot under normal conditions. Tab objects will represent and control: - tabs (as you know them) - browser windows (new window) - iframe - background processes Note: The Browser object is not instantiated by __init__ but using the asynchronous :meth:`Browser.create` method. Note: In Chromium based browsers, there is a parent process which keeps running all the time, even if there are no visible browser windows. Sometimes it's stubborn to close it, so make sure that after using this library, the browser is correctly and fully closed/exited/killed. """ _process: asyncio.subprocess.Process _process_pid: int _http: HTTPApi = None _cookies: CookieJar = None config: Config connection: Connection @classmethod async def create( cls, config: Config = None, *, user_data_dir: PathLike = None, headless: bool = False, incognito: bool = False, guest: bool = False, browser_executable_path: PathLike = None, browser_args: List[str] = None, sandbox: bool = True, host: str = None, port: int = None, **kwargs, ) -> Browser: """Entry point for creating an instance.""" if not config: config = Config( user_data_dir=user_data_dir, headless=headless, incognito=incognito, guest=guest, browser_executable_path=browser_executable_path, browser_args=browser_args or [], sandbox=sandbox, host=host, port=port, **kwargs, ) try: instance = cls(config) await instance.start() except Exception: time.sleep(0.15) instance = cls(config) await instance.start() return instance def __init__(self, config: Config, **kwargs): """ Constructor. To create a instance, use :py:meth:`Browser.create(...)` :param config: """ try: asyncio.get_running_loop() except RuntimeError: raise RuntimeError( "{0} objects of this class are created " "using await {0}.create()".format( self.__class__.__name__ ) ) self.config = config self.targets: List = [] self.info = None self._target = None self._process = None self._process_pid = None self._keep_user_data_dir = None self._is_updating = asyncio.Event() self.connection: Connection = None logger.debug("Session object initialized: %s" % vars(self)) @property def websocket_url(self): return self.info.webSocketDebuggerUrl @property def main_tab(self) -> tab.Tab: """Returns the target which was launched with the browser.""" return sorted( self.targets, key=lambda x: x.type_ == "page", reverse=True )[0] @property def tabs(self) -> List[tab.Tab]: """Returns the current targets which are of type "page".""" tabs = filter(lambda item: item.type_ == "page", self.targets) return list(tabs) @property def cookies(self) -> CookieJar: if not self._cookies: self._cookies = CookieJar(self) return self._cookies @property def stopped(self): if self._process and self._process.returncode is None: return False return True async def wait(self, time: Union[float, int] = 1) -> Browser: """Wait for