fix(humanize): port #303 iframe pointer-events fix to Python

Mirror the JS fix from #303 in the sync and async Python actionability
checks: compute and apply the iframe coordinate offset before
elementFromPoint, and fail open when the check itself cannot run. Add
fail-open regression tests for both Python and JS.
This commit is contained in:
CloakHQ
2026-05-25 01:17:05 +02:00
parent 12d02c3547
commit 7fc577e5c6
4 changed files with 125 additions and 18 deletions
+40
View File
@@ -1559,3 +1559,43 @@ describe("frame.click timeout budget (#307)", () => {
expect(elapsed).toBeLessThan(TIMEOUT_MS * 1.8);
});
});
describe("pointer-events check fail-open", () => {
// When the check itself cannot run (evaluate / boundingBox throws -> result
// null), proceed with the click instead of blocking it until the timeout.
it("checkPointerEventsHandle returns promptly when evaluate throws", async () => {
const { checkPointerEventsHandle } = await import("../src/human/actionability.js");
const el = {
boundingBox: vi.fn().mockRejectedValue(new Error("stale handle")),
evaluate: vi.fn().mockRejectedValue(new Error("execution context destroyed")),
};
const start = Date.now();
await checkPointerEventsHandle(el as any, 100, 100, 2000); // must not throw
expect(Date.now() - start).toBeLessThan(500);
});
it("checkPointerEvents returns promptly when evaluate throws", async () => {
const { checkPointerEvents } = await import("../src/human/actionability.js");
const loc = {
first: () => loc,
boundingBox: vi.fn().mockRejectedValue(new Error("no element")),
evaluate: vi.fn().mockRejectedValue(new Error("no element")),
};
const page = { locator: vi.fn().mockReturnValue(loc) };
const start = Date.now();
await checkPointerEvents(page as any, "#x", 100, 100, null, 2000); // must not throw
expect(Date.now() - start).toBeLessThan(500);
});
it("checkPointerEventsHandle still throws when genuinely covered", async () => {
const { checkPointerEventsHandle, ElementNotReceivingEventsError } =
await import("../src/human/actionability.js");
const el = {
boundingBox: vi.fn().mockResolvedValue({ x: 0, y: 0, width: 10, height: 10 }),
evaluate: vi.fn().mockResolvedValue({ hit: false, covering: "DIV" }),
};
await expect(checkPointerEventsHandle(el as any, 5, 5, 200)).rejects.toBeInstanceOf(
ElementNotReceivingEventsError,
);
});
});