Skip to content

fix(web): keep resource-id a string under form named-property access#3320

Open
ChaMinGyuSoftleaf wants to merge 1 commit into
mobile-dev-inc:mainfrom
ChaMinGyuSoftleaf:fix/web-resource-id-named-property
Open

fix(web): keep resource-id a string under form named-property access#3320
ChaMinGyuSoftleaf wants to merge 1 commit into
mobile-dev-inc:mainfrom
ChaMinGyuSoftleaf:fix/web-resource-id-named-property

Conversation

@ChaMinGyuSoftleaf

Copy link
Copy Markdown

Summary

Fixes the web (Chromium) ClassCastException: LinkedHashMap cannot be cast to String
crash from #2944. It deterministically breaks any flow that navigates to a page with a
<form> whose child control is named id or name — extremely common on login screens
(<input name="id">).

This is complementary to #3306 (the bounds hardening): that PR guards bounds, while
this one addresses the attribute that actually triggers #2944resource-id. In current
main, bounds and text are always built as strings, so resource-id is the only
attribute that can be non-string from this path.

Root cause

maestro-web.js builds resource-id from node.id || node.ariaLabel || node.name || ….

HTMLFormElement (and a few other elements) expose their child controls as properties via
HTML named property access,
so for <form><input name="id"></form>, form.id returns the child <input> element,
not the string id attribute. That element serializes to an object and then hits the
unguarded as String cast in parseDomAsTreeNodes:

java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class java.lang.String
    at maestro.drivers.CdpWebDriver.parseDomAsTreeNodes(...)

Changes

  • maestro-web.js (root cause): when node.id / node.name isn't a string, fall back
    to getAttribute('id') / getAttribute('name'). Keeps resource-id a string and
    preserves the form's real id instead of dropping to the next fallback.
  • CdpWebDriver / WebDriver parseDomAsTreeNodes (defense-in-depth): defensively
    coerce resource-id (as? String ?: toString()) so a stray non-string value degrades
    gracefully instead of crashing the hierarchy walk. Applied to both drivers.
  • Unit tests (CdpWebDriverTest, WebDriverTest): cover the normal string
    resource-id and the non-string (named-property) case for both drivers.

Reproduction

Two static pages served via python3 -m http.server:

index.html

<a id="go" href="login.html">Go to login</a>

login.html

<form id="loginForm"><input name="id" type="text" /></form>

Flow:

url: http://localhost:8000/index.html
---
- openLink: http://localhost:8000/index.html
- tapOn: "Go to login"

Tapping the link navigates to login.html; the post-tap waitForAppToSettle parses the
new DOM and crashes on main.

Verification

  • ./gradlew :maestro-client:test — the new tests pass; they fail with the
    ClassCastException without the driver change.
  • Manual: patched maestro-web.js swapped into the 2.6.0 jar (Chromium driver). Both the
    minimal repro above and a real login page crashed before / pass after. Renaming only
    name="id"name="memberId" also avoids the crash, confirming the trigger.

Closes #2944

…obile-dev-inc#2944)

On a <form> with a child control named "id"/"name" (common on login forms,
e.g. <input name="id">), node.id/node.name resolve to that child element
instead of the string attribute via HTML named property access. maestro-web.js
passed that element into resource-id, which then threw while walking the
hierarchy:

    ClassCastException: LinkedHashMap cannot be cast to String
    (parseDomAsTreeNodes)

This deterministically broke any flow navigating to such a page (typically a
login screen).

- maestro-web.js: fall back to getAttribute('id')/getAttribute('name') when
  node.id/node.name isn't a string (keeps resource-id a string, preserves the
  form's real id).
- CdpWebDriver / WebDriver parseDomAsTreeNodes: defensively coerce resource-id
  as a second line of defense.
- Add unit tests for both drivers covering the non-string resource-id case.
@Fishbowler

Copy link
Copy Markdown
Contributor

From your description, you're suggesting abandoning #3306?

@ChaMinGyuSoftleaf

Copy link
Copy Markdown
Author

No, they're complementary. This PR fixes the trigger I could actually reproduce
(resource-id via form named property access). I couldn't get bounds to come
through as an object on current main, but #3306 is cheap insurance either way.

Fine to land both as far as I'm concerned, or I can rebase this on top of #3306
if that's easier for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Web Beta] ClassCastException: LinkedHashMap cannot be cast to String after tap triggers navigation

3 participants