fix(chanmama): 导出确认弹窗被浮层清理误删与代理出口拉黑修复

浮层清理只删可见浮层——页面常驻 90+ 个 display:none 弹窗模板(含导出确认框
模板),误删后 Vue 只在游离节点上"打开"弹窗,页面上仅剩灰色遮罩与滚动锁,
导出等待 1320s 必超时;导出等待改为逐秒弹窗状态机(可点即点/点不到先关/卡死
强删并补点导出),导出失败落截图与 HTML 诊断;浏览器强制直连并切换真 Chrome
非 stealthy 模式(StealthySession 下蝉妈妈前端会静默压制弹窗渲染);导出按钮
原生可信点击优先,防 @click 绑定在内层元素时 JS 合成点击无效。
This commit is contained in:
wangyunlong committed 2026-10-09 13:45:29 +08:00
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) # 不抛即通过