Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error trying to access shadowRoot elements on iOS #9733

Open
dyhopper1 opened this issue Dec 2, 2017 · 26 comments
Open

Error trying to access shadowRoot elements on iOS #9733

dyhopper1 opened this issue Dec 2, 2017 · 26 comments
Labels
iOS related to iOS native driver(s) ThirdParty upstream problems

Comments

@dyhopper1
Copy link

The problem

Can't access elements under a shadowRoot on iOS real device.
We have an automation suite that runs on our Chrome app, and our Mac and Windows Electron apps using Selenium Webdriver. I'm hoping to use much of the same code on our Hybrid iOS app via Appium.
We use Polymer and thus have to access elements under shadowRoots.
On other platforms, we are doing this via
shadowElement = $driver.execute_script('return document.querySelector("blah_blah").shadowRoot')
el = shadowElement.find_element(:css => "#id_of_my_element")

This is not working in Appium. I'm getting a "Recursive object cannot be transferred" error.

Environment

Appium 1.7.1 (using Appium desktop app 1.2.7)
Mac OS 10.13
Xcode 9.1
iOS 11.1.2 on real iPad Air

Details

I believe I have the capabilities set up correctly, as I can switch to the web context and access the elements above the shadowRoot elements.
When running the sample code below I get the error: "[MJSONWP] Encountered internal error running command: Error: Error while executing atom: Recursive object cannot be transferred (status: 13)"

Link to Appium logs

https://gist.github.com/dyhopper1/0df126924abfd093ae02c80fc71a5fc4

Code To Reproduce Issue [ Good To Have ]

(Sample ruby code)

`capabilities =
{
"bundleId"=> "com.vernier.spectralanalysis",
"udid" => "1905da18d1c56349efb55ab04xxxxxxxxxxxxxxx",
"platformName" => "iOS",
"deviceName" => "iPad Air",
"platformVersion" => "11.1",
"xcodeOrgId" => "xxxxxxxxxx",
"xcodeSigningId" => "iPhone Developer",
"startIWDP" => true
}

$driver = Appium::Driver.new({:caps => capabilities}, false).start_driver
sleep 4
webview = $driver.available_contexts.last
$driver.set_context(webview) #works

el = $driver.execute_script('return document.querySelector("vst-sa-app")')
p el # just to prove the element was retrieved successfully. no problem.

#the following line results in error
shadowEl = $driver.execute_script('return document.querySelector("vst-sa-app").shadowRoot')

`

@mykola-mokhnach mykola-mokhnach added iOS related to iOS native driver(s) ThirdParty upstream problems labels Dec 2, 2017
@dyhopper1
Copy link
Author

I'm still stuck on this. If anyone has any advice on what else I could try to get more information, please let me know.

@ringles-icfi
Copy link

ringles-icfi commented Jan 24, 2018

I'm having the same problem. I ran the command: "document.querySelector("dropdown").shadowRoot" on the following:

  • From web inspector on a simulator, it would expand the shadowRoot with no problem
  • Running on Safari Desktop, it would expand the shadowRoot with no problem
  • Running on Safari iOS, it would raise "Recursive object cannot be transferred"

I have tried Appium 1.7.1 and Appium 1.7.2.

@dyhopper
Copy link

dyhopper commented Jul 2, 2018

@ringles-icfi or anyone else. Did you ever come up with a good workaround for this issue?

@jordanaustin
Copy link

@mykola-mokhnach do you know why this is marked as third party? If so, who's issue do you think this is? I'd love to help find a solution to this issue.

@mykola-mokhnach
Copy link
Collaborator

do you know why this is marked as third party? If so, who's issue do you think this is?

I think the problem has to do with javascript processing inside mobile safari. Appium is just acting as a proxy between your client code and the js engine.

We'd appreciate it if you could help to find a proper solution.

@jordanaustin
Copy link

@mykola-mokhnach the interesting thing is that running these same commands on the device with remote debugging through Safari's developer tools doesn't throw this error.

@imurchie
Copy link
Contributor

Can you try to add .host to the end, so, for instance,

document.querySelector("dropdown").shadowRoot

