I'm trying to automate the www.freeinvaders version of Space Invaders using Python and Selenium. The actual game works through a HTML5 canvas element, which is wrapped in a shadow-root.
Using the answer to this question, I'm attempting to expand the shadow-root, so I can click the canvas and 'play' the game.
My current code:
def expand_shadow_element(element):
shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)
return shadowRoot
browser = webdriver.Firefox()
browser.get("/")
#wait for element to load
el = WebDriverWait(browser, timeout=20).until(lambda d:d.find_element_by_tag_name("ruffle-player"))
time.sleep(5)
#expand the shadowroot and click the canvas
host = browser.find_element_by_tag_name("ruffle-player")
shadowRoot = expand_shadow_element(host)
canvas = shadowRoot.find_element_by_tag_name("canvas")
canvas.click()
The HTML structure of the page is like this: (abridged for legibility)
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Free Invaders</title>
</head>
<body>
<div id='full-page-container'>
<main>
<div id="game-container">
<ruffle-player>
#shadow-root (open)
<div id="container" style="visibility: visible;">
<canvas width="1600" height="760" style="touch-action: none; cursor: auto;"></canvas>
</div><!--container-->
</ruffle-player>
</div><!--game-container-->
</div><!--fullpagecontainer-->
</body>
When running the above Pyhton script, it fails on this line:
shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)
with a Javascript error:
seleniummon.exceptions.JavascriptException: Message: Cyclic object value
I know that error is supposed to mean there is a self-referencing item in the returned JSON string, but that shouldn't be the case here.
Can anyone help me by explaining why this error occurs and what course of action might alleviate the issue?
I'm using Python 3.8.5, Selenium 3.141.0 and Firefox 86.0. All of this runs on Linux Mint 20.1.
Edit I have also attempted the alternative Javascript:
shadowRoot = browser.execute_script('document.querySelector("ruffle-player").shadowRoot')
But that just returns another error:
AttributeError: 'NoneType' object has no attribute 'find_element_by_tag_name'
Which indicates it doesn't even find any objects.
I'm trying to automate the www.freeinvaders version of Space Invaders using Python and Selenium. The actual game works through a HTML5 canvas element, which is wrapped in a shadow-root.
Using the answer to this question, I'm attempting to expand the shadow-root, so I can click the canvas and 'play' the game.
My current code:
def expand_shadow_element(element):
shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)
return shadowRoot
browser = webdriver.Firefox()
browser.get("http://www.freeinvaders/")
#wait for element to load
el = WebDriverWait(browser, timeout=20).until(lambda d:d.find_element_by_tag_name("ruffle-player"))
time.sleep(5)
#expand the shadowroot and click the canvas
host = browser.find_element_by_tag_name("ruffle-player")
shadowRoot = expand_shadow_element(host)
canvas = shadowRoot.find_element_by_tag_name("canvas")
canvas.click()
The HTML structure of the page is like this: (abridged for legibility)
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Free Invaders</title>
</head>
<body>
<div id='full-page-container'>
<main>
<div id="game-container">
<ruffle-player>
#shadow-root (open)
<div id="container" style="visibility: visible;">
<canvas width="1600" height="760" style="touch-action: none; cursor: auto;"></canvas>
</div><!--container-->
</ruffle-player>
</div><!--game-container-->
</div><!--fullpagecontainer-->
</body>
When running the above Pyhton script, it fails on this line:
shadowRoot = browser.execute_script('return arguments[0].shadowRoot', element)
with a Javascript error:
selenium.mon.exceptions.JavascriptException: Message: Cyclic object value
I know that error is supposed to mean there is a self-referencing item in the returned JSON string, but that shouldn't be the case here.
Can anyone help me by explaining why this error occurs and what course of action might alleviate the issue?
I'm using Python 3.8.5, Selenium 3.141.0 and Firefox 86.0. All of this runs on Linux Mint 20.1.
Edit I have also attempted the alternative Javascript:
shadowRoot = browser.execute_script('document.querySelector("ruffle-player").shadowRoot')
But that just returns another error:
AttributeError: 'NoneType' object has no attribute 'find_element_by_tag_name'
Which indicates it doesn't even find any objects.
It seems that this is a known issue filed on bugzilla.
The geckodriver devs also say that the WebDriver spec needs to be updated. See: w3c/webdriver#350.
However, CAVAh has found and posted a workaround for this issue here.
Following the advice to return the children of shadowroot return arguments[0].shadowRoot.children
it finds 4 elements:
[<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="252a6352-4fe0-409d-a626-18456a973da5")>,
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="47d76aed-5f44-4933-9718-53267a6417bf")>,
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="d3fcad48-7cbb-4de3-a247-49d7d227e982")>,
<selenium.webdriver.firefox.webelement.FirefoxWebElement (session="1dd0fedf-1a8c-42f2-a4de-0ed7df478212", element="abb5012e-79bf-494b-b5f2-ff28dfecab0f")>]
After inspecting those, it looks like the third element contains the canvas and can be clicked on.
The following code seems to work for me:
def expand_shadow_element(element):
# return a list of elements
shadowRoot = browser.execute_script('return arguments[0].shadowRoot.children', element)
return shadowRoot
browser = webdriver.Firefox()
browser.get("http://www.freeinvaders/")
#wait for element to load
el = WebDriverWait(browser, timeout=20).until(lambda d:d.find_element_by_tag_name("ruffle-player"))
time.sleep(5)
#expand the shadowroot and click the canvas
host = browser.find_element_by_tag_name("ruffle-player")
shadowRoot = expand_shadow_element(host)
canvas = shadowRoot[2].find_element_by_tag_name("canvas")
canvas.click()
Should now be fixed in firefox 111
see https://bugzilla.mozilla/show_bug.cgi?id=1764594
You can download firefox version 111 here:
https://download-installer.cdn.mozilla/pub/firefox/releases/111.0/