fix(cdp,js): playwright actionability stubs and objectId resolve (#178)

Playwright fails locator actions on Obscura for two avoidable reasons
that are separate from the missing layout engine. Both have trivial
fixes that unblock the actionability polling loop.

- Element.checkVisibility(opts) was undefined. Playwright >= 1.25 calls
  it before every input event and retries until its action timeout if
  the method isn't a function. Add a stub that returns true. Without a
  layout pass we can't compute it for real, but a permissive stub still
  beats the undefined-function path.

- ARIA reflection properties were missing on Element (role, ariaLabel,
  ariaSelected, ariaExpanded, ariaHidden, ariaChecked, ariaDisabled,
  ariaRoleDescription). Playwright's getByRole and getByLabel locators
  read them. Reflect through to the underlying aria-* attributes for
  both read and write.

- DOM.getBoxModel and DOM.getContentQuads ignored the objectId param.
  Playwright passes only objectId (from a prior DOM.resolveNode), and
  the handlers silently defaulted to node 0, so dispatchMouseEvent
  always clicked the wrong element. Extract a resolve_node_id helper
  that honors nodeId, backendNodeId, and objectId in that order.

- Page.captureScreenshot and Page.captureSnapshot now return a clear
  "not supported, use a real Chromium for the screenshot leg" error
  instead of the generic "Unknown Page method" fallback. Matches the
  pattern already in place for Page.printToPDF.

Partial fix for #45. The architectural blocker (no layout engine, so
multi-element click coordinates are wrong) is unchanged.
This commit is contained in:
SGavrl authored and GitHub committed 2026-05-24 20:13:43 +00:00
1 parent 3d12f76d8e
commit a3b21bbb70
4 files changed
+155 -6

No files matched your search

+39
View File
@@ -313,6 +313,17 @@ pub async fn handle(
.to_string(),
)
}
"captureScreenshot" | "captureSnapshot" => {
// Same story as printToPDF: rasterising a page needs a layout and
// paint pipeline that Obscura intentionally does not have. Reply
// with a clear error so clients can fail fast instead of waiting
// on the generic "Unknown Page method" reply.
Err(format!(
"Page.{method} is not supported by Obscura: no layout or paint engine. \
For visual snapshots, drive a real headless Chromium for the \
screenshot leg of your pipeline and use Obscura for the scraping leg."
))
}
_ => Err(format!("Unknown Page method: {}", method)),
}
}
@@ -398,4 +409,32 @@ mod tests {
"error must point to a workaround: {err}"
);
}
/// Regression for #45: same idea as printToPDF for captureScreenshot.
/// Playwright's `page.screenshot()` calls Page.captureScreenshot via CDP;
/// without an explicit arm, clients see "Unknown Page method" and have
/// no idea why their screenshot request failed.
#[tokio::test]
async fn capture_screenshot_returns_descriptive_unsupported_error() {
let mut ctx = CdpContext::new();
let err = handle("captureScreenshot", &json!({}), &mut ctx, &None)
.await
.expect_err("captureScreenshot must error until a real paint exists");
assert!(
!err.contains("Unknown Page method"),
"captureScreenshot must NOT fall through to the catch-all: {err}"
);
assert!(
err.contains("not supported by Obscura"),
"error must clearly state screenshot is unsupported: {err}"
);
// Same for the MHTML snapshot sibling method.
let err2 = handle("captureSnapshot", &json!({}), &mut ctx, &None)
.await
.expect_err("captureSnapshot must error until a real renderer exists");
assert!(
!err2.contains("Unknown Page method"),
"captureSnapshot must NOT fall through: {err2}"
);
}
}