fix(chanmama): 导出确认弹窗被浮层清理误删与代理出口拉黑修复
浮层清理只删可见浮层——页面常驻 90+ 个 display:none 弹窗模板(含导出确认框 模板),误删后 Vue 只在游离节点上"打开"弹窗,页面上仅剩灰色遮罩与滚动锁, 导出等待 1320s 必超时;导出等待改为逐秒弹窗状态机(可点即点/点不到先关/卡死 强删并补点导出),导出失败落截图与 HTML 诊断;浏览器强制直连并切换真 Chrome 非 stealthy 模式(StealthySession 下蝉妈妈前端会静默压制弹窗渲染);导出按钮 原生可信点击优先,防 @click 绑定在内层元素时 JS 合成点击无效。
This commit is contained in:
1 parent
130f06b320
commit
41f448420d
3 files changed
+687
-55
No files matched your search
@@ -1,4 +1,5 @@
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
@@ -723,3 +724,168 @@ def test_wait_for_exported_excel_tolerates_driver_errors(monkeypatch):
|
||||
assert chanmama._wait_for_exported_excel(0.0, driver=_BrokenDriver()) == (
|
||||
"/downloads/已有.xlsx"
|
||||
)
|
||||
|
||||
|
||||
def test_refresh_then_export_relogin_on_expired_session(monkeypatch):
|
||||
"""运行中途登录态失效: 自动账密重登一次并重试当前博主,不影响其他博主。"""
|
||||
events = []
|
||||
navigate_calls = []
|
||||
|
||||
def fake_navigate(_driver, url):
|
||||
navigate_calls.append(url)
|
||||
if len(navigate_calls) == 1:
|
||||
raise RuntimeError(
|
||||
"蝉妈妈登录态失效,目标页被重定向到: "
|
||||
"https://www.chanmama.com/login.html"
|
||||
)
|
||||
|
||||
monkeypatch.setattr(chanmama, "navigate_to_target", fake_navigate)
|
||||
monkeypatch.setattr(
|
||||
chanmama,
|
||||
"refresh_zero_exposure_videos",
|
||||
lambda _driver: {
|
||||
"clicked": 0, "pending": 0, "zero_without_button": 0,
|
||||
"unconfirmed": 0, "pages": 1,
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
chanmama, "login",
|
||||
lambda _driver: events.append("login") or True,
|
||||
)
|
||||
monkeypatch.setattr(chanmama.time, "sleep", lambda _seconds: None)
|
||||
monkeypatch.setattr(chanmama, "_max_excel_mtime", lambda: 0.0)
|
||||
monkeypatch.setattr(
|
||||
chanmama, "export_video_data", lambda _driver, mtime: "x.xlsx"
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
chanmama, "parse_chanmama_excel", lambda _path: ([{"title": "v"}], None)
|
||||
)
|
||||
|
||||
records, summaries = chanmama.refresh_then_export_accounts(
|
||||
object(), ["account-1", "account-2"], wait_seconds=0
|
||||
)
|
||||
|
||||
assert events == ["login"] # 整套运行只自动重登一次
|
||||
assert len(records) == 2 # 两个博主最终都导出成功
|
||||
assert summaries[0]["pages"] == 1 # 博主 1 重登后刷新成功
|
||||
|
||||
|
||||
def test_refresh_then_export_no_relogin_for_other_errors(monkeypatch):
|
||||
"""非登录态失效的异常不触发自动重登。"""
|
||||
events = []
|
||||
|
||||
def fake_navigate(_driver, url):
|
||||
raise RuntimeError("蝉妈妈目标页加载失败: timeout")
|
||||
|
||||
monkeypatch.setattr(chanmama, "navigate_to_target", fake_navigate)
|
||||
monkeypatch.setattr(
|
||||
chanmama, "login",
|
||||
lambda _driver: events.append("login") or True,
|
||||
)
|
||||
monkeypatch.setattr(chanmama.time, "sleep", lambda _seconds: None)
|
||||
monkeypatch.setattr(
|
||||
chanmama, "refresh_zero_exposure_videos", lambda _driver: {}
|
||||
)
|
||||
monkeypatch.setattr(chanmama, "_max_excel_mtime", lambda: 0.0)
|
||||
monkeypatch.setattr(
|
||||
chanmama, "export_video_data", lambda _driver, mtime: None
|
||||
)
|
||||
|
||||
records, _summaries = chanmama.refresh_then_export_accounts(
|
||||
object(), ["account-1"], wait_seconds=0
|
||||
)
|
||||
|
||||
assert events == [] # 普通异常不触发自动重登
|
||||
assert records == [] # 导出失败不产出记录
|
||||
|
||||
|
||||
class _RenderFakeDriver:
|
||||
"""navigate_to_target 渲染链路的桩:列表只在浮层清理后才就绪。"""
|
||||
|
||||
def __init__(self, ready_after_dismiss=1):
|
||||
self.current_url = (
|
||||
"https://www.chanmama.com/bloggerRank/x.html?activeTab=aweme"
|
||||
)
|
||||
self.visited = []
|
||||
self.dismiss_runs = 0
|
||||
self.ready_after_dismiss = ready_after_dismiss
|
||||
self.screenshots = []
|
||||
|
||||
def get(self, url):
|
||||
self.visited.append(url)
|
||||
|
||||
def find_element(self, _by, _value):
|
||||
return object()
|
||||
|
||||
def execute_script(self, script, *_args):
|
||||
if script == chanmama._OVERLAY_DISMISS_SCRIPT:
|
||||
self.dismiss_runs += 1
|
||||
return 2
|
||||
if script == chanmama._VIDEO_LIST_READY_SCRIPT:
|
||||
return self.dismiss_runs >= self.ready_after_dismiss
|
||||
raise AssertionError(f"unexpected script: {script[:60]}")
|
||||
|
||||
def save_screenshot(self, path):
|
||||
self.screenshots.append(str(path))
|
||||
Path(path).write_bytes(b"png")
|
||||
return True
|
||||
|
||||
@property
|
||||
def page_source(self):
|
||||
return "<html>diag</html>"
|
||||
|
||||
|
||||
def test_navigate_to_target_clears_overlays_before_render_wait(monkeypatch):
|
||||
monkeypatch.setattr(chanmama.time, "sleep", lambda _seconds: None)
|
||||
driver = _RenderFakeDriver(ready_after_dismiss=1)
|
||||
|
||||
assert chanmama.navigate_to_target(
|
||||
driver, "https://www.chanmama.com/bloggerRank/x.html"
|
||||
) is True
|
||||
|
||||
assert driver.dismiss_runs >= 1 # 弹窗清理后才轮到渲染判定
|
||||
assert driver.visited == ["https://www.chanmama.com/bloggerRank/x.html"]
|
||||
|
||||
|
||||
def test_navigate_to_target_timeout_saves_diagnostics(monkeypatch, tmp_path):
|
||||
monkeypatch.setattr(chanmama.time, "sleep", lambda _seconds: None)
|
||||
monkeypatch.setattr(chanmama, "CHANMAMA_LIST_READY_TIMEOUT_SECONDS", 0.05)
|
||||
monkeypatch.setattr(chanmama, "DOWNLOAD_DIR", str(tmp_path))
|
||||
driver = _RenderFakeDriver(ready_after_dismiss=99)
|
||||
|
||||
with pytest.raises(RuntimeError, match="蝉妈妈目标页加载失败"):
|
||||
chanmama.navigate_to_target(
|
||||
driver, "https://www.chanmama.com/bloggerRank/x.html"
|
||||
)
|
||||
|
||||
assert driver.dismiss_runs >= 2 # 两次尝试各自清理过浮层
|
||||
assert len(driver.screenshots) == 1
|
||||
diag_dir = tmp_path / "_diag"
|
||||
assert list(diag_dir.glob("render_failure_*.png"))
|
||||
assert list(diag_dir.glob("render_failure_*.html"))
|
||||
|
||||
|
||||
def test_dismiss_overlays_ignores_driver_errors():
|
||||
class BoomDriver:
|
||||
def execute_script(self, _script, *_args):
|
||||
raise RuntimeError("execution context destroyed")
|
||||
|
||||
assert chanmama._dismiss_overlays(BoomDriver()) == 0
|
||||
|
||||
|
||||
def test_wait_video_list_ready_tolerates_transient_script_errors(monkeypatch):
|
||||
monkeypatch.setattr(chanmama.time, "sleep", lambda _seconds: None)
|
||||
|
||||
class FlakyDriver:
|
||||
def __init__(self):
|
||||
self.ready_calls = 0
|
||||
|
||||
def execute_script(self, script, *_args):
|
||||
if script == chanmama._OVERLAY_DISMISS_SCRIPT:
|
||||
return 0
|
||||
self.ready_calls += 1
|
||||
if self.ready_calls == 1:
|
||||
raise RuntimeError("frame detached")
|
||||
return True
|
||||
|
||||
chanmama._wait_video_list_ready(FlakyDriver(), 2) # 不抛即通过
|
||||
Reference in new issue
Block a user