Vrtgs/thirtyfour

Shadow DOM

Closed this issue · 6 comments

According to the README, Shadow DOM is supported. I can't seem to find any documentation/examples on how to access the Shadow DOM via thirtyfour. I've tried a few different approaches (such as trying to treat a shadow DOM node parent as a frame, querying for an ID of #shadow-root) but haven't had any success. There doesn't appear to be any reference to shadow in of the codebase. Is there some other less obvious way to access nodes in a shadow DOM?

It should work the same as with any other selenium client. From memory, I think you need to get the shadow-root node and switch to frame. From there I think all element access is relative, the same as an iframe. I haven't done this in a while though, so if it doesn't work I'd be interested to know. If you google for how to do this in selenium generally, the same approach should work with thirtyfour

After googling around a bit the recommended approach is run some javascript to query into the shadowRoot. This works, but is pretty ugly.

let mut script_args = ScriptArgs::new();
script_args.push(shadow_parent);
script_args.push("button.primary");
let shadow_res = driver.execute_async_script_with_args(r#"
let done = arguments[2];
done(arguments[0].shadowRoot.querySelector(arguments[1]));
"#, &script_args).await?;
let shadowed_element = shadow_res.get_element()?;

Some sites suggest that a newer version of Selenium as of late last year now has a shadowRoot method (ref) which probably does effectively the same thing under the hood.

Trying to access the shadowRoot element and convert it using get_element does not work directly, as shadowRoots have different element identifer than WebElements element-6066-11e4-a52e-4f735466cecf, which I think may limit the ability to switch_to it. Web isn't really my area of expertise so I could be way off.

At the very least, thirtyfour does technically support Shadow DOMs, so I suppose this issue is moot. But it might be worth considering either supporting the new Selenium API, or perhaps internalizing some of this behavior for the benefit of thirtyfour users.

Thanks for thirtyfour, other than this minor annoyance, it's proven to be very useful.

I've definitely used regular selenium 3.x to navigate a page that uses shadow Dom elements before (including chrome's print preview window) using the python selenium library. I don't think I used any JavaScript to do it.

I might have a look at this sometime because I'm pretty sure it's possible. I wouldn't have added it to the readme unless I had tried it. I did have a YouTube example in the repo previously but removed it because YouTube keeps changing their Dom so it kept breaking. I have a feeling that example used it somewhere.

Ok, so it seems this changed recently in chrome v96, and now the returned element doesn't match the WebDriver spec for an element handle.

By executing this script via JS:

return arguments[0].shadowRoot

I'm seeing:

{
    "shadow-6066-11e4-a52e-4f735466cecf": "daaea226-43aa-400f-896c-210e5af2ac62"
}

which you pointed out earlier. This won't deserialize into a WebElement currently.

There are two ways to work around this:

  1. Modify the WebElement deserialization to look for either element-6066-11e4-a52e-4f735466cecf or shadow-6066-11e4-a52e-4f735466cecf and transparently "just work" with both.
  2. Add a get_shadow_root() method to WebElement

Actually, we can do both to make it easier. Doing (1) means we don't need a special get_shadow_element() on ScriptRet when executing the above javascript to get the shadow root. Doing (2) would just do the JS call for you, saving users time trying to figure it out.

Thoughts? I'm going to give this a go and see what it's like

I've just published v0.28.2 which should fix it.

See the shadowroot.rs example in the examples directory.
I added WebElement::get_shadow_root() to make it easier to get the shadow root element. No need to use switch_to(), just use this element to query elements nested below it as normal.

Works for me. Much better than the janky workaround I was using.