Skip to content

Commit 2f29f66

Browse files
Fix #736 legacy ChromeDriver downloads for Chrome 114 and older (#737)
* fix(chrome) №736: restore legacy chromedriver downloads for pre-115 * fix(firefox): report missing GeckoDriver release asset * ci: install Google Chrome only when missing on Windows * ci: install Chromium on linux by actions * changelog update
1 parent c327937 commit 2f29f66

12 files changed

Lines changed: 240 additions & 22 deletions

File tree

.github/workflows/test.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ jobs:
7474
with:
7575
python-version: ${{ matrix.python-version }}
7676

77+
- name: Install Chromium on Linux
78+
if: runner.os == 'Linux'
79+
id: setup-chromium
80+
uses: browser-actions/setup-chrome@v2
81+
with:
82+
# "latest" is resolved from Chromium Snapshots, not stable Google Chrome.
83+
chrome-version: latest
84+
install-dependencies: true
85+
7786
- name: Install browsers on Linux
7887
if: runner.os == 'Linux'
7988
run: |
@@ -92,7 +101,8 @@ jobs:
92101
sudo apt-get update
93102
sudo apt-get -y --no-install-recommends install opera-stable
94103
95-
sudo apt-get install chromium-browser
104+
sudo ln -sf "${{ steps.setup-chromium.outputs.chrome-path }}" /usr/local/bin/chromium
105+
chromium --version
96106
97107
sudo curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
98108
echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg] https://brave-browser-apt-release.s3.brave.com/ stable main"|sudo tee /etc/apt/sources.list.d/brave-browser-release.list
@@ -112,7 +122,17 @@ jobs:
112122
if: runner.os == 'Windows'
113123
shell: powershell
114124
run: |
115-
choco install chromium opera brave googlechrome --no-progress -y --force
125+
choco install chromium opera brave --no-progress -y --force
126+
127+
$chromePaths = @(
128+
"$env:PROGRAMFILES\Google\Chrome\Application\chrome.exe",
129+
"${env:PROGRAMFILES(X86)}\Google\Chrome\Application\chrome.exe",
130+
"$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe"
131+
)
132+
$chromePath = $chromePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
133+
if (-not $chromePath) {
134+
choco install googlechrome --no-progress -y --force
135+
}
116136
117137
- name: Install browsers on MacOS
118138
if: startsWith(runner.os, 'macOS')

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
---
44