would become

document.querySelector("dropdown").shadowRoot.host

@jordanaustin
Copy link

@imurchie thanks for your reply! You're thinking along the same lines as me, I tried this previously and again just now. The problem with using .host is that this just gives you the host element again, so

document.querySelector("dropdown").shadowRoot.host 

Will just return you the dropdown as if you just used

document.querySelector("dropdown")

So using find_element at that point will just result in not being able to locate the element, because you're not in the shadowRoot

@imurchie
Copy link
Contributor

Hmm. That makes sense.

The problem is in the Selenium "atoms", which is how Appium injects JavaScript into the page context. When I remove the check that throw the error, the element available is like the following:

{ mode: 'open',
  host: { ELEMENT: ':wdc:1531325564675' },
  innerHTML: '\n      <style>\n        p {\n          color: red;\n        }\n      </style>\n\n      <p>Element with Shadow DOM</p>',
  activeElement: null,
  elementFromPoint: 'function elementFromPoint() {\n    [native code]\n}',
  elementsFromPoint: 'function elementsFromPoint() {\n    [native code]\n}',
  children:
   [ { ELEMENT: ':wdc:1531325564676' },
     { ELEMENT: ':wdc:1531325564677' } ],
  firstElementChild: { ELEMENT: ':wdc:1531325564676' },
  lastElementChild: { ELEMENT: ':wdc:1531325564677' },
  childElementCount: 2,
  getElementById: 'function getElementById() {\n    [native code]\n}',
  prepend: 'function prepend() {\n    [native code]\n}',
  append: 'function append() {\n    [native code]\n}',
  querySelector: 'function querySelector() {\n    [native code]\n}',
  querySelectorAll: 'function querySelectorAll() {\n    [native code]\n}',
  nodeType: 11,
  nodeName: '#document-fragment',
  baseURI: 'http://localhost:4994/test/shadow',
  isConnected: true,
  ownerDocument: { ELEMENT: ':wdc:1531325564678' },
  parentNode: null,
  parentElement: null,
  childNodes:
   [ [ null, null, null, null, null, null, null ],
     { ELEMENT: ':wdc:1531325564676' },
     [ null, null, null, null, null, null, null, null ],
     { ELEMENT: ':wdc:1531325564677' } ],
  firstChild: [ null, null, null, null, null, null, null ],
  lastChild: { ELEMENT: ':wdc:1531325564677' },
  previousSibling: null,
  nextSibling: null,
  nodeValue: null,
  textContent: '\n      \n        p {\n          color: red;\n        }\n      \n\n      Element with Shadow DOM',
  getRootNode: 'function getRootNode() {\n    [native code]\n}',
  hasChildNodes: 'function hasChildNodes() {\n    [native code]\n}',
  normalize: 'function normalize() {\n    [native code]\n}',
  cloneNode: 'function cloneNode() {\n    [native code]\n}',
  isEqualNode: 'function isEqualNode() {\n    [native code]\n}',
  isSameNode: 'function isSameNode() {\n    [native code]\n}',
  compareDocumentPosition: 'function compareDocumentPosition() {\n    [native code]\n}',
  contains: 'function contains() {\n    [native code]\n}',
  lookupPrefix: 'function lookupPrefix() {\n    [native code]\n}',
  lookupNamespaceURI: 'function lookupNamespaceURI() {\n    [native code]\n}',
  isDefaultNamespace: 'function isDefaultNamespace() {\n    [native code]\n}',
  insertBefore: 'function insertBefore() {\n    [native code]\n}',
  appendChild: 'function appendChild() {\n    [native code]\n}',
  replaceChild: 'function replaceChild() {\n    [native code]\n}',
  removeChild: 'function removeChild() {\n    [native code]\n}',
  ELEMENT_NODE: 1,
  ATTRIBUTE_NODE: 2,
  TEXT_NODE: 3,
  CDATA_SECTION_NODE: 4,
  ENTITY_REFERENCE_NODE: 5,
  ENTITY_NODE: 6,
  PROCESSING_INSTRUCTION_NODE: 7,
  COMMENT_NODE: 8,
  DOCUMENT_NODE: 9,
  DOCUMENT_TYPE_NODE: 10,
  DOCUMENT_FRAGMENT_NODE: 11,
  NOTATION_NODE: 12,
  DOCUMENT_POSITION_DISCONNECTED: 1,
  DOCUMENT_POSITION_PRECEDING: 2,
  DOCUMENT_POSITION_FOLLOWING: 4,
  DOCUMENT_POSITION_CONTAINS: 8,
  DOCUMENT_POSITION_CONTAINED_BY: 16,
  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32,
  addEventListener: 'function addEventListener() {\n    [native code]\n}',
  removeEventListener: 'function removeEventListener() {\n    [native code]\n}',
  dispatchEvent: 'function dispatchEvent() {\n    [native code]\n}' }

