From 2834058d0d1450e9346717da70cbecf2dbf4a96c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 12 Sep 2026 22:34:37 +0000 Subject: [PATCH 1/2] ci: only run the release workflow on the upstream repository The Release workflow triggers on every push to master. On a fork that means semantic-release rewrites the version in pyproject.toml, commits the change to the default branch, tags it and then tries to publish the result to PyPI under the upstream package name. Gate the job on the repository name so forks keep a clean master. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PjZRpyoEfSCLjTx5ctzQmJ --- .github/workflows/publish.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8df3ca7f..2119bfad 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,6 +11,10 @@ permissions: jobs: release: + # Releases are cut from the upstream repository only. Without this guard the + # workflow also runs on forks, where semantic-release rewrites the version in + # pyproject.toml, commits to the default branch and attempts a PyPI upload. + if: github.repository == 'appium/python-client' runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 From d5465a5fac2e66408a0a6e84d8dd570508ca07e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 06:01:51 +0000 Subject: [PATCH 2/2] test: verify driver sends By.ID/NAME/CLASS_NAME locators unconverted Selenium's default LocatorConverter rewrites these strategies to 'css selector'. Assert at the driver level that AppiumWebDriver (and child element lookups) keep them as-is, so removing the AppiumLocatorConverter wiring is caught. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012HCUdSWNJMojXCjBoxhg41 --- test/unit/webdriver/webdriver_test.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/unit/webdriver/webdriver_test.py b/test/unit/webdriver/webdriver_test.py index 73966a24..575a99e6 100644 --- a/test/unit/webdriver/webdriver_test.py +++ b/test/unit/webdriver/webdriver_test.py @@ -16,7 +16,9 @@ from unittest.mock import patch import httpretty +import pytest import urllib3 +from selenium.webdriver.common.by import By from appium import webdriver from appium.options.android import UiAutomator2Options @@ -292,6 +294,30 @@ def test_get_events_catches_missing_events(self): events = driver.events assert events == {} + @httpretty.activate + @pytest.mark.parametrize('by', [By.ID, By.NAME, By.CLASS_NAME]) + def test_find_element_keeps_locator_strategy(self, by): + # Selenium's default LocatorConverter rewrites these strategies to 'css selector', + # which Appium native contexts do not support. + driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element'), + body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "element-id"}}', + ) + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element/element-id/element'), + body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "child-element-id"}}', + ) + el = driver.find_element(by=by, value='foo') + d = get_httpretty_request_body(httpretty.last_request()) + assert d == {'using': by, 'value': 'foo'} + + el.find_element(by=by, value='bar') + d = get_httpretty_request_body(httpretty.last_request()) + assert d == {'using': by, 'value': 'bar'} + @httpretty.activate @patch('appium.webdriver.webdriver.logger.warning') def test_session_catches_error(self, mock_warning):