Avoid accessing shadow element in selenium/Chrome
Asked Answered
M

1

1

After my Chrome update (now Version 66.0.3359.117), the plugins options is not displayed like it used to be (you have to click once more to get the same result, it's uglier, and everything you've done before with Selenium to access the plugin options doesn't work anymore (here and all other links like that, thanks google team), hence this question).

Calling click() or submit() on the button doesnt work anymore. My wild guess is that it's related to 'css-build': 'shadow'. Is there a way to access the original 'id': 'paper-button' element (not the "shadowed" one)?

I've made this small python3 script to enumerate and try to find by all possible ways an element. The only one working is:

driver.find_elements_by_id("paper-button")

What's strange is that paper-button is an element, it's not the id of an element... the inspector shows this:

<paper-button id="details-button" aria-describedby="a11yAssociation" role="button" 
              tabindex="0" animated="" aria-disabled="false" elevation="0">
    Details
</paper-button>

But when I enumerate its properties, thanks to this, I get this:

{'assetpath': 'chrome://resources/polymer/v1_0/paper-button/',
 'css-build': 'shadow',
 'id': 'paper-button'}

Not only the menu makes you clic more to get the same result, but it seems they use now polymer to display this menu, thus having 'css-build': 'shadow'.

from selenium import webdriver                                                   
from selenium.webdriver.common.by import By                                      
from time import sleep                                                           
from pprint import pprint                                                        

def print_err(e):                                                                
    pass  # print(str(e).split("\n")[0])                                         


def print_ok(st, res):                                                           
    if not isinstance(res, list):                                                
        print(res)                                                               
        print("NOT A LIST : ")                                                   
        print(dir(res))                                                          
        print(st, res.is_selected())                                             
        attrs = driver.execute_script(                                           
            'var items = {}; '+                                                  
            'for (index=0; index<arguments[0].attributes.length; ++index) { '+   
            'items[arguments[0].attributes[index].name] = '+                     
            'arguments[0].attributes[index].value }; '+                          
            'return items;', res)                                                
        pprint(attrs)                                                            
        return                                                                   
    if len(res):                                                                 
        print(res)                                                               
        print([x for x in res])                                                  
        print([x.get_attribute("role") for x in res])                            
        print([x.get_attribute("describedby") for x in res])                     
        print(dir(res))                                                          

def l(desc, st, fn):                                                             
    try:                                                                         
        print("driver.{}({})...".format(desc, st))                               
        print_ok(st, fn(st))                                                     
    except Exception as e:                                                       
        print_err(e)                                                             

def search(st):                                                                  
    l("find_element_by_class_name", st, driver.find_element_by_class_name)          
    l("find_element_by_css_selector", st, driver.find_element_by_css_selector)   
    l("find_element_by_id", st, driver.find_element_by_id)                       
    l("find_element_by_link_text", st, driver.find_element_by_link_text)         
    l("find_element_by_name", st, driver.find_element_by_name)                   
    l("find_element_by_partial_link_text", st, driver.find_element_by_partial_link_text)
    l("find_element_by_tag_name", st, driver.find_element_by_tag_name)           
    l("find_element_by_xpath", st, driver.find_element_by_xpath)                 
    l("find_elements_by_class_name", st, driver.find_elements_by_class_name)        
    l("find_elements_by_css_selector", st, driver.find_elements_by_css_selector) 
    l("find_elements_by_id", st, driver.find_elements_by_id)                     
    l("find_elements_by_link_text", st, driver.find_elements_by_link_text)          
    l("find_elements_by_name", st, driver.find_elements_by_name)                 
    l("find_elements_by_partial_link_text", st, driver.find_elements_by_partial_link_text)
    l("find_elements_by_tag_name", st, driver.find_elements_by_tag_name)         


driver = webdriver.Chrome()                                                      
driver.get("chrome://extensions-frame")                                          
search("paper-button")                                                           
search("details-button")                                                         
search("#details-button")                                                        
search('//[@id="details-button"')    
Mnemonic answered 18/4, 2018 at 8:30 Comment(4)
A user interacts with the button in the shadow DOM. To get it with a CSS selector: * /deep/ #details-button. To get all the attributes: return [].map.call(arguments[0].attributes, function(e){ return {name: e.name, value: e.value}}).Boatswain
@FlorentB. Is this working with Selenium?Mnemonic
yes it works with Selenium. Though /deep/ is supported by Chrome only. Try driver.find_element_by_css_selector("* /deep/ #details-button"). If you want all the data from the extension page: driver.execute_async_script("chrome.management.getAll(arguments[0])").Boatswain
@FlorentB. May I ask you to make an answer so I can check it as valid? Thank you!Mnemonic
B
0

To get the first Detail button with a shadow piercing selector:

driver.get("chrome://extensions/")
bt_detail = driver.find_element_by_css_selector("* /deep/ #details-button")

Or to get all the information from all the extensions:

driver.get("chrome://extensions/")
data = driver.execute_async_script("chrome.management.getAll(arguments[0])")
print(data)
Boatswain answered 18/4, 2018 at 12:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.