From 534ea5529f17dae339828c55a813184d1996b7d3 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Tue, 28 Oct 2014 17:37:24 -0400 Subject: [PATCH] Bug 1080764 - Add 'anon' and 'anon attribute' search strategies to marionette.find_element(), r=AutomatedTester The 'anon' search strategy maps directly to nsIXULDocument.getAnonymousNodes() whereas the 'anon attribute' strategy maps to nsIXULDocument.getAnonymousElementByAttribute(). These strategies are needed for clients who wish to find and manipulate anonymous content, typically found in the Firefox chrome. For more details, see: https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content --HG-- rename : testing/marionette/client/marionette/tests/unit/test_switch_anonymous_content.py => testing/marionette/client/marionette/tests/unit/test_anonymous_content.py --- .../client/marionette/marionette.py | 26 +++++++------ ...s_content.py => test_anonymous_content.py} | 28 ++++++++++++-- .../marionette/tests/unit/unit-tests.ini | 2 +- testing/marionette/marionette-elements.js | 37 +++++++++++++++++-- 4 files changed, 74 insertions(+), 19 deletions(-) rename testing/marionette/client/marionette/tests/unit/{test_switch_anonymous_content.py => test_anonymous_content.py} (53%) diff --git a/testing/marionette/client/marionette/marionette.py b/testing/marionette/client/marionette/marionette.py index 5e50aa26235..0b3a0882ef9 100644 --- a/testing/marionette/client/marionette/marionette.py +++ b/testing/marionette/client/marionette/marionette.py @@ -1281,14 +1281,15 @@ class Marionette(object): NoSuchElementException will be raised. :param method: The method to use to locate the element; one of: "id", - "name", "class name", "tag name", "css selector", "link text", - "partial link text" and "xpath". Note that the methods supported in - the chrome dom are only "id", "class name", "tag name" and "xpath". + "name", "class name", "tag name", "css selector", "link text", + "partial link text", "xpath", "anon" and "anon attribute". + Note that the "name", "css selector", "link text" and + "partial link test" methods are not supported in the chrome dom. :param target: The target of the search. For example, if method = - "tag", target might equal "div". If method = "id", target would be - an element id. + "tag", target might equal "div". If method = "id", target would be + an element id. :param id: If specified, search for elements only inside the element - with the specified id. + with the specified id. ''' kwargs = { 'value': target, 'using': method } if id: @@ -1307,14 +1308,15 @@ class Marionette(object): time set by set_search_timeout(). :param method: The method to use to locate the elements; one of: - "id", "name", "class name", "tag name", "css selector", "link text", - "partial link text" and "xpath". Note that the methods supported in - the chrome dom are only "id", "class name", "tag name" and "xpath". + "id", "name", "class name", "tag name", "css selector", "link text", + "partial link text", "xpath", "anon" and "anon attribute". + Note that the "name", "css selector", "link text" and + "partial link test" methods are not supported in the chrome dom. :param target: The target of the search. For example, if method = - "tag", target might equal "div". If method = "id", target would be - an element id. + "tag", target might equal "div". If method = "id", target would be + an element id. :param id: If specified, search for elements only inside the element - with the specified id. + with the specified id. ''' kwargs = { 'value': target, 'using': method } if id: diff --git a/testing/marionette/client/marionette/tests/unit/test_switch_anonymous_content.py b/testing/marionette/client/marionette/tests/unit/test_anonymous_content.py similarity index 53% rename from testing/marionette/client/marionette/tests/unit/test_switch_anonymous_content.py rename to testing/marionette/client/marionette/tests/unit/test_anonymous_content.py index 53939281dd4..3949e29d349 100644 --- a/testing/marionette/client/marionette/tests/unit/test_switch_anonymous_content.py +++ b/testing/marionette/client/marionette/tests/unit/test_anonymous_content.py @@ -2,10 +2,13 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from marionette import HTMLElement from marionette_test import MarionetteTestCase -from errors import JavascriptException, NoSuchElementException +from errors import NoSuchElementException +from expected import element_present +from wait import Wait -class TestSwitchFrameChrome(MarionetteTestCase): +class TestAnonymousContent(MarionetteTestCase): def setUp(self): MarionetteTestCase.setUp(self) self.marionette.set_context("chrome") @@ -20,7 +23,7 @@ class TestSwitchFrameChrome(MarionetteTestCase): self.marionette.switch_to_window(self.win) MarionetteTestCase.tearDown(self) - def test_switch(self): + def test_switch_to_anonymous_frame(self): self.marionette.find_element("id", "testAnonymousContentBox") anon_browser_el = self.marionette.find_element("id", "browser") self.assertTrue("test_anonymous_content.xul" in self.marionette.get_url()) @@ -28,3 +31,22 @@ class TestSwitchFrameChrome(MarionetteTestCase): self.assertTrue("test.xul" in self.marionette.get_url()) self.marionette.find_element("id", "testXulBox") self.assertRaises(NoSuchElementException, self.marionette.find_element, "id", "testAnonymousContentBox") + + def test_find_anonymous_element_by_attribute(self): + el = Wait(self.marionette).until(element_present("id", "dia")) + self.assertEquals(HTMLElement, type(el.find_element("anon attribute", {"anonid": "buttons"}))) + self.assertEquals(1, len(el.find_elements("anon attribute", {"anonid": "buttons"}))) + + with self.assertRaises(NoSuchElementException): + el.find_element("anon attribute", {"anonid": "nonexistent"}) + self.assertEquals([], el.find_elements("anon attribute", {"anonid": "nonexistent"})) + + def test_find_anonymous_children(self): + el = Wait(self.marionette).until(element_present("id", "dia")) + self.assertEquals(HTMLElement, type(el.find_element("anon", None))) + self.assertEquals(2, len(el.find_elements("anon", None))) + + el = self.marionette.find_element("id", "framebox") + with self.assertRaises(NoSuchElementException): + el.find_element("anon", None) + self.assertEquals([], el.find_elements("anon", None)) diff --git a/testing/marionette/client/marionette/tests/unit/unit-tests.ini b/testing/marionette/client/marionette/tests/unit/unit-tests.ini index e6fc4ed45a2..79e6cea8432 100644 --- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini @@ -87,7 +87,7 @@ browser = false [test_simpletest_chrome.js] [test_simpletest_timeout.js] [test_specialpowers.py] -[test_switch_anonymous_content.py] +[test_anonymous_content.py] [test_switch_frame.py] b2g = false skip-if = os == "win" # Bug 1078237 diff --git a/testing/marionette/marionette-elements.js b/testing/marionette/marionette-elements.js index 0d7ed9fa0c2..d13f9741229 100644 --- a/testing/marionette/marionette-elements.js +++ b/testing/marionette/marionette-elements.js @@ -20,7 +20,9 @@ this.EXPORTED_SYMBOLS = [ "LINK_TEXT", "PARTIAL_LINK_TEXT", "TAG", - "XPATH" + "XPATH", + "ANON", + "ANON_ATTRIBUTE" ]; const DOCUMENT_POSITION_DISCONNECTED = 1; @@ -36,6 +38,8 @@ this.LINK_TEXT = "link text"; this.PARTIAL_LINK_TEXT = "partial link text"; this.TAG = "tag name"; this.XPATH = "xpath"; +this.ANON= "anon"; +this.ANON_ATTRIBUTE = "anon attribute"; function ElementException(msg, num, stack) { this.message = msg; @@ -46,7 +50,7 @@ function ElementException(msg, num, stack) { this.ElementManager = function ElementManager(notSupported) { this.seenItems = {}; this.timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); - this.elementStrategies = [CLASS_NAME, SELECTOR, ID, NAME, LINK_TEXT, PARTIAL_LINK_TEXT, TAG, XPATH]; + this.elementStrategies = [CLASS_NAME, SELECTOR, ID, NAME, LINK_TEXT, PARTIAL_LINK_TEXT, TAG, XPATH, ANON, ANON_ATTRIBUTE]; for (let i = 0; i < notSupported.length; i++) { this.elementStrategies.splice(this.elementStrategies.indexOf(notSupported[i]), 1); } @@ -309,7 +313,14 @@ ElementManager.prototype = { return; } else { if (!searchTimeout || new Date().getTime() - startTime > searchTimeout) { - on_error("Unable to locate element: " + values.value, 7, null, command_id); + // Format message depending on strategy if necessary + let message = "Unable to locate element: " + values.value; + if (values.using == ANON) { + message = "Unable to locate anonymous children"; + } else if (values.using == ANON_ATTRIBUTE) { + message = "Unable to locate anonymous element: " + JSON.stringify(values.value); + } + on_error(message, 7, null, command_id); } else { values.time = startTime; this.timer.initWithCallback(this.find.bind(this, win, values, @@ -419,6 +430,16 @@ ElementManager.prototype = { case SELECTOR: element = startNode.querySelector(value); break; + case ANON: + element = rootNode.getAnonymousNodes(startNode); + if (element != null) { + element = element[0]; + } + break; + case ANON_ATTRIBUTE: + let attr = Object.keys(value)[0]; + element = rootNode.getAnonymousElementByAttribute(startNode, attr, value[attr]); + break; default: throw new ElementException("No such strategy", 500, null); } @@ -476,6 +497,16 @@ ElementManager.prototype = { case SELECTOR: elements = Array.slice(startNode.querySelectorAll(value)); break; + case ANON: + elements = rootNode.getAnonymousNodes(startNode) || []; + break; + case ANON_ATTRIBUTE: + let attr = Object.keys(value)[0]; + let el = rootNode.getAnonymousElementByAttribute(startNode, attr, value[attr]); + if (el != null) { + elements = [el]; + } + break; default: throw new ElementException("No such strategy", 500, null); }