mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/WECENG/ticket-purchase.git
synced 2026-09-20 08:04:02 +08:00
Merge pull request #93 from chris22333-dot/fix/windows-chrome-driver
fix: support ChromeDriver setup on Windows
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -162,6 +162,7 @@ chromedriver.log
|
||||
config.json
|
||||
credentials.json
|
||||
*.cookie
|
||||
*.pkl
|
||||
*.session
|
||||
|
||||
# Temporary files
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
@@ -23,16 +24,147 @@ def _run_command_get_version(command):
|
||||
result = subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
errors="replace",
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return result.stdout.strip()
|
||||
return (result.stdout or result.stderr).strip()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _unique_paths(paths):
|
||||
"""保持顺序去重,并忽略空路径。"""
|
||||
unique = []
|
||||
seen = set()
|
||||
for path in paths:
|
||||
if not path or path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
unique.append(path)
|
||||
return unique
|
||||
|
||||
|
||||
def _get_chrome_paths():
|
||||
"""返回常见平台上的 Chrome 可执行文件候选路径。"""
|
||||
program_files = os.environ.get("PROGRAMFILES", r"C:\Program Files")
|
||||
program_files_x86 = os.environ.get("PROGRAMFILES(X86)", r"C:\Program Files (x86)")
|
||||
local_appdata = os.environ.get("LOCALAPPDATA")
|
||||
|
||||
paths = [
|
||||
os.environ.get("CHROME_PATH"),
|
||||
os.environ.get("GOOGLE_CHROME_BIN"),
|
||||
os.path.join(program_files, "Google", "Chrome", "Application", "chrome.exe"),
|
||||
os.path.join(program_files_x86, "Google", "Chrome", "Application", "chrome.exe"),
|
||||
os.path.join(local_appdata, "Google", "Chrome", "Application", "chrome.exe") if local_appdata else None,
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/google-chrome-stable",
|
||||
"/usr/bin/chromium",
|
||||
"/usr/bin/chromium-browser",
|
||||
]
|
||||
|
||||
for command in [
|
||||
"chrome.exe",
|
||||
"chrome",
|
||||
"google-chrome",
|
||||
"google-chrome-stable",
|
||||
"chromium",
|
||||
"chromium-browser",
|
||||
]:
|
||||
paths.append(shutil.which(command))
|
||||
|
||||
return _unique_paths(paths)
|
||||
|
||||
|
||||
def _get_chromedriver_paths():
|
||||
"""返回常见平台上的 ChromeDriver 候选路径。"""
|
||||
paths = [
|
||||
os.environ.get("CHROMEDRIVER_PATH"),
|
||||
os.environ.get("SELENIUM_DRIVER_PATH"),
|
||||
shutil.which("chromedriver.exe"),
|
||||
shutil.which("chromedriver"),
|
||||
os.path.join(os.getcwd(), "chromedriver.exe"),
|
||||
os.path.join(os.getcwd(), "chromedriver"),
|
||||
"/opt/homebrew/bin/chromedriver",
|
||||
"/usr/local/bin/chromedriver",
|
||||
"/opt/homebrew/Caskroom/chromedriver",
|
||||
]
|
||||
|
||||
try:
|
||||
import chromedriver_autoinstaller
|
||||
package_dir = os.path.dirname(chromedriver_autoinstaller.__file__)
|
||||
for name in os.listdir(package_dir):
|
||||
driver_dir = os.path.join(package_dir, name)
|
||||
if os.path.isdir(driver_dir):
|
||||
paths.append(os.path.join(driver_dir, "chromedriver.exe"))
|
||||
paths.append(os.path.join(driver_dir, "chromedriver"))
|
||||
except (AttributeError, ImportError, OSError):
|
||||
pass
|
||||
|
||||
return _unique_paths(paths)
|
||||
|
||||
|
||||
def _version_sort_key(version):
|
||||
return tuple(int(part) for part in version.split("."))
|
||||
|
||||
|
||||
def _get_windows_chrome_version(chrome_path):
|
||||
"""Windows 上 chrome.exe --version 可能被已有会话吞掉,改从安装目录读取版本。"""
|
||||
app_dir = os.path.dirname(chrome_path)
|
||||
try:
|
||||
version_dirs = [
|
||||
name for name in os.listdir(app_dir)
|
||||
if re.fullmatch(r"\d+(?:\.\d+){1,3}", name)
|
||||
and os.path.isdir(os.path.join(app_dir, name))
|
||||
]
|
||||
except OSError:
|
||||
version_dirs = []
|
||||
|
||||
if version_dirs:
|
||||
return sorted(version_dirs, key=_version_sort_key)[-1]
|
||||
|
||||
try:
|
||||
import winreg
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
registry_locations = [
|
||||
(winreg.HKEY_CURRENT_USER, r"Software\Google\Chrome\BLBeacon"),
|
||||
(winreg.HKEY_LOCAL_MACHINE, r"Software\Google\Chrome\BLBeacon"),
|
||||
(winreg.HKEY_LOCAL_MACHINE, r"Software\WOW6432Node\Google\Chrome\BLBeacon"),
|
||||
]
|
||||
for hive, key_path in registry_locations:
|
||||
try:
|
||||
with winreg.OpenKey(hive, key_path) as key:
|
||||
version, _ = winreg.QueryValueEx(key, "version")
|
||||
if version:
|
||||
return version
|
||||
except OSError:
|
||||
continue
|
||||
return None
|
||||
|
||||
|
||||
def _find_chrome():
|
||||
"""查找 Chrome,返回 (path, version_str, major_version)。"""
|
||||
for chrome_path in _get_chrome_paths():
|
||||
if os.path.exists(chrome_path):
|
||||
version_str = _run_command_get_version([chrome_path, "--version"])
|
||||
if version_str:
|
||||
chrome_version = _get_version_from_output(version_str)
|
||||
if chrome_version:
|
||||
return chrome_path, version_str, chrome_version
|
||||
|
||||
windows_version = _get_windows_chrome_version(chrome_path)
|
||||
if windows_version:
|
||||
return chrome_path, f"Google Chrome {windows_version}", _get_version_from_output(windows_version)
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
def check_python_version():
|
||||
"""检查 Python 版本"""
|
||||
print("Python 版本检查...")
|
||||
@@ -73,23 +205,13 @@ def check_dependencies():
|
||||
def check_chrome():
|
||||
"""检查 Chrome 浏览器"""
|
||||
print("Chrome 浏览器检查...")
|
||||
chrome_paths = [
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/chromium-browser",
|
||||
]
|
||||
|
||||
for chrome_path in chrome_paths:
|
||||
if os.path.exists(chrome_path):
|
||||
version_str = _run_command_get_version([chrome_path, "--version"])
|
||||
if version_str:
|
||||
chrome_version = _get_version_from_output(version_str)
|
||||
if chrome_version:
|
||||
print(f" ✓ Chrome 浏览器: {version_str}")
|
||||
print(f" ✓ 主版本号: {chrome_version}")
|
||||
print()
|
||||
return True
|
||||
chrome_path, version_str, chrome_version = _find_chrome()
|
||||
if chrome_path:
|
||||
print(f" ✓ Chrome 浏览器: {version_str}")
|
||||
print(f" ✓ 主版本号: {chrome_version}")
|
||||
print(f" ✓ 路径: {chrome_path}")
|
||||
print()
|
||||
return True
|
||||
|
||||
print(" ✗ 未找到 Chrome 浏览器")
|
||||
print(" 请安装 Chrome: https://www.google.com/chrome/")
|
||||
@@ -100,13 +222,7 @@ def check_chrome():
|
||||
def check_chromedriver():
|
||||
"""检查 ChromeDriver"""
|
||||
print("ChromeDriver 检查...")
|
||||
chromedriver_paths = [
|
||||
"/opt/homebrew/bin/chromedriver",
|
||||
"/usr/local/bin/chromedriver",
|
||||
"/opt/homebrew/Caskroom/chromedriver",
|
||||
]
|
||||
|
||||
for driver_path in chromedriver_paths:
|
||||
for driver_path in _get_chromedriver_paths():
|
||||
if os.path.exists(driver_path) or os.path.islink(driver_path):
|
||||
version_str = _run_command_get_version([driver_path, "--version"])
|
||||
if version_str:
|
||||
@@ -121,7 +237,8 @@ def check_chromedriver():
|
||||
print(" ⚠ 未找到 ChromeDriver")
|
||||
print(" 安装方法:")
|
||||
print(" - macOS: brew install --cask chromedriver")
|
||||
print(" - 或使用脚本自动安装: chromedriver-autoinstaller")
|
||||
print(" - Windows/macOS/Linux: 使用脚本自动安装 chromedriver-autoinstaller")
|
||||
print(" - 或手动下载: https://googlechromelabs.github.io/chrome-for-testing/")
|
||||
print()
|
||||
return False
|
||||
|
||||
@@ -130,19 +247,15 @@ def check_version_match():
|
||||
"""检查 Chrome 和 ChromeDriver 版本是否匹配"""
|
||||
print("版本匹配检查...")
|
||||
|
||||
chrome_version = None
|
||||
chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
if os.path.exists(chrome_path):
|
||||
version_str = _run_command_get_version([chrome_path, "--version"])
|
||||
if version_str:
|
||||
chrome_version = _get_version_from_output(version_str)
|
||||
_, _, chrome_version = _find_chrome()
|
||||
|
||||
driver_version = None
|
||||
driver_path = "/opt/homebrew/bin/chromedriver"
|
||||
if os.path.exists(driver_path) or os.path.islink(driver_path):
|
||||
version_str = _run_command_get_version([driver_path, "--version"])
|
||||
if version_str:
|
||||
driver_version = _get_version_from_output(version_str)
|
||||
for driver_path in _get_chromedriver_paths():
|
||||
if os.path.exists(driver_path) or os.path.islink(driver_path):
|
||||
version_str = _run_command_get_version([driver_path, "--version"])
|
||||
if version_str:
|
||||
driver_version = _get_version_from_output(version_str)
|
||||
break
|
||||
|
||||
if not chrome_version or not driver_version:
|
||||
print(" ⚠ 无法获取版本信息")
|
||||
@@ -171,45 +284,30 @@ def check_version_match():
|
||||
|
||||
def get_chromedriver_path():
|
||||
"""
|
||||
获取 ChromeDriver 路径,如果不存在则自动安装
|
||||
供其他脚本导入使用
|
||||
获取 ChromeDriver 路径,如果不存在则自动安装。
|
||||
供其他脚本导入使用。
|
||||
:return: ChromeDriver 可执行文件路径
|
||||
"""
|
||||
chrome_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
_, _, chrome_version = _find_chrome()
|
||||
|
||||
if not os.path.exists(chrome_path):
|
||||
raise RuntimeError("未找到 Chrome 浏览器,请先安装 Chrome")
|
||||
if chrome_version:
|
||||
for driver_path in _get_chromedriver_paths():
|
||||
if os.path.exists(driver_path) or os.path.islink(driver_path):
|
||||
driver_version_str = _run_command_get_version([driver_path, "--version"])
|
||||
if driver_version_str:
|
||||
driver_version = _get_version_from_output(driver_version_str)
|
||||
if driver_version == chrome_version:
|
||||
return driver_path
|
||||
|
||||
# 获取 Chrome 版本
|
||||
chrome_version_str = _run_command_get_version([chrome_path, "--version"])
|
||||
chrome_version = _get_version_from_output(chrome_version_str) if chrome_version_str else None
|
||||
print(f" Chrome 版本: {chrome_version}")
|
||||
else:
|
||||
print(" ⚠ 未通过常见路径检测到 Chrome,尝试使用 chromedriver-autoinstaller 自动检测...")
|
||||
|
||||
if not chrome_version:
|
||||
raise RuntimeError("无法获取 Chrome 版本")
|
||||
|
||||
# 检查已安装的 ChromeDriver 是否匹配
|
||||
chromedriver_paths = [
|
||||
"/opt/homebrew/bin/chromedriver",
|
||||
"/usr/local/bin/chromedriver",
|
||||
]
|
||||
|
||||
for driver_path in chromedriver_paths:
|
||||
if os.path.exists(driver_path) or os.path.islink(driver_path):
|
||||
driver_version_str = _run_command_get_version([driver_path, "--version"])
|
||||
if driver_version_str:
|
||||
driver_version = _get_version_from_output(driver_version_str)
|
||||
if driver_version == chrome_version:
|
||||
# 版本匹配,直接使用
|
||||
return driver_path
|
||||
|
||||
# 版本不匹配或不存在,使用自动安装器
|
||||
print(f" Chrome 版本: {chrome_version}")
|
||||
print(" 正在自动安装匹配的 ChromeDriver...")
|
||||
try:
|
||||
import chromedriver_autoinstaller
|
||||
chromedriver_path = chromedriver_autoinstaller.install()
|
||||
|
||||
# 验证安装的版本
|
||||
result = _run_command_get_version([chromedriver_path, "--version"])
|
||||
if not result:
|
||||
raise RuntimeError("ChromeDriver 无法执行")
|
||||
|
||||
@@ -64,9 +64,7 @@ def mock_appium_driver():
|
||||
mock_driver.swipe = Mock()
|
||||
mock_driver.quit = Mock()
|
||||
|
||||
# Mock the Remote class
|
||||
with patch.object(mock_driver, "__class__.__name__", "Remote"):
|
||||
yield mock_driver
|
||||
yield mock_driver
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
96
tests/unit/test_check_environment_windows.py
Normal file
96
tests/unit/test_check_environment_windows.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
from damai import check_environment
|
||||
|
||||
|
||||
WINDOWS_CHROME = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
|
||||
WINDOWS_CHROMEDRIVER = r"C:\Tools\chromedriver.exe"
|
||||
|
||||
|
||||
def test_check_chrome_detects_windows_program_files_install(monkeypatch):
|
||||
"""Chrome installed in Program Files should be detected on Windows."""
|
||||
|
||||
def fake_exists(path):
|
||||
return path == WINDOWS_CHROME
|
||||
|
||||
def fake_version(command):
|
||||
if command[0] == WINDOWS_CHROME:
|
||||
return "Google Chrome 136.0.7103.114"
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(check_environment.os.path, "exists", fake_exists)
|
||||
monkeypatch.setattr(check_environment, "_run_command_get_version", fake_version)
|
||||
|
||||
assert check_environment.check_chrome() is True
|
||||
|
||||
|
||||
def test_check_version_match_uses_windows_chrome_and_driver_paths(monkeypatch):
|
||||
"""Version matching should work for Windows Chrome and chromedriver.exe."""
|
||||
|
||||
def fake_exists(path):
|
||||
return path in {WINDOWS_CHROME, WINDOWS_CHROMEDRIVER}
|
||||
|
||||
def fake_islink(path):
|
||||
return False
|
||||
|
||||
def fake_version(command):
|
||||
if command[0] == WINDOWS_CHROME:
|
||||
return "Google Chrome 136.0.7103.114"
|
||||
if command[0] == WINDOWS_CHROMEDRIVER:
|
||||
return "ChromeDriver 136.0.7103.114"
|
||||
return None
|
||||
|
||||
monkeypatch.setenv("CHROMEDRIVER_PATH", WINDOWS_CHROMEDRIVER)
|
||||
monkeypatch.setattr(check_environment.os.path, "exists", fake_exists)
|
||||
monkeypatch.setattr(check_environment.os.path, "islink", fake_islink)
|
||||
monkeypatch.setattr(check_environment, "_run_command_get_version", fake_version)
|
||||
|
||||
assert check_environment.check_version_match() is True
|
||||
|
||||
|
||||
def test_find_chrome_reads_windows_install_version_directory(monkeypatch, tmp_path):
|
||||
"""
|
||||
On Windows, chrome.exe --version can open an existing browser session instead
|
||||
of printing a version, so fall back to the versioned install directory.
|
||||
"""
|
||||
chrome_path = str(tmp_path / "Google" / "Chrome" / "Application" / "chrome.exe")
|
||||
version_dir = Path(chrome_path).parent / "136.0.7103.114"
|
||||
|
||||
def fake_exists(path):
|
||||
return path == chrome_path
|
||||
|
||||
def fake_listdir(path):
|
||||
if path == str(Path(chrome_path).parent):
|
||||
return [version_dir.name]
|
||||
return []
|
||||
|
||||
def fake_isdir(path):
|
||||
return path == str(version_dir)
|
||||
|
||||
monkeypatch.setattr(check_environment, "_get_chrome_paths", lambda: [chrome_path], raising=False)
|
||||
monkeypatch.setattr(check_environment.os.path, "exists", fake_exists)
|
||||
monkeypatch.setattr(check_environment.os.path, "isdir", fake_isdir)
|
||||
monkeypatch.setattr(check_environment.os, "listdir", fake_listdir)
|
||||
monkeypatch.setattr(check_environment, "_run_command_get_version", lambda command: "正在现有的浏览器会话中打开。")
|
||||
|
||||
assert check_environment._find_chrome() == (
|
||||
chrome_path,
|
||||
"Google Chrome 136.0.7103.114",
|
||||
"136",
|
||||
)
|
||||
|
||||
|
||||
def test_get_chromedriver_paths_includes_autoinstaller_cache(monkeypatch, tmp_path):
|
||||
"""Environment checks should find drivers downloaded by chromedriver-autoinstaller."""
|
||||
package_dir = tmp_path / "chromedriver_autoinstaller"
|
||||
driver_dir = package_dir / "136"
|
||||
driver_dir.mkdir(parents=True)
|
||||
driver_path = driver_dir / "chromedriver.exe"
|
||||
driver_path.write_text("")
|
||||
|
||||
fake_autoinstaller = SimpleNamespace(__file__=str(package_dir / "__init__.py"))
|
||||
monkeypatch.setitem(sys.modules, "chromedriver_autoinstaller", fake_autoinstaller)
|
||||
|
||||
assert str(driver_path) in check_environment._get_chromedriver_paths()
|
||||
Reference in New Issue
Block a user