5+
## 4.1.2
6+
7+
### Fixes
8+
9+
- ChromeDriver: restored legacy ChromeDriver storage URL handling for Chrome/ChromeDriver 114 and older, fixing invalid Chrome for Testing download URLs for older versions such as ChromeDriver `102.0.5005.61` on Windows. (#736)
10+
- ChromeDriver on 64-bit Windows: use the legacy `win32` archive name for ChromeDriver 114 and older while preserving `win64` Chrome for Testing downloads for ChromeDriver 115 and newer. (#736)
11+
- Firefox/GeckoDriver: report a readable error when a GeckoDriver release does not contain an asset matching the requested OS type instead of failing with an ambiguous missing-list entry.
12+
13+
### Tests
14+
15+
- Added regression coverage for ChromeDriver `102.0.5005.61` URL construction, legacy latest-release lookup, and the `ChromeDriverManager.install()` download-manager path without live network calls. (#736)
16+
- Pinned GeckoDriver cache coverage to a known release and added coverage for missing GeckoDriver release assets.
17+
18+
### CI
19+
20+
- Windows: install Google Chrome only when it is missing, while continuing to install Chromium, Opera, and Brave through Chocolatey.
21+
- Linux: install Chromium through `browser-actions/setup-chrome` and expose it as `chromium`, avoiding the unavailable/unstable `apt` Chromium package path on GitHub Actions.
22+
23+
---
24+
525
## 4.1.1
626

727
### Packaging

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "webdriver-manager"
7-
version = "4.1.1"
7+
version = "4.1.2"
88
description = "Library provides the way to automatically manage drivers for different browsers"
99
readme = "README.md"
1010
requires-python = ">=3.7"

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 4.1.1
2+
current_version = 4.1.2
33
commit = True
44
tag = True
55

tests/helper.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import json
22

33
from webdriver_manager.core.os_manager import ChromeType
4-
from webdriver_manager.drivers.chrome import ChromeDriver
4+
from webdriver_manager.drivers.chrome import (
5+
CHROME_FOR_TESTING_DOWNLOAD_URL,
6+
CHROME_FOR_TESTING_LATEST_RELEASE_URL,
7+
ChromeDriver,
8+
)
59

610

711
class ResponseMock:
@@ -33,15 +37,20 @@ def get_browser_version_from_os(self, browser_type=None):
3337
return self.browser_version
3438

3539

36-
def chrome_driver_for(browser_version, responses, chrome_type=ChromeType.CHROMIUM):
40+
def chrome_driver_for(
41+
browser_version,
42+
responses,
43+
chrome_type=ChromeType.CHROMIUM,
44+
driver_version=None,
45+
url=CHROME_FOR_TESTING_DOWNLOAD_URL,
46+
latest_release_url=CHROME_FOR_TESTING_LATEST_RELEASE_URL,
47+
):
3748
http_client = HttpClientMock(responses)
3849
driver = ChromeDriver(
3950
name="chromedriver",
40-
driver_version=None,
41-
url="https://storage.googleapis.com/chrome-for-testing-public/",
42-
latest_release_url=(
43-
"https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_STABLE"
44-
),
51+
driver_version=driver_version,
52+
url=url,
53+
latest_release_url=latest_release_url,
4554
http_client=http_client,
4655
os_system_manager=OperationSystemManagerMock(browser_version),
4756
chrome_type=chrome_type,

tests/test_chrome_driver.py

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414

1515
from webdriver_manager.core.driver_cache import DriverCacheManager
1616
from webdriver_manager.core.os_manager import OperationSystemManager, ChromeType
17-
from webdriver_manager.drivers.chrome import CHROME_FOR_TESTING_LATEST_PATCH_VERSIONS_PER_BUILD_URL, \
18-
CHROME_FOR_TESTING_KNOWN_GOOD_VERSIONS_URL
17+
from webdriver_manager.drivers.chrome import (
18+
CHROMEDRIVER_STORAGE_LATEST_RELEASE_URL,
19+
CHROME_FOR_TESTING_LATEST_PATCH_VERSIONS_PER_BUILD_URL,
20+
CHROME_FOR_TESTING_KNOWN_GOOD_VERSIONS_URL,
21+
)
1922

2023
os.environ.setdefault("WDM_LOCAL", "false")
2124

@@ -156,6 +159,130 @@ def test_chrome_118_resolves_cft_driver_version_and_download_url():
156159
assert CHROME_FOR_TESTING_KNOWN_GOOD_VERSIONS_URL in http_client.requested_urls
157160

158161

162+
def test_chrome_102_uses_legacy_storage_url_and_win32_archive_for_win64():
163+
driver, http_client = chrome_driver_for(
164+
browser_version="102.0.5005.63",
165+
driver_version="102.0.5005.61",
166+
chrome_type=ChromeType.GOOGLE,
167+
responses={},
168+
)
169+
170+
assert driver.get_driver_download_url("win64") == (
171+
"https://chromedriver.storage.googleapis.com/"
172+
"102.0.5005.61/chromedriver_win32.zip"
173+
)
174+
assert http_client.requested_urls == []
175+
176+
177+
def test_chrome_102_detected_version_uses_legacy_latest_release_url():
178+
expected_url = f"{CHROMEDRIVER_STORAGE_LATEST_RELEASE_URL}_102.0.5005"
179+
driver, http_client = chrome_driver_for(
180+
browser_version="102.0.5005.63",
181+
chrome_type=ChromeType.GOOGLE,
182+
responses={
183+
expected_url: "102.0.5005.61",
184+
},
185+
)
186+
187+
assert driver.get_latest_release_version() == "102.0.5005.61"
188+
assert http_client.requested_urls == [expected_url]
189+
190+
191+
def test_chrome_download_url_boundary_switches_from_legacy_to_cft():
192+
legacy_driver, legacy_http_client = chrome_driver_for(
193+
browser_version="114.0.5735.199",
194+
driver_version="114.0.5735.90",
195+
chrome_type=ChromeType.GOOGLE,
196+
responses={},
197+
)
198+
cft_url = (
199+
"https://storage.googleapis.com/chrome-for-testing-public/"
200+
"115.0.5790.170/win64/chromedriver-win64.zip"
201+
)
202+
cft_driver, cft_http_client = chrome_driver_for(
203+
browser_version="115.0.5790.99",
204+
driver_version="115.0.5790.170",
205+
chrome_type=ChromeType.GOOGLE,
206+
responses={
207+
CHROME_FOR_TESTING_KNOWN_GOOD_VERSIONS_URL: {
208+
"versions": [
209+
{
210+
"version": "115.0.5790.170",
211+
"downloads": {
212+
"chromedriver": [
213+
{"platform": "win64", "url": cft_url},
214+
],
215+
},
216+
},
217+
],
218+
},
219+
},
220+
)
221+
222+
assert legacy_driver.get_driver_download_url("win64") == (
223+
"https://chromedriver.storage.googleapis.com/"
224+
"114.0.5735.90/chromedriver_win32.zip"
225+
)
226+
assert legacy_http_client.requested_urls == []
227+
assert cft_driver.get_driver_download_url("win64") == cft_url
228+
assert cft_http_client.requested_urls == [
229+
CHROME_FOR_TESTING_KNOWN_GOOD_VERSIONS_URL,
230+
]
231+
232+
233+
def test_chrome_manager_downloads_legacy_chrome_102_url_for_win64(tmp_path):
234+
class CacheManagerMock:
235+
def find_driver(self, _driver):
236+
return None
237+
238+
def get_driver_lock_path(self, _driver_name, _os_type):
239+
return str(tmp_path / ".wdm-lock")
240+
241+
def save_file_to_cache(self, _driver, _file):
242+
driver_path = tmp_path / "chromedriver.exe"
243+
driver_path.write_text("")
244+
return str(driver_path)
245+
246+
class DownloadManagerMock:
247+
http_client = None
248+
249+
def __init__(self):
250+
self.requested_urls = []
251+
252+
def download_file(self, url):
253+
self.requested_urls.append(url)
254+
return object()
255+
256+
class Windows64OSManagerMock:
257+
def get_os_type(self):
258+
return "win64"
259+
260+
def get_os_architecture(self):
261+
return 64
262+
263+
def is_mac_os(self, _os_type):
264+
return False
265+
266+
def get_browser_version_from_os(self, _browser_type=None):
267+
return "102.0.5005.63"
268+
269+
download_manager = DownloadManagerMock()
270+
manager = ChromeDriverManager(
271+
driver_version="102.0.5005.61",
272+
download_manager=download_manager,
273+
cache_manager=CacheManagerMock(),
274+
os_system_manager=Windows64OSManagerMock(),
275+
)
276+
277+
driver_path = manager.install()
278+
279+
assert os.path.exists(driver_path)
280+
assert download_manager.requested_urls == [
281+
"https://chromedriver.storage.googleapis.com/"
282+
"102.0.5005.61/chromedriver_win32.zip"
283+
]
284+
285+
159286
def test_chrome_115_plus_prefers_win64_download_when_available():
160287
expected_url = (
161288
"https://storage.googleapis.com/chrome-for-testing-public/"

tests/test_firefox_manager.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ def _cache_os_types_for_current_platform():
7676
@pytest.mark.parametrize('os_type', _cache_os_types_for_current_platform())
7777
@requires_gh_token
7878
def test_can_get_driver_from_cache(os_type):
79-
GeckoDriverManager(os_system_manager=OperationSystemManager(os_type)).install()
80-
driver_path = GeckoDriverManager(os_system_manager=OperationSystemManager(os_type)).install()
79+
driver_version = "v0.36.0"
80+
GeckoDriverManager(
81+
version=driver_version,
82+
os_system_manager=OperationSystemManager(os_type),
83+
).install()
84+
driver_path = GeckoDriverManager(
85+
version=driver_version,
86+
os_system_manager=OperationSystemManager(os_type),
87+
).install()
8188
assert os.path.exists(driver_path)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webdriver_manager/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "4.1.1"
1+
__version__ = "4.1.2"

webdriver_manager/chrome.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55
from webdriver_manager.core.driver_cache import DriverCacheManager
66
from webdriver_manager.core.manager import DriverManager
77
from webdriver_manager.core.os_manager import OperationSystemManager, ChromeType
8-
from webdriver_manager.drivers.chrome import ChromeDriver
8+
from webdriver_manager.drivers.chrome import (
9+
CHROME_FOR_TESTING_DOWNLOAD_URL,
10+
CHROME_FOR_TESTING_LATEST_RELEASE_URL,
11+
ChromeDriver,
12+
)
913

1014

1115
class ChromeDriverManager(DriverManager):
1216
def __init__(
1317
self,
1418
driver_version: Optional[str] = None,
1519
name: str = "chromedriver",
16-
url: str = "https://storage.googleapis.com/chrome-for-testing-public/",
17-
latest_release_url: str = "https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_STABLE",
20+
url: str = CHROME_FOR_TESTING_DOWNLOAD_URL,
21+
latest_release_url: str = CHROME_FOR_TESTING_LATEST_RELEASE_URL,
1822
chrome_type: str = ChromeType.GOOGLE,
1923
download_manager: Optional[DownloadManager] = None,
2024
cache_manager: Optional[DriverCacheManager] = None,

0 commit comments

Comments
 (0)