From 69f866875c4d755630ac4508010132f7a4f97a2f Mon Sep 17 00:00:00 2001 From: Viktoriia Ivanova Date: Fri, 22 Aug 2025 16:12:03 -0700 Subject: [PATCH 1/6] [java] Refactored selectByContainsVisibleText and selectByVisibleText methods to remove code duplication --- .../openqa/selenium/support/ui/Select.java | 165 ++++++++---------- 1 file changed, 69 insertions(+), 96 deletions(-) diff --git a/java/src/org/openqa/selenium/support/ui/Select.java b/java/src/org/openqa/selenium/support/ui/Select.java index 9942b7057832e..23c3bc8a72654 100644 --- a/java/src/org/openqa/selenium/support/ui/Select.java +++ b/java/src/org/openqa/selenium/support/ui/Select.java @@ -122,55 +122,7 @@ public WebElement getFirstSelectedOption() { */ @Override public void selectByVisibleText(String text) { - assertSelectIsEnabled(); - assertSelectIsVisible(); - - // try to find the option via XPATH ... - List options = - element.findElements( - By.xpath(".//option[normalize-space(.) = " + Quotes.escape(text) + "]")); - - for (WebElement option : options) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - } - - boolean matched = !options.isEmpty(); - if (!matched && text.contains(" ")) { - String subStringWithoutSpace = getLongestSubstringWithoutSpace(text); - List candidates; - if ("".equals(subStringWithoutSpace)) { - // hmm, text is either empty or contains only spaces - get all options ... - candidates = element.findElements(By.tagName("option")); - } else { - // get candidates via XPATH ... - candidates = - element.findElements( - By.xpath(".//option[contains(., " + Quotes.escape(subStringWithoutSpace) + ")]")); - } - - String trimmed = text.trim(); - - for (WebElement option : candidates) { - if (trimmed.equals(option.getText().trim())) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - matched = true; - } - } - } - - if (!matched) { - throw new NoSuchElementException("Cannot locate option with text: " + text); - } + selectByVisibleText(text, false); } /** @@ -193,53 +145,7 @@ public void selectByVisibleText(String text) { */ @Override public void selectByContainsVisibleText(String text) { - assertSelectIsEnabled(); - assertSelectIsVisible(); - - // try to find the option via XPATH ... - List options = - element.findElements( - By.xpath(".//option[normalize-space(.) = " + Quotes.escape(text) + "]")); - - for (WebElement option : options) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - } - - boolean matched = !options.isEmpty(); - if (!matched) { - String searchText = text.contains(" ") ? getLongestSubstringWithoutSpace(text) : text; - - List candidates; - if (searchText.isEmpty()) { - candidates = element.findElements(By.tagName("option")); - } else { - candidates = - element.findElements( - By.xpath(".//option[contains(., " + Quotes.escape(searchText) + ")]")); - } - - String trimmed = text.trim(); - for (WebElement option : candidates) { - if (option.getText().contains(trimmed)) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - matched = true; - } - } - } - - if (!matched) { - throw new NoSuchElementException("Cannot locate option with text: " + text); - } + selectByVisibleText(text, true); } private String getLongestSubstringWithoutSpace(String s) { @@ -425,6 +331,73 @@ private void setSelected(WebElement option, boolean select) { } } + /** + * @param text The visible text to match against. It can be a partial match of the option if + * isPartialMatch = true + * @param isPartialMatch If true a partial match on the Options list will be performed, otherwise + * exact match + * @throws NoSuchElementException If no matching option elements are found or matching options are + * hidden + */ + private void selectByVisibleText(String text, boolean isPartialMatch) { + assertSelectIsEnabled(); + assertSelectIsVisible(); + + // try to find the option via XPATH ... + List options = + element.findElements( + By.xpath(".//option[normalize-space(.) = " + Quotes.escape(text) + "]")); + + for (WebElement option : options) { + if (!hasCssPropertyAndVisible(option)) + throw new NoSuchElementException("Invisible option with text: " + text); + + setSelected(option, true); + + if (!isMultiple()) { + return; + } + } + + boolean matched = !options.isEmpty(); + if (!matched) { + String searchText = text.contains(" ") ? getLongestSubstringWithoutSpace(text) : text; + + List candidates; + if (searchText.isEmpty()) { + candidates = element.findElements(By.tagName("option")); + } else { + candidates = + element.findElements( + By.xpath(".//option[contains(., " + Quotes.escape(searchText) + ")]")); + } + + String trimmed = text.trim(); + for (WebElement option : candidates) { + boolean isMatchedOptionFound = + isPartialMatch + ? option.getText().contains(trimmed) + : option.getText().trim().equals(trimmed); + + if (isMatchedOptionFound) { + if (!hasCssPropertyAndVisible(option)) + throw new NoSuchElementException("Invisible option with text: " + text); + + setSelected(option, true); + + if (!isMultiple()) { + return; + } + matched = true; + } + } + } + + if (!matched) { + throw new NoSuchElementException("Cannot locate option with text: " + text); + } + } + private void assertOptionIsEnabled(WebElement option, boolean select) { if (select && !option.isEnabled()) { throw new UnsupportedOperationException("You may not select a disabled option"); From f8c356fe59459ab611071acae4bee438822ae987 Mon Sep 17 00:00:00 2001 From: Selenium CI Bot Date: Sat, 23 Aug 2025 04:39:36 +0200 Subject: [PATCH 2/6] [dotnet][rb][java][js][py] Automated Browser Version Update (#16247) Co-authored-by: Selenium CI Bot --- common/repositories.bzl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/common/repositories.bzl b/common/repositories.bzl index ac3cbd3bfdb8d..574e10bdee11d 100644 --- a/common/repositories.bzl +++ b/common/repositories.bzl @@ -50,8 +50,8 @@ js_library( http_archive( name = "linux_beta_firefox", - url = "https://ftp.mozilla.org/pub/firefox/releases/143.0b2/linux-x86_64/en-US/firefox-143.0b2.tar.xz", - sha256 = "70a8af598dedca123b72422811ec04e5a28060778d4a86622f4ecd400f234a78", + url = "https://ftp.mozilla.org/pub/firefox/releases/143.0b3/linux-x86_64/en-US/firefox-143.0b3.tar.xz", + sha256 = "ea43085f0a47d14a8c088175b489c8af149f59703aec20e6dd2bbf01e36d36fc", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -72,8 +72,8 @@ js_library( dmg_archive( name = "mac_beta_firefox", - url = "https://ftp.mozilla.org/pub/firefox/releases/143.0b2/mac/en-US/Firefox%20143.0b2.dmg", - sha256 = "52f9a7a944f857b207bc79f66ae2d600ff8736264d708d6c0fa5138aea664f97", + url = "https://ftp.mozilla.org/pub/firefox/releases/143.0b3/mac/en-US/Firefox%20143.0b3.dmg", + sha256 = "0e392fc487de20c31c4fe66e25473f91ce001dd6e56528409720f7b9934f34e0", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -165,8 +165,8 @@ js_library( http_archive( name = "linux_edgedriver", - url = "https://msedgedriver.microsoft.com/139.0.3405.102/edgedriver_linux64.zip", - sha256 = "e092178901c011ccfddc0556193742a01f206d71b5bb1a12cc1ba7a243262fc2", + url = "https://msedgedriver.microsoft.com/139.0.3405.111/edgedriver_linux64.zip", + sha256 = "76f1c0e35120e37848c174eee1368150b7c2461b8e4907c80bcfc2e61b93c471", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -182,8 +182,8 @@ js_library( http_archive( name = "mac_edgedriver", - url = "https://msedgedriver.microsoft.com/139.0.3405.102/edgedriver_mac64.zip", - sha256 = "98aa637081061ea610c1f70be09fdea6b5f5b3a46c94a87ca291e2285bad1328", + url = "https://msedgedriver.microsoft.com/139.0.3405.111/edgedriver_mac64.zip", + sha256 = "c97588078c6f661dd6c66b858f5e21dc9cbcee40420a2b2e839a05803e0e468f", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) From 51db4f25010a44687ee2f740875a4a0c05b528f1 Mon Sep 17 00:00:00 2001 From: Simon Mavi Stewart Date: Sun, 24 Aug 2025 16:37:49 +0100 Subject: [PATCH 3/6] [bazel] Bump `rules_jvm_external` to 6.8 --- MODULE.bazel | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 87a5a38cf54bf..0080fc4a90b22 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -19,7 +19,7 @@ bazel_dep(name = "rules_cc", version = "0.2.0", dev_dependency = True) bazel_dep(name = "rules_dotnet", version = "0.17.5") bazel_dep(name = "rules_java", version = "8.7.1") -bazel_dep(name = "rules_jvm_external", version = "6.6") +bazel_dep(name = "rules_jvm_external", version = "6.8") bazel_dep(name = "rules_multitool", version = "1.3.0") bazel_dep(name = "rules_nodejs", version = "6.3.2") bazel_dep(name = "rules_oci", version = "1.8.0") @@ -29,12 +29,10 @@ bazel_dep(name = "rules_proto", version = "7.0.2") bazel_dep(name = "rules_ruby", version = "0.19.0") # Until `rules_jvm_external` 6.8 ships -git_override( +single_version_override( module_name = "rules_jvm_external", - commit = "aca619b117c1fe306ffdd20c5f47cc4dbd5effed", patch_strip = 1, patches = ["//java:rules_jvm_external_javadoc.patch"], - remote = "https://github.com/bazel-contrib/rules_jvm_external.git", ) multitool = use_extension("@rules_multitool//multitool:extension.bzl", "multitool") From ca3b7cf7b26fcc1a7ed69c2a9460add951922196 Mon Sep 17 00:00:00 2001 From: Navin Chandra Date: Mon, 25 Aug 2025 12:02:11 +0530 Subject: [PATCH 4/6] [py][bidi]: enable `history_updated` event test (#16236) --- .../webdriver/common/bidi_browsing_context_tests.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py index 768640d7f71a8..254d2aace3c08 100644 --- a/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py +++ b/py/test/selenium/webdriver/common/bidi_browsing_context_tests.py @@ -756,9 +756,6 @@ def on_user_prompt_closed(info): driver.browsing_context.remove_event_handler("user_prompt_closed", callback_id) -@pytest.mark.xfail_chrome -@pytest.mark.xfail_firefox -@pytest.mark.xfail_edge def test_add_event_handler_history_updated(driver, pages): """Test adding event handler for history_updated event.""" events_received = [] @@ -769,16 +766,17 @@ def on_history_updated(info): callback_id = driver.browsing_context.add_event_handler("history_updated", on_history_updated) assert callback_id is not None - # Navigate to a page and use history API to trigger the event context_id = driver.current_window_handle url = pages.url("simpleTest.html") driver.browsing_context.navigate(context=context_id, url=url, wait=ReadinessState.COMPLETE) # Use history.pushState to trigger history updated event - driver.execute_script("history.pushState({}, '', '/new-path');") + driver.script.execute("() => { history.pushState({}, '', '/new-path'); }") + WebDriverWait(driver, 5).until(lambda d: len(events_received) > 0) - assert len(events_received) == 1 - assert any("/new-path" in event.url for event in events_received) + assert len(events_received) >= 1 + assert "/new-path" in events_received[0].url + assert events_received[0].context == context_id driver.browsing_context.remove_event_handler("history_updated", callback_id) From 58f2268a996f6bfd4f9916e4338fa8085db68cc5 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Mon, 25 Aug 2025 11:13:46 -0400 Subject: [PATCH 5/6] [py] Bump ruff version for linting/formatting (#16254) --- multitool.lock.json | 20 ++++++++++---------- py/tox.ini | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/multitool.lock.json b/multitool.lock.json index 1c51f6ba88b80..fcb1199ee8fe8 100644 --- a/multitool.lock.json +++ b/multitool.lock.json @@ -4,41 +4,41 @@ "binaries": [ { "kind": "archive", - "url": "https://github.com/astral-sh/ruff/releases/download/0.12.3/ruff-aarch64-unknown-linux-musl.tar.gz", + "url": "https://github.com/astral-sh/ruff/releases/download/0.12.10/ruff-aarch64-unknown-linux-musl.tar.gz", "file": "ruff-aarch64-unknown-linux-musl/ruff", - "sha256": "7890e49b12c1321688540324a7788457a22711657301598402cba1a9e9be6607", + "sha256": "10b43a88fb948aaae538ec9f35e93b4433f144d0379fb3a67d88282595b969f7", "os": "linux", "cpu": "arm64" }, { "kind": "archive", - "url": "https://github.com/astral-sh/ruff/releases/download/0.12.3/ruff-x86_64-unknown-linux-musl.tar.gz", + "url": "https://github.com/astral-sh/ruff/releases/download/0.12.10/ruff-x86_64-unknown-linux-musl.tar.gz", "file": "ruff-x86_64-unknown-linux-musl/ruff", - "sha256": "6ee9216ba4f7fd761e68bd5c23958e662c67fcff8f49e511660d557431b4bd7c", + "sha256": "dd4e5b8f81547a48975489913a80e0dce6be7f1c455912fe3a5bd5a1f5f1a35d", "os": "linux", "cpu": "x86_64" }, { "kind": "archive", - "url": "https://github.com/astral-sh/ruff/releases/download/0.12.3/ruff-aarch64-apple-darwin.tar.gz", + "url": "https://github.com/astral-sh/ruff/releases/download/0.12.10/ruff-aarch64-apple-darwin.tar.gz", "file": "ruff-aarch64-apple-darwin/ruff", - "sha256": "5769e4841870d1f7c17f12a7d1437222e8035eded52f93c54c035c770dbffebb", + "sha256": "72c6abf39f5e87c57faa2d191baf2582e437ff72cdc0f52b7c7e50f26d41b807", "os": "macos", "cpu": "arm64" }, { "kind": "archive", - "url": "https://github.com/astral-sh/ruff/releases/download/0.12.3/ruff-x86_64-apple-darwin.tar.gz", + "url": "https://github.com/astral-sh/ruff/releases/download/0.12.10/ruff-x86_64-apple-darwin.tar.gz", "file": "ruff-x86_64-apple-darwin/ruff", - "sha256": "472a4790db11a8bcd79cf7b731fb1c036c376f9cc4e6532099f8f39a6f86b9bb", + "sha256": "8619f277921b3e2e56d850c3e203fd4ef10b457bc50f93ab6fe85743eb324de6", "os": "macos", "cpu": "x86_64" }, { "kind": "archive", - "url": "https://github.com/astral-sh/ruff/releases/download/0.12.3/ruff-x86_64-pc-windows-msvc.zip", + "url": "https://github.com/astral-sh/ruff/releases/download/0.12.10/ruff-x86_64-pc-windows-msvc.zip", "file": "ruff-x86_64-pc-windows-msvc/ruff.exe", - "sha256": "37dc6f2f5756421fc59fe5f841ecaa92beee38a39751dfe5a42bdc13992da3d5", + "sha256": "a639e4dee10cb2900bffa7165457766671c59c744ce6b61cc658c35ab33a91fd", "os": "windows", "cpu": "x86_64" } diff --git a/py/tox.ini b/py/tox.ini index 36a90466bb3e1..086df0a2f56a5 100644 --- a/py/tox.ini +++ b/py/tox.ini @@ -43,7 +43,7 @@ commands = [testenv:linting] skip_install = true deps = - ruff==0.12.3 + ruff==0.12.10 commands = ruff check --fix --show-fixes --exit-non-zero-on-fix . ruff format --exit-non-zero-on-format . From 92a56647f2758ff8e70b3ba1820fac7c7b884f75 Mon Sep 17 00:00:00 2001 From: Viktoriia Ivanova Date: Fri, 22 Aug 2025 16:12:03 -0700 Subject: [PATCH 6/6] [java] Refactored selectByContainsVisibleText and selectByVisibleText methods to remove code duplication --- .../openqa/selenium/support/ui/Select.java | 165 ++++++++---------- 1 file changed, 69 insertions(+), 96 deletions(-) diff --git a/java/src/org/openqa/selenium/support/ui/Select.java b/java/src/org/openqa/selenium/support/ui/Select.java index 9942b7057832e..23c3bc8a72654 100644 --- a/java/src/org/openqa/selenium/support/ui/Select.java +++ b/java/src/org/openqa/selenium/support/ui/Select.java @@ -122,55 +122,7 @@ public WebElement getFirstSelectedOption() { */ @Override public void selectByVisibleText(String text) { - assertSelectIsEnabled(); - assertSelectIsVisible(); - - // try to find the option via XPATH ... - List options = - element.findElements( - By.xpath(".//option[normalize-space(.) = " + Quotes.escape(text) + "]")); - - for (WebElement option : options) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - } - - boolean matched = !options.isEmpty(); - if (!matched && text.contains(" ")) { - String subStringWithoutSpace = getLongestSubstringWithoutSpace(text); - List candidates; - if ("".equals(subStringWithoutSpace)) { - // hmm, text is either empty or contains only spaces - get all options ... - candidates = element.findElements(By.tagName("option")); - } else { - // get candidates via XPATH ... - candidates = - element.findElements( - By.xpath(".//option[contains(., " + Quotes.escape(subStringWithoutSpace) + ")]")); - } - - String trimmed = text.trim(); - - for (WebElement option : candidates) { - if (trimmed.equals(option.getText().trim())) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - matched = true; - } - } - } - - if (!matched) { - throw new NoSuchElementException("Cannot locate option with text: " + text); - } + selectByVisibleText(text, false); } /** @@ -193,53 +145,7 @@ public void selectByVisibleText(String text) { */ @Override public void selectByContainsVisibleText(String text) { - assertSelectIsEnabled(); - assertSelectIsVisible(); - - // try to find the option via XPATH ... - List options = - element.findElements( - By.xpath(".//option[normalize-space(.) = " + Quotes.escape(text) + "]")); - - for (WebElement option : options) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - } - - boolean matched = !options.isEmpty(); - if (!matched) { - String searchText = text.contains(" ") ? getLongestSubstringWithoutSpace(text) : text; - - List candidates; - if (searchText.isEmpty()) { - candidates = element.findElements(By.tagName("option")); - } else { - candidates = - element.findElements( - By.xpath(".//option[contains(., " + Quotes.escape(searchText) + ")]")); - } - - String trimmed = text.trim(); - for (WebElement option : candidates) { - if (option.getText().contains(trimmed)) { - if (!hasCssPropertyAndVisible(option)) - throw new NoSuchElementException("Invisible option with text: " + text); - setSelected(option, true); - if (!isMultiple()) { - return; - } - matched = true; - } - } - } - - if (!matched) { - throw new NoSuchElementException("Cannot locate option with text: " + text); - } + selectByVisibleText(text, true); } private String getLongestSubstringWithoutSpace(String s) { @@ -425,6 +331,73 @@ private void setSelected(WebElement option, boolean select) { } } + /** + * @param text The visible text to match against. It can be a partial match of the option if + * isPartialMatch = true + * @param isPartialMatch If true a partial match on the Options list will be performed, otherwise + * exact match + * @throws NoSuchElementException If no matching option elements are found or matching options are + * hidden + */ + private void selectByVisibleText(String text, boolean isPartialMatch) { + assertSelectIsEnabled(); + assertSelectIsVisible(); + + // try to find the option via XPATH ... + List options = + element.findElements( + By.xpath(".//option[normalize-space(.) = " + Quotes.escape(text) + "]")); + + for (WebElement option : options) { + if (!hasCssPropertyAndVisible(option)) + throw new NoSuchElementException("Invisible option with text: " + text); + + setSelected(option, true); + + if (!isMultiple()) { + return; + } + } + + boolean matched = !options.isEmpty(); + if (!matched) { + String searchText = text.contains(" ") ? getLongestSubstringWithoutSpace(text) : text; + + List candidates; + if (searchText.isEmpty()) { + candidates = element.findElements(By.tagName("option")); + } else { + candidates = + element.findElements( + By.xpath(".//option[contains(., " + Quotes.escape(searchText) + ")]")); + } + + String trimmed = text.trim(); + for (WebElement option : candidates) { + boolean isMatchedOptionFound = + isPartialMatch + ? option.getText().contains(trimmed) + : option.getText().trim().equals(trimmed); + + if (isMatchedOptionFound) { + if (!hasCssPropertyAndVisible(option)) + throw new NoSuchElementException("Invisible option with text: " + text); + + setSelected(option, true); + + if (!isMultiple()) { + return; + } + matched = true; + } + } + } + + if (!matched) { + throw new NoSuchElementException("Cannot locate option with text: " + text); + } + } + private void assertOptionIsEnabled(WebElement option, boolean select) { if (select && !option.isEnabled()) { throw new UnsupportedOperationException("You may not select a disabled option");