Skip to main content

Moving an existing Python SeleniumBase or Selenium WebDriver test suite to Rust is rarely a pure translation exercise. Tests often contain dynamic expressions, custom helpers, fixtures, and framework plugins that cannot be converted automatically. The sbrs import-python command is designed to handle the mechanical parts and surface the parts that need human review.

This page explains how to use the importer, what it can and cannot translate, and how to validate the generated Rust.

When to use the importer#

Use sbrs import-python when:

  • You have many simple, linear tests that mostly navigate, click, type, and assert.
  • You want a starting point for a manual migration.
  • You need to compare the original Python with generated Rust side by side.
Warning

Do not expect the importer to produce production-ready Rust for complex suites without review. Treat its output as a first draft.

Convert a file from the CLI#

Terminal

The tool reads the Python file, detects whether it looks like SeleniumBase or plain Selenium WebDriver, and writes a Rust test that uses #[tokio::test] and run_browser_test.

Diagnostics are printed to standard error. If you omit --output, the generated Rust is written to standard output so you can inspect it before saving. Unless you pass --test-name, the test function is named after the Python file (login_test here). The file is written even when there are problems, but the command then exits with an error if any diagnostic has the Error severity.

The importer skips import, from, def, class, decorator and with SB( lines and reads the statements that remain, so every statement in the file ends up in one generated test, even when the Python file has several test methods.

Note

Split the test methods by hand: every statement of the file lands in one generated test.

Override detection#

The importer tries to detect the source API automatically, but you can override it:

Terminal
--sourceauto | selenium-base | selenium#

Force the source API interpretation.

--test-nameRust identifier#

Name the generated test function. Any valid Rust identifier.

--outputfile path#

Write generated Rust to a file instead of stdout.

What the importer converts#

The importer reads one statement at a time and understands these. In all cases the arguments must be string (or, for waits and sleep, number) literals. Arguments are matched with Python’s parameters, so selector="#a" and timeout=5 work as well as positional ones, and a left-out selector is body, as in Python.

SeleniumBase, as self. or sb. calls:

  • Navigation: open, visit, get, refresh, refresh_page, go_back, go_forward, maximize_window.
  • Interactions: click, click_link, click_link_text, click_partial_link_text, double_click, right_click, type, type_text, update_text (which clear the field), add_text and send_keys (which add to it, as in Python), enter_mfa_code, clear, submit, hover, check_if_unchecked, uncheck_if_checked, select_option_by_text.
  • Assertions: assert_element, assert_element_visible, assert_text, assert_text_visible, assert_exact_text, assert_text_not_visible, assert_exact_text_not_visible, assert_title, assert_title_contains, assert_url, assert_url_contains. The text assertions take the text first and the selector second in Rust too, so the arguments are copied as they are. A timeout argument is refused (the assertion waits for the default timeout), and so is a by that is not the CSS strategy.
  • Soft assertions: deferred_assert_element, deferred_assert_text, deferred_assert_exact_text (and their delayed_ names) and process_deferred_asserts.
  • Reads: get_text, get_attribute (the value is bound to an unused variable).
  • Waits: wait_for_element_visible, wait_for_element_present, wait_for_text and wait_for_exact_text (and their _visible names). The timeout is the timeout= argument and defaults to 10 seconds. And sleep.

What requires manual review#

The importer is conservative. Anything it does not understand is emitted as a TODO comment or a diagnostic. Common cases that need manual work include:

  • Dynamic expressions and string formatting for selectors or URLs.
  • Control flow such as loops, conditionals, and try/except blocks.
  • Complex XPath that has no CSS equivalent.
  • Custom helpers and page-object classes.
  • Pytest fixtures, parametrization, and plugins.
  • File uploads, downloads, and non-standard API calls.
  • Selenium WebDriverWait, Select(...).select_by_visible_text, ActionChains and keyboard chords for send_keys.

A statement the importer does not recognise becomes a TODO comment and a Warning diagnostic. One it recognises but cannot translate safely (a non-literal argument, an XPath with no CSS equivalent, an unknown By strategy) becomes a TODO comment and an Error diagnostic, which makes the CLI exit with an error.

Danger

Always review the generated file before running it.

Example conversion#

Given this Python file, the importer produces the Rust file next to it (the layout is rustfmt-able but not formatted):

Validate the generated Rust#

After conversion, run these steps before trusting the test:

Resolve the TODOs

Read the file and resolve every TODO comment.

Format

Run cargo fmt to normalize formatting.

Compile

Compile with cargo check or cargo build.

Run it safely

Run the test against a safe environment first.

Review the diagnostics

Review diagnostics printed by the importer.

Programmatic API#

The same importer is available from Rust code through import_python, ImportOptions, and PythonSource:

result.detected_source says which API family the importer assumed, and result.is_complete() is true when no diagnostic has the Error severity.

Migration strategy#

For large suites, a full automated migration is usually not practical. A more realistic approach is:

Start small

Pick a small, stable subset of tests to migrate first.

Generate drafts

Use sbrs import-python to generate drafts.

Refactor

Refactor the generated code into helpers and page objects in Rust.

Run both suites

Run both Python and Rust suites in parallel until confidence is high.

Migrate the rest

Gradually migrate the remaining tests, prioritizing the most valuable ones.