0

Add GetComputedRole to ChromeDriver

* Add `GetComputedRole` to Element in ChromeDriver

Bug: chromedriver:3507
Change-Id: Id3e3dc2fa99ca05286d17323c9c03cbf4f36ece7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2416381
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: Shengfa Lin <shengfa@google.com>
Reviewed-by: John Chen <johnchen@chromium.org>
Reviewed-by: Mathias Bynens <mathias@chromium.org>
Commit-Queue: Maksim Sadym <sadym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809890}
This commit is contained in:
Maksim Sadym
2020-09-23 19:19:42 +00:00
committed by Commit Bot
parent c82d0a6531
commit 0e978688ae
10 changed files with 129 additions and 41 deletions

@ -81,6 +81,8 @@ class Command(object):
_Method.GET, '/session/:sessionId/element/:id/property/:name')
GET_ELEMENT_COMPUTED_LABEL = (
_Method.GET, '/session/:sessionId/element/:id/computedlabel')
GET_ELEMENT_COMPUTED_ROLE = (
_Method.GET, '/session/:sessionId/element/:id/computedrole')
ELEMENT_EQUALS = (
_Method.GET, '/session/:sessionId/element/:id/equals/:other')
GET_COOKIES = (_Method.GET, '/session/:sessionId/cookie')

@ -37,6 +37,9 @@ class WebElement(object):
def GetComputedLabel(self):
return self._Execute(Command.GET_ELEMENT_COMPUTED_LABEL)
def GetComputedRole(self):
return self._Execute(Command.GET_ELEMENT_COMPUTED_ROLE)
def Click(self):
self._Execute(Command.CLICK_ELEMENT)