Perhaps it is possible to develop queries based on available properties or functions?

@jordanaustin
Copy link

@imurchie this is very interesting! Could you point me to where that check is at? I'm digging into Selenium for the first time, so any direction would be great!

@imurchie
Copy link
Contributor

The check itself is happening here: https://github.com/SeleniumHQ/selenium/blob/master/javascript/atoms/inject.js#L123-L126

@dominikg
Copy link

dominikg commented Sep 17, 2018

Selenium atoms seem to have more issues with shadow-dom. e.g. SeleniumHQ/selenium#6399

I stumbled accross that when i wrote a custom By implementation that uses javascript to find elements inside a shadowroot. This custom By works in chromedriver but fails when run via Appium on an ios device (i guess chromedriver does not use these atoms because why not....)

@caseycantwell
Copy link

caseycantwell commented Oct 5, 2018

Has anyone found a work around to this? We haven't been able to find a way to execute selenium scripts on appium and IOS, where shadow doms are used. Our work around for lack of shadow dom support within selenium works fine on Chrome / Android with appium.

@mykola-mokhnach
Copy link
Collaborator

Linking https://appiumpro.com/editions/44

@alfonso-presa
Copy link

Any news on this issue? Interacting with the elements via JS is not ideal as elements are not interacted as the user would do so it may give false positives when executing tests (element is interactuable with JS and not by the user).

@imurchie
Copy link
Contributor

This has been fixed in version 1.16.0

@dyhopper
Copy link

We were excited to see this get fixed, but I just tried the exact steps from the original ticket and this issue is still happening with Appium 1.16.0. Maybe there is a different way I should be accessing the shadowRoot elements?
My environment now:
Appium 1.16.0
Mac OS 10.14.6
Xcode 11.3
iOS 12.4.1 on real iPad Pro

@imurchie
Copy link
Contributor

Can someone provide an app in which this is a problem?

@dyhopper
Copy link

dyhopper commented Jan 6, 2020

Sorry for the delay. Yes, this is easily reproduced using the sample site at http://shop.polymer-project.org.
Here is my quick code to demonstrate the issue:

require 'appium_lib'

caps =
{
  'browserName' => 'safari',
  'deviceName' => 'iPad',
  'platformVerion' => '12.4',
  "platformName" => "iOS",
  "automationName" => "XCUITest",
  "udid" => "[something]",
  "startIWDP" => true,
  "xcodeOrgId" => "[something]",
  "xcodeSigningId" => "iPhone Developer"
}
appiumlib = {
  "server_url": "http://localhost:4723/wd/hub"
}
$driver = Appium::Driver.new({:caps => caps, :appium_lib => appiumlib}, false).start_driver
$driver.get("http://shop.polymer-project.org")
btn = $driver.execute_script("return document.querySelector('shop-app').shadowRoot.querySelector('shop-home').shadowRoot")
btn.find_element("shop-button")

@dyhopper
Copy link

dyhopper commented Mar 3, 2020

@imurchie This is still limiting our ability to work with shadowDoms on iOS. Is there any further news on this? Can we re-open this ticket since it really isn't fixed as stated. Or, should I just create a new ticket?
Thanks!

@imurchie
Copy link
Contributor

imurchie commented Mar 3, 2020

@dyhopper Sorry, this slipped through my workload. I'll take a look at what is going on with your repro case.

