feat(browser): 插件真实坐标点击与页面内容读取(CF-2)
- umt 插件 POST /umt/tabs/{id}/mouse-click:page.mouse.click,跨域 iframe 的 Turnstile 也点得到;click 坐标优先走它,旧镜像退回 elementFromPoint
- 适配器 pageContent(地址 / 标题 / outerHTML / 正文 / navigator.userAgent);setEgress 返回插件出口模式
- mock camofox 增加 Cloudflare 模式(auto / interactive / captcha)与插件鼠标点击
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
a11ef16e40
commit
6b14b0da45
7 files changed
+402
-10
No files matched your search
@@ -163,12 +163,67 @@ describe("camofox 适配器:身份与出口", () => {
|
||||
mock = await createMockCamofox({ accessKey: "k" });
|
||||
const p = provider(mock);
|
||||
expect(await p.identity!()).toMatchObject({ userAgent: MOCK_CAMOFOX_IDENTITY.userAgent, os: "windows" });
|
||||
expect(await p.setEgress!("site-a", { server: "http://127.0.0.1:7891" })).toEqual({ restarted: false });
|
||||
expect(await p.setEgress!("site-b", null)).toEqual({ restarted: false });
|
||||
expect(await p.setEgress!("site-a", { server: "http://127.0.0.1:7891" })).toEqual({
|
||||
restarted: false,
|
||||
mode: "site",
|
||||
});
|
||||
expect(await p.setEgress!("site-b", null)).toEqual({ restarted: false, mode: "site" });
|
||||
expect(mock.egress.get("site-a")).toEqual({ server: "http://127.0.0.1:7891" });
|
||||
expect(mock.egress.get("site-b")).toBeNull();
|
||||
});
|
||||
|
||||
const budget = { domainAllowlist: ["audiences.example"], timeoutMs: 5_000, maxRetries: 0, maxBytes: 1_000_000 };
|
||||
const openCf = async (m: MockCamofox) => {
|
||||
const p = provider(m);
|
||||
const { session } = await p.openSession({
|
||||
providerId: "11111111-1111-4111-8111-111111111111",
|
||||
siteId: "s",
|
||||
accountId: "a",
|
||||
profileKey: "umt-site-s-acct-a",
|
||||
initialUrl: "https://audiences.example/torrents.php",
|
||||
budget,
|
||||
});
|
||||
return { p, ref: { sessionId: session.id } };
|
||||
};
|
||||
const cfSite = (mode: "auto" | "interactive" | "captcha") => ({
|
||||
origin: "https://audiences.example",
|
||||
username: "u",
|
||||
password: "p",
|
||||
protectedPaths: /^$/,
|
||||
cloudflare: { mode, autoPolls: 2, clearance: "clr-1" },
|
||||
});
|
||||
|
||||
it("CF-2 pageContent():地址 / 标题 / HTML / UA;挑战页分类由调用方做", async () => {
|
||||
mock = await createMockCamofox({ accessKey: "k", site: cfSite("auto") });
|
||||
const { p, ref } = await openCf(mock);
|
||||
const c1 = await p.pageContent!(ref);
|
||||
expect(c1).toMatchObject({ title: "Just a moment...", userAgent: MOCK_CAMOFOX_IDENTITY.userAgent });
|
||||
expect(c1?.html).toContain("_cf_chl_opt");
|
||||
// 第 2 次读页面时 mock 自动放行,并写 cf_clearance
|
||||
const c2 = await p.pageContent!(ref);
|
||||
expect(c2?.title).not.toBe("Just a moment...");
|
||||
const cookies = await p.getCookies!("umt-site-s-acct-a", ["https://audiences.example/"]);
|
||||
expect(cookies.find((c) => c.name === "cf_clearance")?.value).toBe("clr-1");
|
||||
});
|
||||
|
||||
it("CF-2 坐标点击走插件真实鼠标事件(跨域 iframe 的 Turnstile 也点得到),不再 elementFromPoint", async () => {
|
||||
mock = await createMockCamofox({ accessKey: "k", site: cfSite("interactive") });
|
||||
const { p, ref } = await openCf(mock);
|
||||
// 第 2 行(y = 20 + 40 + 10)是复选框
|
||||
await p.click(ref, { x: 100, y: 70 });
|
||||
expect(mock.actions.some((a) => a.action === "mouse-click" && a.body.x === 100 && a.body.y === 70)).toBe(true);
|
||||
expect(mock.actions.some((a) => a.action === "evaluate")).toBe(false);
|
||||
expect(mock.cfPassed.has("umt-site-s-acct-a")).toBe(true);
|
||||
});
|
||||
|
||||
it("CF-2 插件没有 mouse-click(旧镜像)→ 退回 elementFromPoint + 选择器点击", async () => {
|
||||
mock = await createMockCamofox({ accessKey: "k", cookieApi: "none", site: cfSite("interactive") });
|
||||
const { p, ref } = await openCf(mock);
|
||||
await p.click(ref, { x: 100, y: 70 });
|
||||
expect(mock.actions.some((a) => a.action === "evaluate")).toBe(true);
|
||||
expect(mock.actions.some((a) => a.action === "click")).toBe(true);
|
||||
});
|
||||
|
||||
it("旧插件(没有接口):identity() 为 null,setEgress() 报 not_implemented", async () => {
|
||||
mock = await createMockCamofox({ accessKey: "k", cookieApi: "none" });
|
||||
const p = provider(mock);
|
||||
|
||||
Reference in new issue
Block a user