Bug 1381519 - Find element for (partial) link text has to use rendered content. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 05 Dec 2017 13:25:09 +0100
changeset 770704 9b89ad1bc110cebb6b4c21831711bab51e03addb
parent 770228 025426a3e2e780d33bb666c78bfbaf22b3456c19
push id103488
push userbmo:hskupin@gmail.com
push dateWed, 21 Mar 2018 18:59:43 +0000
bugs1381519
milestone61.0a1
Bug 1381519 - Find element for (partial) link text has to use rendered content. To retrieve links via "link text" or "partial link text" the rendered content of the element has to be used. This can be the case for CSS transformations like "uppercase". MozReview-Commit-ID: fxaHEuWnbf
testing/marionette/element.js
testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webdriver/tests/element_retrieval/find_element.py
testing/web-platform/tests/webdriver/tests/element_retrieval/find_element_from_element.py
testing/web-platform/tests/webdriver/tests/element_retrieval/find_elements.py
testing/web-platform/tests/webdriver/tests/element_retrieval/find_elements_from_element.py
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -436,34 +436,36 @@ element.findByXPathAll = function* (docu
  *     Where in the DOM hierarchy to begin searching.
  * @param {string} linkText
  *     Link text to search for.
  *
  * @return {Iterable.<HTMLAnchorElement>}
  *     Sequence of link elements which text is <var>s</var>.
  */
 element.findByLinkText = function(startNode, linkText) {
-  return filterLinks(startNode, link => link.text.trim() === linkText);
+  return filterLinks(startNode,
+      link => atom.getElementText(link).trim() === linkText);
 };
 
 /**
  * Find all hyperlinks descendant of <var>startNode</var> which
  * link text contains <var>linkText</var>.
  *
  * @param {Element} startNode
  *     Where in the DOM hierachy to begin searching.
  * @param {string} linkText
  *     Link text to search for.
  *
  * @return {Iterable.<HTMLAnchorElement>}
  *     Iterator of link elements which text containins
  *     <var>linkText</var>.
  */
 element.findByPartialLinkText = function(startNode, linkText) {
-  return filterLinks(startNode, link => link.text.includes(linkText));
+  return filterLinks(startNode,
+      link => atom.getElementText(link).includes(linkText));
 };
 
 /**
  * Find anonymous nodes of <var>node</var>.
  *
  * @param {XULDocument} document
  *     Root node of the document.
  * @param {XULElement} node
@@ -546,25 +548,25 @@ function findElement(strategy, selector,
     case element.Strategy.TagName:
       return startNode.getElementsByTagName(selector)[0];
 
     case element.Strategy.XPath:
       return element.findByXPath(document, startNode, selector);
 
     case element.Strategy.LinkText:
       for (let link of startNode.getElementsByTagName("a")) {
-        if (link.text.trim() === selector) {
+        if (atom.getElementText(link).trim() === selector) {
           return link;
         }
       }
       return undefined;
 
     case element.Strategy.PartialLinkText:
       for (let link of startNode.getElementsByTagName("a")) {
-        if (link.text.includes(selector)) {
+        if (atom.getElementText(link).includes(selector)) {
           return link;
         }
       }
       return undefined;
 
     case element.Strategy.Selector:
       try {
         return startNode.querySelector(selector);
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
@@ -37,19 +37,16 @@ id_xhtml = inline('<p id="foo"></p>', do
 parent_child_html = inline("<div id=parent><p id=child></p></div>", doctype="html")
 parent_child_xhtml = inline('<div id="parent"><p id="child"></p></div>', doctype="xhtml")
 children_html = inline("<div><p>foo <p>bar</div>", doctype="html")
 children_xhtml = inline("<div><p>foo</p> <p>bar</p></div>", doctype="xhtml")
 class_html = inline("<p class='foo bar'>", doctype="html")
 class_xhtml = inline('<p class="foo bar"></p>', doctype="xhtml")
 name_html = inline("<p name=foo>", doctype="html")
 name_xhtml = inline('<p name="foo"></p>', doctype="xhtml")
-link_html = inline("<p><a href=#>foo bar</a>", doctype="html")
-link_html_with_trailing_space = inline("<p><a href=#>a link with a trailing space </a>")
-link_xhtml = inline('<p><a href="#">foo bar</a></p>', doctype="xhtml")
 
 
 class TestFindElementHTML(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.marionette.timeout.implicit = 0
 
     def test_id(self):
@@ -95,37 +92,16 @@ class TestFindElementHTML(MarionetteTest
         found = self.marionette.find_element(By.CSS_SELECTOR, "p")
         self.assertIsInstance(found, HTMLElement)
         self.assertEqual(el, found)
 
     def test_invalid_css_selector_should_throw(self):
         with self.assertRaises(InvalidSelectorException):
             self.marionette.find_element(By.CSS_SELECTOR, "#")
 
-    def test_link_text(self):
-        self.marionette.navigate(link_html)
-        el = self.marionette.execute_script("return document.querySelector('a')")
-        found = self.marionette.find_element(By.LINK_TEXT, "foo bar")
-        self.assertIsInstance(found, HTMLElement)
-        self.assertEqual(el, found)
-
-    def test_link_text_with_trailing_space(self):
-        self.marionette.navigate(link_html_with_trailing_space)
-        el = self.marionette.execute_script("return document.querySelector('a')")
-        found = self.marionette.find_element(By.LINK_TEXT, "a link with a trailing space")
-        self.assertIsInstance(found, HTMLElement)
-        self.assertEqual(el, found)
-
-    def test_partial_link_text(self):
-        self.marionette.navigate(link_html)
-        el = self.marionette.execute_script("return document.querySelector('a')")
-        found = self.marionette.find_element(By.PARTIAL_LINK_TEXT, "foo")
-        self.assertIsInstance(found, HTMLElement)
-        self.assertEqual(el, found)
-
     def test_xpath(self):
         self.marionette.navigate(id_html)
         el = self.marionette.execute_script("return document.querySelector('#foo')")
         found = self.marionette.find_element(By.XPATH, "id('foo')")
         self.assertIsInstance(found, HTMLElement)
         self.assertEqual(el, found)
 
     def test_not_found(self):
@@ -259,30 +235,16 @@ class TestFindElementXHTML(MarionetteTes
 
     def test_css_selector(self):
         self.marionette.navigate(children_xhtml)
         el = self.marionette.execute_script("return document.querySelector('p')")
         found = self.marionette.find_element(By.CSS_SELECTOR, "p")
         self.assertIsInstance(found, HTMLElement)
         self.assertEqual(el, found)
 
-    def test_link_text(self):
-        self.marionette.navigate(link_xhtml)
-        el = self.marionette.execute_script("return document.querySelector('a')")
-        found = self.marionette.find_element(By.LINK_TEXT, "foo bar")
-        self.assertIsInstance(found, HTMLElement)
-        self.assertEqual(el, found)
-
-    def test_partial_link_text(self):
-        self.marionette.navigate(link_xhtml)
-        el = self.marionette.execute_script("return document.querySelector('a')")
-        found = self.marionette.find_element(By.PARTIAL_LINK_TEXT, "foo")
-        self.assertIsInstance(found, HTMLElement)
-        self.assertEqual(el, found)
-
     def test_xpath(self):
         self.marionette.navigate(id_xhtml)
         el = self.marionette.execute_script("return document.querySelector('#foo')")
         found = self.marionette.find_element(By.XPATH, "id('foo')")
         self.assertIsInstance(found, HTMLElement)
         self.assertEqual(el, found)
 
     def test_css_selector_scope_does_not_start_at_rootnode(self):
@@ -342,38 +304,16 @@ class TestFindElementsHTML(MarionetteTes
         found = self.marionette.find_elements(By.CSS_SELECTOR, "p")
         self.assertItemsIsInstance(found, HTMLElement)
         self.assertSequenceEqual(els, found)
 
     def test_invalid_css_selector_should_throw(self):
         with self.assertRaises(InvalidSelectorException):
             self.marionette.find_elements(By.CSS_SELECTOR, "#")
 
-    def test_link_text(self):
-        self.marionette.navigate(link_html)
-        els = self.marionette.execute_script("return document.querySelectorAll('a')")
-        found = self.marionette.find_elements(By.LINK_TEXT, "foo bar")
-        self.assertItemsIsInstance(found, HTMLElement)
-        self.assertSequenceEqual(els, found)
-
-    def test_link_text_with_trailing_space(self):
-        self.marionette.navigate(link_html_with_trailing_space)
-        els = self.marionette.execute_script("return document.querySelectorAll('a')")
-        found = self.marionette.find_elements(By.LINK_TEXT, "a link with a trailing space")
-        self.assertItemsIsInstance(found, HTMLElement)
-        self.assertSequenceEqual(els, found)
-
-
-    def test_partial_link_text(self):
-        self.marionette.navigate(link_html)
-        els = self.marionette.execute_script("return document.querySelectorAll('a')")
-        found = self.marionette.find_elements(By.PARTIAL_LINK_TEXT, "foo")
-        self.assertItemsIsInstance(found, HTMLElement)
-        self.assertSequenceEqual(els, found)
-
     def test_xpath(self):
         self.marionette.navigate(children_html)
         els = self.marionette.execute_script("return document.querySelectorAll('p')")
         found = self.marionette.find_elements(By.XPATH, ".//p")
         self.assertItemsIsInstance(found, HTMLElement)
         self.assertSequenceEqual(els, found)
 
     def test_css_selector_scope_doesnt_start_at_rootnode(self):
@@ -450,30 +390,16 @@ class TestFindElementsXHTML(MarionetteTe
 
     def test_css_selector(self):
         self.marionette.navigate(children_xhtml)
         els = self.marionette.execute_script("return document.querySelectorAll('p')")
         found = self.marionette.find_elements(By.CSS_SELECTOR, "p")
         self.assertItemsIsInstance(found, HTMLElement)
         self.assertSequenceEqual(els, found)
 
-    def test_link_text(self):
-        self.marionette.navigate(link_xhtml)
-        els = self.marionette.execute_script("return document.querySelectorAll('a')")
-        found = self.marionette.find_elements(By.LINK_TEXT, "foo bar")
-        self.assertItemsIsInstance(found, HTMLElement)
-        self.assertSequenceEqual(els, found)
-
-    def test_partial_link_text(self):
-        self.marionette.navigate(link_xhtml)
-        els = self.marionette.execute_script("return document.querySelectorAll('a')")
-        found = self.marionette.find_elements(By.PARTIAL_LINK_TEXT, "foo")
-        self.assertItemsIsInstance(found, HTMLElement)
-        self.assertSequenceEqual(els, found)
-
     @skip("XHTML namespace not yet supported")
     def test_xpath(self):
         self.marionette.navigate(children_xhtml)
         els = self.marionette.execute_script("return document.querySelectorAll('p')")
         found = self.marionette.find_elements(By.XPATH, "//xhtml:p")
         self.assertItemsIsInstance(found, HTMLElement)
         self.assertSequenceEqual(els, found)
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -500889,37 +500889,37 @@
    "a736f68dc602c0fccab56ec5cc6234cb3298c88d",
    "support"
   ],
   "css/css-scoping/shadow-cascade-order-001.html": [
    "46913ea7e47811b11be898de5c3bd0a330ea6637",
    "testharness"
   ],
   "css/css-scoping/shadow-disabled-sheet-001.html": [
-   "a3ddf0fe7fa422f5e712b8771669be76b0e3a798",
+   "3de2d23c1b3339b964ec2c009832a3207a3b9dc4",
    "reftest"
   ],
   "css/css-scoping/shadow-fallback-dynamic-001.html": [
-   "062c99df18077a0205d0170d641b1d1e61199657",
+   "741cd9e29067a4634aa5beb6bd06afa540895d22",
    "reftest"
   ],
   "css/css-scoping/shadow-fallback-dynamic-002.html": [
-   "2f66c8bca48c2ce5c9e82c5d67b152e2d143f4c6",
+   "e9a0d8178387901404030dde1b7ae7b2842f2eca",
    "reftest"
   ],
   "css/css-scoping/shadow-fallback-dynamic-003.html": [
-   "f054b0974277fbee38a96a26559c9a15400266db",
+   "0b75fadbaee366349576e2d6f3ca8d6a49069f66",
    "reftest"
   ],
   "css/css-scoping/shadow-fallback-dynamic-004.html": [
-   "fc33527eaaa7711ecb2c7cd9523e793bce2503f2",
+   "71dcc60c4ff59690927c1575fff2eecf85ee558f",
    "reftest"
   ],
   "css/css-scoping/shadow-fallback-dynamic-005.html": [
-   "46d78b6d6931505bbc4bfc2f83e2bd0bac0d3472",
+   "ab3c3d205e59df800ba5b4217245b83685521c31",
    "reftest"
   ],
   "css/css-scoping/shadow-root-insert-into-document.html": [
    "2cee9fff35c9222074f4ef78dcfcb8a3e02bbc98",
    "reftest"
   ],
   "css/css-scoping/slotted-invalidation.html": [
    "c500e1ceba1b293d45df5f66fd89d4a5d9ceb952",
@@ -593509,29 +593509,29 @@
    "37af63203540dfe11d36fe05d74694f05c6505f2",
    "wdspec"
   ],
   "webdriver/tests/element_retrieval/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/element_retrieval/find_element.py": [
-   "699b97bd31eed625e2f0bed145aaf94c3e646853",
+   "8c9ed3ac2169f4b65a2e172a6e77b9586d83afa2",
    "wdspec"
   ],
   "webdriver/tests/element_retrieval/find_element_from_element.py": [
-   "34f356f2579391289edb31adf5b4d4eb88ffc643",
+   "ecb74fc793a35481e2aa2456544b8b8f32d08546",
    "wdspec"
   ],
   "webdriver/tests/element_retrieval/find_elements.py": [
-   "284ae53c5c94d02fb46b26dcd70af02d7917e7b4",
+   "fa8c2de1ffd12432bf14368b9c72000567ab8d20",
    "wdspec"
   ],
   "webdriver/tests/element_retrieval/find_elements_from_element.py": [
-   "b062b9f044268f0d9e092def81afae1277a91cd8",
+   "6099bab098d03c0ac17134014b0acaf0b977731a",
    "wdspec"
   ],
   "webdriver/tests/element_retrieval/get_active_element.py": [
    "7d40a7641dbf04cd78f1dba630afa2e8d80dad13",
    "wdspec"
   ],
   "webdriver/tests/element_send_keys/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
@@ -594653,17 +594653,17 @@
    "f3255ca1da5c8e81c37e658cb2dc83e1a7dc2e63",
    "testharness"
   ],
   "websockets/Create-nonAscii-protocol-string.htm": [
    "f556c52a5ba7370dfd94f57331d606b147b0932f",
    "testharness"
   ],
   "websockets/Create-on-worker-shutdown.html": [
-   "75112264efdc3b310f4ba2ab4517b7608aacf2f2",
+   "213d7b23d154b930cc20985c2a86509fcdc09a9a",
    "testharness"
   ],
   "websockets/Create-protocol-with-space.htm": [
    "923fd2681d86812d63347f995f91747ebec18a44",
    "testharness"
   ],
   "websockets/Create-protocols-repeated-case-insensitive.htm": [
    "55a3dbf713cf45900f8af2e9c9bc2f41bb447141",
--- a/testing/web-platform/tests/webdriver/tests/element_retrieval/find_element.py
+++ b/testing/web-platform/tests/webdriver/tests/element_retrieval/find_element.py
@@ -46,31 +46,65 @@ def test_closed_context(session, create_
 def test_find_element(session, using, value):
     # Step 8 - 9
     session.url = inline("<a href=# id=linkText>full link text</a>")
 
     response = find_element(session, using, value)
     assert_success(response)
 
 
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>link text</a>", "link text"),
+    ("<a href=#>&nbsp;link text&nbsp;</a>", "link text"),
+    ("<a href=#>link<br>text</a>", "link\ntext"),
+    ("<a href=#>link&amp;text</a>", "link&text"),
+    ("<a href=#>LINK TEXT</a>", "LINK TEXT"),
+    ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"),
+])
+def test_find_element_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline(document)
+
+    response = find_element(session, "link text", value)
+    assert_success(response)
+
+
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>partial link text</a>", "link"),
+    ("<a href=#>&nbsp;partial link text&nbsp;</a>", "link"),
+    ("<a href=#>partial link text</a>", "k t"),
+    ("<a href=#>partial link<br>text</a>", "k\nt"),
+    ("<a href=#>partial link&amp;text</a>", "k&t"),
+    ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"),
+    ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"),
+])
+def test_find_element_partial_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline(document)
+
+    response = find_element(session, "partial link text", value)
+    assert_success(response)
+
+
 @pytest.mark.parametrize("using,value", [("css selector", "#wontExist")])
 def test_no_element(session, using, value):
     # Step 8 - 9
     response = find_element(session, using, value)
     assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("using,value",
                          [("css selector", "#linkText"),
                           ("link text", "full link text"),
                           ("partial link text", "link text"),
                           ("tag name", "a"),
                           ("xpath", "//*[name()='a']")])
 def test_xhtml_namespace(session, using, value):
-    session.url = inline("""<a href="#" id="linkText">full link text</a>""", doctype="xhtml")
+    session.url = inline("""<a href="#" id="linkText">full link text</a>""",
+                         doctype="xhtml")
     expected = session.execute_script("return document.links[0]")
 
     response = find_element(session, using, value)
     value = assert_success(response)
     assert_same_element(session, value, expected)
 
 
 @pytest.mark.parametrize("using,value",
--- a/testing/web-platform/tests/webdriver/tests/element_retrieval/find_element_from_element.py
+++ b/testing/web-platform/tests/webdriver/tests/element_retrieval/find_element_from_element.py
@@ -26,17 +26,16 @@ def test_invalid_selector_argument(sessi
 
 def test_closed_context(session, create_window):
     # Step 5
     new_window = create_window()
     session.window_handle = new_window
     session.close()
 
     response = find_element(session, "notReal", "css selector", "foo")
-
     assert_error(response, "no such window")
 
 
 @pytest.mark.parametrize("using,value",
                          [("css selector", "#linkText"),
                           ("link text", "full link text"),
                           ("partial link text", "link text"),
                           ("tag name", "a"),
@@ -44,33 +43,69 @@ def test_closed_context(session, create_
 def test_find_element(session, using, value):
     # Step 8 - 9
     session.url = inline("<div><a href=# id=linkText>full link text</a></div>")
     element = session.find.css("div", all=False)
     response = find_element(session, element.id, using, value)
     assert_success(response)
 
 
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>link text</a>", "link text"),
+    ("<a href=#>&nbsp;link text&nbsp;</a>", "link text"),
+    ("<a href=#>link<br>text</a>", "link\ntext"),
+    ("<a href=#>link&amp;text</a>", "link&text"),
+    ("<a href=#>LINK TEXT</a>", "LINK TEXT"),
+    ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"),
+])
+def test_find_element_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline("<div>{0}</div>".format(document))
+    element = session.find.css("div", all=False)
+
+    response = find_element(session, element.id, "link text", value)
+    assert_success(response)
+
+
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>partial link text</a>", "link"),
+    ("<a href=#>&nbsp;partial link text&nbsp;</a>", "link"),
+    ("<a href=#>partial link text</a>", "k t"),
+    ("<a href=#>partial link<br>text</a>", "k\nt"),
+    ("<a href=#>partial link&amp;text</a>", "k&t"),
+    ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"),
+    ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"),
+])
+def test_find_element_partial_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline("<div>{0}</div>".format(document))
+    element = session.find.css("div", all=False)
+
+    response = find_element(session, element.id, "partial link text", value)
+    assert_success(response)
+
+
 @pytest.mark.parametrize("using,value",[("css selector", "#wontExist")])
 def test_no_element(session, using, value):
     # Step 8 - 9
     session.url = inline("<div></div>")
     element = session.find.css("div", all=False)
     response = find_element(session, element.id, using, value)
     assert_error(response, "no such element")
 
 
 @pytest.mark.parametrize("using,value",
                          [("css selector", "#linkText"),
                           ("link text", "full link text"),
                           ("partial link text", "link text"),
                           ("tag name", "a"),
                           ("xpath", "//*[name()='a']")])
 def test_xhtml_namespace(session, using, value):
-    session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""", doctype="xhtml")
+    session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""",
+                         doctype="xhtml")
     from_element = session.execute_script("""return document.querySelector("p")""")
     expected = session.execute_script("return document.links[0]")
 
     response = find_element(session, from_element.id, using, value)
     value = assert_success(response)
     assert_same_element(session, value, expected)
 
 
--- a/testing/web-platform/tests/webdriver/tests/element_retrieval/find_elements.py
+++ b/testing/web-platform/tests/webdriver/tests/element_retrieval/find_elements.py
@@ -25,20 +25,17 @@ def test_invalid_selector_argument(sessi
 
 
 def test_closed_context(session, create_window):
     # Step 5
     new_window = create_window()
     session.window_handle = new_window
     session.close()
 
-    response = session.transport.send("POST",
-                                      "session/%s/elements" % session.session_id,
-                                      {"using": "css selector", "value": "foo"})
-
+    response = find_elements(session, "css selector", "foo")
     assert_error(response, "no such window")
 
 
 @pytest.mark.parametrize("using,value",
                          [("css selector", "#linkText"),
                           ("link text", "full link text"),
                           ("partial link text", "link text"),
                           ("tag name", "a"),
@@ -47,33 +44,79 @@ def test_find_elements(session, using, v
     # Step 8 - 9
     session.url = inline("<a href=# id=linkText>full link text</a>")
 
     response = find_elements(session, using, value)
     assert_success(response)
     assert len(response.body["value"]) == 1
 
 
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>link text</a>", "link text"),
+    ("<a href=#>&nbsp;link text&nbsp;</a>", "link text"),
+    ("<a href=#>link<br>text</a>", "link\ntext"),
+    ("<a href=#>link&amp;text</a>", "link&text"),
+    ("<a href=#>LINK TEXT</a>", "LINK TEXT"),
+    ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"),
+])
+def test_find_elements_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline("<a href=#>not wanted</a><br/>{0}".format(document))
+    expected = session.execute_script("return document.links[1];")
+
+    response = find_elements(session, "link text", value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
+
+    found_element = value[0]
+    assert_same_element(session, found_element, expected)
+
+
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>partial link text</a>", "link"),
+    ("<a href=#>&nbsp;partial link text&nbsp;</a>", "link"),
+    ("<a href=#>partial link text</a>", "k t"),
+    ("<a href=#>partial link<br>text</a>", "k\nt"),
+    ("<a href=#>partial link&amp;text</a>", "k&t"),
+    ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"),
+    ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"),
+])
+def test_find_elements_partial_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline("<a href=#>not wanted</a><br/>{0}".format(document))
+    expected = session.execute_script("return document.links[1];")
+
+    response = find_elements(session, "partial link text", value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
+
+    found_element = value[0]
+    assert_same_element(session, found_element, expected)
+
+
 @pytest.mark.parametrize("using,value", [("css selector", "#wontExist")])
 def test_no_element(session, using, value):
     # Step 8 - 9
     response = find_elements(session, using, value)
     assert_success(response)
     assert response.body["value"] == []
 
 
 @pytest.mark.parametrize("using,value",
                          [("css selector", "#linkText"),
                           ("link text", "full link text"),
                           ("partial link text", "link text"),
                           ("tag name", "a"),
                           ("xpath", "//*[name()='a']")])
 def test_xhtml_namespace(session, using, value):
-    session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""", doctype="xhtml")
-    expected = session.execute_script("return document.links[0]")
+    session.url = inline("""<a href="#" id="linkText">full link text</a>""",
+                         doctype="xhtml")
+    expected = session.execute_script("return document.links[0];")
 
     response = find_elements(session, using, value)
     value = assert_success(response)
     assert isinstance(value, list)
     assert len(value) == 1
 
     found_element = value[0]
     assert_same_element(session, found_element, expected)
--- a/testing/web-platform/tests/webdriver/tests/element_retrieval/find_elements_from_element.py
+++ b/testing/web-platform/tests/webdriver/tests/element_retrieval/find_elements_from_element.py
@@ -43,33 +43,81 @@ def test_closed_context(session, create_
 def test_find_elements(session, using, value):
     # Step 8 - 9
     session.url = inline("<div><a href=# id=linkText>full link text</a></div>")
     element = session.find.css("div", all=False)
     response = find_elements(session, element.id, using, value)
     assert_success(response)
 
 
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>link text</a>", "link text"),
+    ("<a href=#>&nbsp;link text&nbsp;</a>", "link text"),
+    ("<a href=#>link<br>text</a>", "link\ntext"),
+    ("<a href=#>link&amp;text</a>", "link&text"),
+    ("<a href=#>LINK TEXT</a>", "LINK TEXT"),
+    ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"),
+])
+def test_find_elements_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline("<div><a href=#>not wanted</a><br/>{0}</div>".format(document))
+    element = session.find.css("div", all=False)
+    expected = session.execute_script("return document.links[1];")
+
+    response = find_elements(session, element.id, "link text", value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
+
+    found_element = value[0]
+    assert_same_element(session, found_element, expected)
+
+
+@pytest.mark.parametrize("document,value", [
+    ("<a href=#>partial link text</a>", "link"),
+    ("<a href=#>&nbsp;partial link text&nbsp;</a>", "link"),
+    ("<a href=#>partial link text</a>", "k t"),
+    ("<a href=#>partial link<br>text</a>", "k\nt"),
+    ("<a href=#>partial link&amp;text</a>", "k&t"),
+    ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"),
+    ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"),
+])
+def test_find_elements_partial_link_text(session, document, value):
+    # Step 8 - 9
+    session.url = inline("<div><a href=#>not wanted</a><br/>{0}</div>".format(document))
+    element = session.find.css("div", all=False)
+    expected = session.execute_script("return document.links[1];")
+
+    response = find_elements(session, element.id, "partial link text", value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
+
+    found_element = value[0]
+    assert_same_element(session, found_element, expected)
+
+
 @pytest.mark.parametrize("using,value", [("css selector", "#wontExist")])
 def test_no_element(session, using, value):
     # Step 8 - 9
     session.url = inline("<div></div>")
     element = session.find.css("div", all=False)
     response = find_elements(session, element.id, using, value)
     assert response.body["value"] == []
 
 
 @pytest.mark.parametrize("using,value",
                          [("css selector", "#linkText"),
                           ("link text", "full link text"),
                           ("partial link text", "link text"),
                           ("tag name", "a"),
                           ("xpath", "//*[name()='a']")])
 def test_xhtml_namespace(session, using, value):
-    session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""", doctype="xhtml")
+    session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""",
+                         doctype="xhtml")
     from_element = session.execute_script("""return document.querySelector("p")""")
     expected = session.execute_script("return document.links[0]")
 
     response = find_elements(session, from_element.id, using, value)
     value = assert_success(response)
     assert isinstance(value, list)
     assert len(value) == 1