Migrate lint/format tooling to Ruff, update CI matrix, and stabilize cross-platform/firefox tests#1978
Conversation
… support to 3.10+
- Updated type hints to use the new syntax (e.g., `list[str]` instead of `List[str]`). - Improved readability by replacing `isinstance` checks with more concise conditions. - Simplified exception handling and error messages for better clarity. - Enhanced the `WindowInfo` class by switching from `namedtuple` to `NamedTuple`. - Streamlined the `get_window_infos` method to reduce redundancy. - Replaced assertions in unit tests with assert statements for consistency. - Updated the linting task to allow automatic fixing of issues. - Removed unnecessary imports and cleaned up code formatting.
- Updated test_event_firing_webdriver.py to use pytest for exception assertions. - Refactored test_plugin_keyword_tags.py to utilize MyLib and MyLibArgs classes. - Changed test_plugins.py to use NamedTuple for plugin representation and improved exception handling with pytest. - Modified test_entry_point.py to include pathlib import. - Reordered imports and improved exception assertions in various test files. - Enhanced test_webdrivercreator.py and test_webdrivercreator_executable_path.py for better readability and consistency. - Updated test_browsermanagement.py to use pytest for exception assertions and improved assertions. - Refactored test_click_modifier.py to streamline invalid modifier tests. - Improved test_cookie.py and test_expectedconditions.py for consistency and clarity. - Enhanced test_firefox_profile_parsing.py and test_input_text_file_decorator.py for better readability. - Refactored test_keyword_arguments_browsermanagement.py and test_keyword_arguments_element.py for consistency. - Updated test_windowmanager.py and test_elementfinder.py to improve exception handling and assertions. - Added translation tests and improved type handling in test_types.py. Co-authored-by: Copilot <copilot@github.com>
…revent password manager leak detection in Chrome
Co-authored-by: Copilot <copilot@github.com>
… commands Co-authored-by: Copilot <copilot@github.com>
…xclude Windows tests Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates the project’s Python lint/format tooling to Ruff, updates CI/runtime compatibility targets (Python 3.10+), and adjusts unit/acceptance tests so results are more consistent across platforms/browsers (notably Windows + Firefox).
Changes:
- Replaced Black/Flake8 with Ruff (config in
pyproject.toml, dev deps, Invoke tasks, and a dedicated GitHub Actions workflow). - Updated CI matrices and acceptance-test runner behavior (Windows exclusion tags, Firefox re-enabled with known-issue tagging).
- Modernized codebase typing/style for Python 3.10+ and refactored tests/helpers accordingly.
Reviewed changes
Copilot reviewed 98 out of 100 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| utest/test/utils/test_xpath_escape.py | Removes Windows skip and updates imports around approval tests. |
| utest/test/utils/test_types.py | Updates list construction style compatible with Ruff formatting. |
| utest/test/utils/test_package.py | Converts unittest assertions to pytest-style asserts. |
| utest/test/translation/test_translation.py | Import ordering + pytest fixture decorator normalization. |
| utest/test/translation/init.py | Package marker for translation tests. |
| utest/test/robotframework_seleniumlibrary_translation_list/init.py | Formatting cleanup for translation list helper. |
| utest/test/robotframework_seleniumlibrary_translation_fi/init.py | Formatting cleanup for FI translation helper. |
| utest/test/locators/test_windowmanager.py | Moves to pytest.raises/assert style and minor mock behavior adjustments. |
| utest/test/locators/test_elementfinder.py | Ruff-driven import cleanup, pytest fixture normalization, stronger exception matching. |
| utest/test/keywords/test_windowmananger_window_info.py | Import order fixes and pytest-style asserts. |
| utest/test/keywords/test_webdrivercache.py | Converts exception testing to pytest.raises and updates asserts/imports. |
| utest/test/keywords/test_waiting_stale_element_refereance_exception.py | Import order cleanup. |
| utest/test/keywords/test_tablekeywords.py | Import ordering cleanup. |
| utest/test/keywords/test_selenium_service_parser.py | Removes Windows skips and refactors assertions/formatting for approval tests. |
| utest/test/keywords/test_selenium_options_parser.py | Removes Windows skips and refactors formatting/assertions. |
| utest/test/keywords/test_screen_shot.py | Import ordering + spacing cleanup. |
| utest/test/keywords/test_runonfailure_from_lib.py | Import ordering cleanup. |
| utest/test/keywords/test_press_keys.py | Import ordering and removes Windows skip decorators. |
| utest/test/keywords/test_keyword_arguments_waiting.py | Removes unused variable in test. |
| utest/test/keywords/test_keyword_arguments_selectelement.py | Whitespace cleanup. |
| utest/test/keywords/test_keyword_arguments_formelement.py | Fixture normalization and variable rename to avoid keyword conflicts. |
| utest/test/keywords/test_keyword_arguments_element.py | Import ordering and formatting; adjusts ActionChains mocking formatting. |
| utest/test/keywords/test_keyword_arguments_browsermanagement.py | Import ordering and pytest-style asserts. |
| utest/test/keywords/test_javascript.py | Removes Windows skips and approval-test gating for Windows. |
| utest/test/keywords/test_input_text_file_decorator.py | Adjusts asserts to use is None. |
| utest/test/keywords/test_firefox_profile_parsing.py | Removes Windows skip and normalizes attribute detection formatting. |
| utest/test/keywords/test_expectedconditions.py | Renames test class and method; formatting. |
| utest/test/keywords/test_cookie.py | Fixture decorator normalization and whitespace cleanup. |
| utest/test/keywords/test_click_modifier.py | Tightens exception assertions using match=. |
| utest/test/keywords/test_browsermanagement.py | Import ordering, better error assertions, formatting and variable naming. |
| utest/test/keywords/IGNOREtest_webdrivercreator_service_log_path.py | Replaces namedtuple with NamedTuple; formatting and stubbing updates. |
| utest/test/keywords/IGNOREtest_webdrivercreator_executable_path.py | Mockito import ordering and formatting cleanup. |
| utest/test/keywords/IGNOREDtest_webdrivercreator.py | Mockito import ordering, stronger exception matching, minor lint suppressions. |
| utest/test/entry/test_entry_point.py | Import ordering cleanup. |
| utest/test/api/test_plugins.py | Converts helper namedtuple to NamedTuple; converts many asserts to pytest style. |
| utest/test/api/test_plugin_keyword_tags.py | Updates plugin class imports and converts assertions to pytest style. |
| utest/test/api/test_plugin_documentation.py | Removes Windows skips to allow running under normalized LF files. |
| utest/test/api/test_filepath_unusual_characters.py | Removes Windows skip to allow running under normalized LF files. |
| utest/test/api/test_event_firing_webdriver.py | Adds pytest.raises match assertions and converts asserts to pytest style. |
| utest/test/api/test_accessing_keywod_methods.py | Converts unittest raises/asserts to pytest equivalents. |
| utest/test/api/plugin_with_event_firing_webdriver.py | Renames class to CapWords and keeps backward-compatible alias. |
| utest/test/api/plugin_tester.py | Renames class to CapWords and keeps backward-compatible alias. |
| utest/test/api/my_lib_wrong_name.py | Renames class to CapWords. |
| utest/test/api/my_lib_not_inherit.py | Renames class to CapWords and keeps backward-compatible alias. |
| utest/test/api/my_lib_args.py | Renames class to CapWords and keeps backward-compatible alias. |
| utest/test/api/my_lib.py | Renames class and doc headings; keeps backward-compatible alias. |
| utest/test/api/approved_files/PluginDocumentation.test_many_plugins.approved.txt | Updates approved output to reflect renamed plugin classes/headings. |
| utest/run.py | Switches to pytest.main() and logs suppressed errors via logging. |
| tasks.py | Adds Ruff-based lint/format Invoke tasks; updates atest command to use sys.executable. |
| src/SeleniumLibrary/utils/types.py | Python 3.10 typing, minor formatting/flow simplification. |
| src/SeleniumLibrary/utils/events/event.py | Simplifies Selenium major version parsing. |
| src/SeleniumLibrary/utils/events/init.py | Import ordering and __all__ ordering cleanup. |
| src/SeleniumLibrary/utils/init.py | Consolidates type exports import and adjusts noqa placement. |
| src/SeleniumLibrary/locators/windowmanager.py | Replaces namedtuple with NamedTuple; simplifies control flow and naming. |
| src/SeleniumLibrary/locators/elementfinder.py | Uses PEP604 union types; improves exception chaining; minor simplifications. |
| src/SeleniumLibrary/locators/customlocator.py | Uses callable() and simplifies return flow. |
| src/SeleniumLibrary/keywords/window.py | PEP604 typing, import ordering, minor logic simplification and doc cleanup. |
| src/SeleniumLibrary/keywords/webdrivertools/webdrivertools.py | Refactors browser mapping, exception chaining, formatting, and service/options parsing. |
| src/SeleniumLibrary/keywords/webdrivertools/sl_file_detector.py | Simplifies boolean return and ensures explicit None return from _get_sl. |
| src/SeleniumLibrary/keywords/waiting.py | PEP604 typing; adjusts control flow for limit is None branches. |
| src/SeleniumLibrary/keywords/selectelement.py | PEP604 typing, simplifies return logic, stricter zip formatting. |
| src/SeleniumLibrary/keywords/screenshot.py | PEP604 typing, formatting cleanups, explicit None returns when no browser is open, PDF printing improvements. |
| src/SeleniumLibrary/keywords/runonfailure.py | PEP604 typing and refactor of keyword resolution logic. |
| src/SeleniumLibrary/keywords/javascript.py | Replaces namedtuple with NamedTuple; simplifies marker parsing flow. |
| src/SeleniumLibrary/keywords/formelement.py | PEP604 typing and exception chaining improvements. |
| src/SeleniumLibrary/keywords/expectedconditions.py | Improves error handling + messages; formatting and typing updates. |
| src/SeleniumLibrary/keywords/element.py | PEP604 typing; replaces namedtuple with NamedTuple; formatting and minor refactors. |
| src/SeleniumLibrary/keywords/cookie.py | PEP604 typing; simplifies branching; minor formatting adjustments. |
| src/SeleniumLibrary/keywords/browsermanagement.py | PEP604 typing; import reorg; exception chaining and warning message formatting. |
| src/SeleniumLibrary/keywords/alert.py | PEP604 typing; exception chaining; minor message formatting cleanup. |
| src/SeleniumLibrary/errors.py | Formatting-only adjustment. |
| src/SeleniumLibrary/entry/translation.py | PEP604 typing; local import with Ruff suppression; list type hint update. |
| src/SeleniumLibrary/entry/get_versions.py | Import ordering cleanup. |
| src/SeleniumLibrary/entry/main.py | PEP604 typing; replace prints with click.echo; minor cleanup. |
| src/SeleniumLibrary/base/librarycomponent.py | PEP604 typing and import ordering. |
| src/SeleniumLibrary/base/context.py | PEP604 typing and return type update for element lists. |
| src/SeleniumLibrary/init.py | PEP604 typing; replaces namedtuples with NamedTuples; refactors parsing helpers and messaging. |
| setup.py | Bumps python_requires to >=3.10. |
| requirements-dev.txt | Replaces Black/Flake8 with Ruff and updates dev tool pins. |
| pyproject.toml | Adds Ruff lint/format configuration. |
| docs/index.html | Updates documented supported Python versions. |
| atest/run.py | Adds Windows exclusion tag handling; formatting cleanup. |
| atest/resources/testlibs/get_driver_path.py | Minor formatting/quoting updates. |
| atest/acceptance/open_and_close.robot | Adds SKIP_ON_WINDOWS to a flaky/invalid-on-Windows case. |
| atest/acceptance/multiple_browsers_service.robot | Adds SKIP_ON_WINDOWS and adjusts known-issue tags. |
| atest/acceptance/multiple_browsers_options.robot | Adds SKIP_ON_WINDOWS tags to Windows-incompatible option parsing tests. |
| atest/acceptance/keywords/textfields.robot | Makes Chrome-only option conditional to avoid failures on non-Chrome browsers. |
| atest/acceptance/keywords/page_load_timeout.robot | Adds SKIP_ON_WINDOWS tag. |
| atest/acceptance/keywords/mouse.robot | Marks unstable mouse-over case as known issue for Firefox. |
| atest/acceptance/entry_point.robot | Normalizes path separators for cross-platform execution. |
| atest/acceptance/create_webdriver.robot | Adds SKIP_ON_WINDOWS tag. |
| atest/acceptance/2-event_firing_webdriver/event_firing_webdriver.robot | Adds SKIP_ON_WINDOWS tag. |
| atest/acceptance/1-plugin/OpenBrowserExample.py | Formatting for long signature line. |
| README.rst | Updates documented supported Python versions. |
| CONTRIBUTING.rst | Updates contributor guidance for Ruff + Invoke tasks, and adjusts docs task name. |
| .github/workflows/Select.yml | Expands matrix (adds Firefox) and updates actions/steps. |
| .github/workflows/LintFormatCheck.yml | Adds Ruff lint/format workflow using Invoke tasks. |
| .github/workflows/CI.yml | Updates Python matrix targets and browser setup actions. |
| .gitattibutes | Adds LF normalization rule for approval test approved files (but filename is misspelled). |
| .flake8 | Removes legacy Flake8 configuration. |
Comments suppressed due to low confidence (1)
.github/workflows/Select.yml:47
- This step is labeled “Setup ${{ matrix.config.browser }} browser” but always runs
browser-actions/setup-chrome, even when the matrix browser isfirefox. Either make browser setup conditional (Chrome vs Firefox) or rename the step so it doesn’t imply it’s configuring the selected browser.
- name: Setup ${{ matrix.config.browser }} browser
uses: browser-actions/setup-chrome@v2
with:
chrome-version: latest
install-dependencies: true
id: setup-chrome
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot <copilot@github.com>
…rkflows Co-authored-by: Copilot <copilot@github.com>
…side workflow Co-authored-by: Copilot <copilot@github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 97 out of 99 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL} | ||
| ... options=add_experimental_option("prefs", {"profile.password_manager_leak_detection": False}) alias=${alias} | ||
| ELSE | ||
| Open Browser ${FRONT PAGE} ${BROWSER} remote_url=${REMOTE_URL} alias=${alias} |
There was a problem hiding this comment.
If this test is designed to check chrome then should we be skipping it instead of running a non-test?
There was a problem hiding this comment.
Not a test, A keyword that is being used bu the tests, in case of Firefox/non-chrome this gives an error, since experimental options does not exist.
| Entry Point Translation | ||
| ${process} = Run Process | ||
| ... python -m SeleniumLibrary.entry translation ${OUTPUT_DIR}/translation.json | ||
| ... python -m SeleniumLibrary.entry translation ${OUTPUT_DIR}${/}translation.json |
There was a problem hiding this comment.
What is the RF standard for dealing with paths? I know I had once recommended switching / to ${/} but I recall someone else giving a strong argument for these to remain / noting that it will work for both Windows and Linux/MacOS (although I don't recall the underlying mechanisms. I thought this would fail on windows).
There was a problem hiding this comment.
Less concerned about this specific PR. I am good with changing this but trying to understand when we lint what the recommended results should be.
There was a problem hiding this comment.
I think for most paths I would (and did) not change this to ${/}, since windows will be able to handle /. In this specific case it did make a difference because it's output from the stdout that is string compared in the test.
|
|
||
| *** Test Cases *** | ||
| Chrome Browser With Selenium Options As String | ||
| [Tags] SKIP_ON_WINDOWS |
There was a problem hiding this comment.
[An aside, I had always wanted an easy to document but clear documented explanation as to why for wa particular test we would want to skip on a certain OS. Or if a known issues what change would trigger a reevalaution for the skip/ignore reason. That way when one came along latter one could evaklute if this was still the case.]
There was a problem hiding this comment.
These atests test on specific lines in the logging. Due to Windows specific stuff there are additional debug lines at the start which messes these up. So we could fix them, but this would mean the assertions will have to change quite a lot. Since this is not windows-specific functionality I figured this would not pose much of a risk if we skip these on Windows.
|
|
||
| See each command argument help for more details what (optional) arguments that command supports. | ||
| """ | ||
| pass |
There was a problem hiding this comment.
I don't like this Linter rule. I think ity make it less clear. Does the linter accept ... instead?
There was a problem hiding this comment.
No, also not accepted.
I can add # noqa: PIE790 to suppress the linting rule here.
| for library in libraries: | ||
| if isinstance(libraries[library], SeleniumLibrary.SeleniumLibrary): | ||
| return libraries[library] | ||
| return None |
There was a problem hiding this comment.
Does this affect any downstream usage of this method? Or is all this doing is explicitly setting the return to None?
There was a problem hiding this comment.
Does not affect at all. Just explicitly returns None, instead of doing it anyway if the for/if loop does not return anything. So normally would return None anyway if it reached here, but difficult to debug since it's not explicit.
| } | ||
|
|
||
| def __init__(self, log_dir): | ||
| self.browser_names = { |
There was a problem hiding this comment.
Do we want this as a class attribute (as was) or should it be an instance attribute (as changed to)?
There was a problem hiding this comment.
You're right, this would make more sense as a class attribute.
| @keyword | ||
| def input_text_into_alert( | ||
| self, text: str, action: str = ACCEPT, timeout: Optional[timedelta] = None | ||
| self, text: str, action: str = ACCEPT, timeout: timedelta | None = None |
There was a problem hiding this comment.
leaving note to self to relook at this ..
| return wait.until(EC.alert_is_present()) | ||
| except TimeoutException: | ||
| raise AssertionError(f"Alert not found in {secs_to_timestr(timeout)}.") | ||
| except TimeoutException as original_exception: |
There was a problem hiding this comment.
Is this a linter syntax or an AI syntax?
There was a problem hiding this comment.
Fom linter (rule B904), basically to better debug where the exception originated. In this case: instead of getting an assertion error that has something timeout in it. you'd get the explanation with it that the timeout exception caused the assertion error, preserving the full traceback
| def input_password( | ||
| self, locator: Locator, password: str, clear: bool = True | ||
| ): | ||
| def input_password(self, locator: Locator, password: str, clear: bool = True): |
There was a problem hiding this comment.
Why does it in one instance put all the arguments into one line. And then elsewhere want one argument per line?
There was a problem hiding this comment.
Basically comes down to 80 character line limit. If it exceeds it splits all the arguments to its own line. (all or nothing)
| class ScreenshotKeywords(LibraryComponent): | ||
| @keyword | ||
| def set_screenshot_directory(self, path: Union[None, str]) -> str: | ||
| def set_screenshot_directory(self, path: None | str) -> str: |
There was a problem hiding this comment.
This is an example oif why i think I like the syntax Optional[str] over str | None. That is if those are reversed as here, None | str that would seem to have a different meaning.
Maybe these are equivalent anyways and it is just that in the Optional case it has a default value .. 🤔
There was a problem hiding this comment.
These are 100% equivalent. None | str, str | None, and Optional[str] all produce the same type at runtime. Union order is irrelevant for type checking.
Optional[str] is literally just an alias for Union[str, None] and the UP007 rule converts that to str | None
Readability and convention say to put primary type (str) first before None, so we could update that, but no functional difference.
…OWS import which is no longer needed,. see robotframework#1884 Co-authored-by: Copilot <copilot@github.com>
This branch replaces legacy lint/format tooling with Ruff, updates CI and runtime compatibility targets, and cleans up platform/browser-specific test behavior so checks are consistent across environments.
Running the utests, atests, linting, and formatting should now work consistently on Windows and Unix based devices.
Major changes
tasks.pymouse.robotDocumentation updates
CONTRIBUTING.rstBehavioral impact
CI and quality checks
Notes