@imurchie imurchie reopened this Mar 3, 2020
@imurchie
Copy link
Contributor

imurchie commented Mar 3, 2020

The error I'm seeing is

info [11-39-56:104] HTTP --> POST /wd/hub/session/f0f3b175-b645-479d-a113-5dc6d9262cae/execute
info [11-39-56:104] HTTP {"script":"return document.querySelector('shop-app').shadowRoot.querySelector('shop-home').shadowRoot","args":[]}
dbug [11-39-56:105] MJSONWP (f0f3b175) Calling XCUITestDriver.execute() with args: ["return document.querySelector('shop-app').shadowRoot.querySelector('shop-home').shadowRoot",[],"f0f3b175-b645-479d-a113-5dc6d9262cae"]
dbug [11-39-56:105] XCUITest Executing command 'execute'
dbug [11-39-56:105] RemoteDebugger Executing atom 'execute_script'
dbug [11-39-56:111] RemoteDebugger Executing 'execute_script' atom in default context
dbug [11-39-56:111] RemoteDebugger Sending javascript command: '(function(){return function(){var e=this;
dbug [11-39-56:111] RemoteDebugger funct...'
dbug [11-39-56:111] RemoteDebugger Sending '_rpc_forwardSocketData:' message to app 'PID:8435', page '2', target 'page-20' (id: 42): 'Runtime.evaluate'
dbug [11-39-56:607] BaseDriver Waiting up to 0 ms for condition
dbug [11-39-56:608] WD Proxy Matched '/elements' to command name 'findElements'
dbug [11-39-56:609] WD Proxy Proxying [POST /elements] to [POST http://127.0.0.1:8100/session/2A91D5FE-B251-4314-8C42-F7CF3B694A99/elements] with body: {"using":"class name","value":"XCUIElementTypeScrollView"}
dbug [11-39-56:626] WD Proxy Got response with status 200: {"value":[],"sessionId":"2A91D5FE-B251-4314-8C42-F7CF3B694A99"}
dbug [11-39-56:847] RemoteDebugger Received data response from send (id: 42): '{"status":17,"value":{"message":"Recursive object cannot be transferred"}}'
dbug [11-39-56:847] RemoteDebugger Sending to Web Inspector took 736ms
dbug [11-39-56:848] MJSONWP Matched JSONWP error code 17 to JavaScriptError
dbug [11-39-56:857] XCUITest Error received while executing atom: Recursive object cannot be transferred
dbug [11-39-57:132] MJSONWP (f0f3b175) Encountered internal error running command: JavaScriptError: Recursive object cannot be transferred
dbug [11-39-57:132] MJSONWP (f0f3b175)     at errorFromMJSONWPStatusCode (/Users/isaacmurchie/code/appium-xcuitest-driver/node_modules/appium-base-driver/lib/protocol/errors.js:764:12)
dbug [11-39-57:132] MJSONWP (f0f3b175)     at convertResult (/Users/isaacmurchie/code/appium-xcuitest-driver/node_modules/appium-remote-debugger/lib/helpers.js:253:11)
dbug [11-39-57:132] MJSONWP (f0f3b175)     at RemoteDebugger.execute (/Users/isaacmurchie/code/appium-xcuitest-driver/node_modules/appium-remote-debugger/lib/remote-debugger.js:753:12)
info [11-39-57:133] HTTP <-- POST /wd/hub/session/f0f3b175-b645-479d-a113-5dc6d9262cae/execute 500 1028 ms - 125

@imurchie
Copy link
Contributor

The latest beta of Appium (with appium-remote-debugger@8.7.0) should not have the "Recursive object cannot be transferred" error. Still working on interacting with Shadow elements, though your mileage may vary on that.

@sweaver2
Copy link

sweaver2 commented Sep 3, 2020

Just wanted to check in on this situation and see if there has been any movement on interacting with Shadow elements.

@itaymelamed
Copy link

any update?

@caseycantwell
Copy link

Any update on this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
iOS related to iOS native driver(s) ThirdParty upstream problems
Projects
None yet
Development

No branches or pull requests