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:
chrome/test
chromedriver
data
chromedriver
docs
@ -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)
|
||||
|
Reference in New Issue
Block a user