@ -744,47 +744,20 @@ Status ExecuteGetComputedLabel(Session* session,
const std::string& element_id,
const base::DictionaryValue& params,
std::unique_ptr<base::Value>* value) {
Status status = CheckElement(element_id);
std::unique_ptr<base::Value> axNode;
Status status = GetAXNodeByElementId(session, web_view, element_id, &axNode);
if (status.IsError())
return status;
int node_id;
std::unique_ptr<base::DictionaryValue> element(CreateElement(element_id));
status = web_view->GetNodeIdByElement(session->GetCurrentFrameId(), *element,
&node_id);
if (status.IsError())
return status;
base::DictionaryValue body;
body.SetInteger("nodeId", node_id);
body.SetBoolean("fetchRelatives", false);
std::unique_ptr<base::Value> result;
status = web_view->SendCommandAndGetResult("Accessibility.getPartialAXTree",
body, &result);
if (status.IsError())
return status;
base::Optional<base::Value> nodes = result->ExtractKey("nodes");
if (!nodes)
return Status(kUnknownError, "No `nodes` found in CDP response");
base::Value::ListView nodesList = nodes->GetList();
if (nodesList.size() != 1)
return Status(kUnknownError, "`nodes` in CDP response is not a list");
base::Value* node = &nodesList[0];
// Computed label stores as `name` in the AXTree.
base::Optional<base::Value> name = node->ExtractKey("name");
if (!name) {
base::Optional<base::Value> nameNode = axNode->ExtractKey("name");
if (!nameNode) {
// No computed label found. Return empty string.
*value = std::make_unique<base::Value>("");
return Status(kOk);
}
base::Optional<base::Value> nameVal = name->ExtractKey("value");
base::Optional<base::Value> nameVal = nameNode->ExtractKey("value");
if (!nameVal)
return Status(kUnknownError,
"No name value found in the node in CDP response");
@ -794,6 +767,33 @@ Status ExecuteGetComputedLabel(Session* session,
return Status(kOk);
}
Status ExecuteGetComputedRole(Session* session,
WebView* web_view,
const std::string& element_id,
const base::DictionaryValue& params,
std::unique_ptr<base::Value>* value) {
std::unique_ptr<base::Value> axNode;
Status status = GetAXNodeByElementId(session, web_view, element_id, &axNode);
if (status.IsError())
return status;
base::Optional<base::Value> roleNode = axNode->ExtractKey("role");
if (!roleNode) {
// No computed role found. Return empty string.
*value = std::make_unique<base::Value>("");
return Status(kOk);
}
base::Optional<base::Value> roleVal = roleNode->ExtractKey("value");
if (!roleVal)
return Status(kUnknownError,
"No role value found in the node in CDP response");
*value = std::make_unique<base::Value>(std::move(*roleVal));
return Status(kOk);
}
Status ExecuteIsElementDisplayed(Session* session,
WebView* web_view,
const std::string& element_id,

@ -154,6 +154,12 @@ Status ExecuteGetComputedLabel(Session* session,
const base::DictionaryValue& params,
std::unique_ptr<base::Value>* value);
Status ExecuteGetComputedRole(Session* session,
WebView* web_view,
const std::string& element_id,
const base::DictionaryValue& params,
std::unique_ptr<base::Value>* value);
Status ExecuteIsElementDisplayed(Session* session,
WebView* web_view,
const std::string& element_id,

@ -906,3 +906,45 @@ Status GetElementLocationInViewCenter(Session* session,
*location = center_location;
return Status(kOk);
}
Status GetAXNodeByElementId(Session* session,
WebView* web_view,
const std::string& element_id,
std::unique_ptr<base::Value>* axNode) {
Status status = CheckElement(element_id);
if (status.IsError())
return status;
int node_id;
std::unique_ptr<base::DictionaryValue> element(CreateElement(element_id));
status = web_view->GetNodeIdByElement(session->GetCurrentFrameId(), *element,
&node_id);
if (status.IsError())
return status;
base::DictionaryValue body;
body.SetInteger("nodeId", node_id);
body.SetBoolean("fetchRelatives", false);
std::unique_ptr<base::Value> result;
status = web_view->SendCommandAndGetResult("Accessibility.getPartialAXTree",
body, &result);
if (status.IsError())
return status;
base::Optional<base::Value> nodes = result->ExtractKey("nodes");
if (!nodes)
return Status(kUnknownError, "No `nodes` found in CDP response");
base::Value::ListView nodesList = nodes->GetList();
if (nodesList.size() < 1)
return Status(kUnknownError, "Empty nodes list in CDP response");
if (nodesList.size() > 1)
return Status(kUnknownError, "Non-unique node in CDP response");
*axNode = std::make_unique<base::Value>(std::move(nodesList[0]));
return Status(kOk);
}

@ -164,4 +164,9 @@ Status GetElementLocationInViewCenter(Session* session,
const std::string& element_id,
WebPoint* location);
Status GetAXNodeByElementId(Session* session,
WebView* web_view,
const std::string& element_id,
std::unique_ptr<base::Value>* axNode);
#endif // CHROME_TEST_CHROMEDRIVER_ELEMENT_UTIL_H_

@ -304,6 +304,9 @@ HttpHandler::HttpHandler(
CommandMapping(kGet, "session/:sessionId/element/:id/computedlabel",
WrapToCommand("GetComputedLabel",
base::Bind(&ExecuteGetComputedLabel))),
CommandMapping(kGet, "session/:sessionId/element/:id/computedrole",
WrapToCommand("GetComputedRole",
base::Bind(&ExecuteGetComputedRole))),
CommandMapping(kPost, "session/:sessionId/element/:id/click",
WrapToCommand("ClickElement",
base::BindRepeating(&ExecuteClickElement))),

@ -538,13 +538,37 @@ class ChromeDriverTest(ChromeDriverBaseTestWithWebServer):
def testStartStop(self):
pass
def testGetComputedLabel(self):
self._driver.Load(self.GetHttpUrlForFile(
'/chromedriver/accessibility.html'))
element = self._driver.FindElement('css selector', '#first-header')
# print >> sys.stdout, "element.GetText: ", element.GetText()
computedLabel = element.GetComputedLabel()
self.assertEquals(computedLabel, 'header content')
def testGetComputedAttributes(self):
self._driver.Load(
self.GetHttpUrlForFile('/chromedriver/accessibility.html'))
firstHeaderElement = self._driver.FindElement(
'css selector', '#first-header')
self.assertEquals(firstHeaderElement.GetComputedLabel(), 'header content')
self.assertEquals(firstHeaderElement.GetComputedRole(), 'heading')
def testGetComputedAttributesForIgnoredNode(self):
self._driver.Load(
self.GetHttpUrlForFile('/chromedriver/accessibility.html'))
ignoredHeaderElement = self._driver.FindElement(
'css selector', '#ignored-header')
# GetComputedLabel for ignored node should return empty string.
self.assertEquals(ignoredHeaderElement.GetComputedLabel(), '')
self.assertEquals(ignoredHeaderElement.GetComputedRole(), 'Ignored')
def testGetComputedAttributesForUnrenderedNode(self):
self._driver.Load(
self.GetHttpUrlForFile('/chromedriver/accessibility.html'))
unrenderedHeaderElement = self._driver.FindElement(
'css selector', '#unrendered-header')
# GetComputedLabel for unrendered node should return empty string.
self.assertEquals(unrenderedHeaderElement.GetComputedLabel(), '')
self.assertEquals(unrenderedHeaderElement.GetComputedRole(), 'Ignored')
def testLoadUrl(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))

@ -1,3 +1,5 @@
<!DOCTYPE html>
<title>Test</title>
<h1 id="first-header">header content</h1>
<title>Test</title>
<h1 id="first-header">header content</h1>
<h1 id="ignored-header" role=presentation>ignored header content</h1>
<h1 id="unrendered-header" hidden>unrendered header content</h1>

@ -41,6 +41,7 @@ Below is a list of all WebDriver commands and their current support in ChromeDri
| GET | /session/{session id}/element/{element id}/rect | Get Element Rect | Complete |
| GET | /session/{session id}/element/{element id}/enabled | Is Element Enabled | Complete |
| GET | /session/{session id}/element/{element id}/computedlabel | Get Computed Label | Complete |
| GET | /session/{session id}/element/{element id}/computedrole | Get Computed Role | Complete |
| POST | /session/{session id}/element/{element id}/click | Element Click | Partially Complete | [1996](https://bugs.chromium.org/p/chromedriver/issues/detail?id=1996)
| POST | /session/{session id}/element/{element id}/clear | Element Clear | Complete |
| POST | /session/{session id}/element/{element id}/value | Element Send Keys | Partially Complete | [1999](https://bugs.chromium.org/p/chromedriver/issues/detail?id=1999)