diff --git a/atest/acceptance/keywords/elements.robot b/atest/acceptance/keywords/elements.robot index 95bf90734..5419baa15 100644 --- a/atest/acceptance/keywords/elements.robot +++ b/atest/acceptance/keywords/elements.robot @@ -3,6 +3,7 @@ Documentation Tests elements Test Setup Go To Page "links.html" Resource ../resource.robot Library String +Library DebugLibrary *** Test Cases *** Get Many Elements @@ -60,6 +61,92 @@ Get Element Attribute ${class}= Get Element Attribute ${second_div} class Should Be Equal ${class} Second Class +# About DOM Attributes and Properties +# ----------------------------------- +# When implementing the new `Get DOM Attirbute` and `Get Property` keywords (#1822), several +# questions were raised. Fundamentally what is the difference between a DOM attribute and +# a Property. As [1] explains "Attributes are defined by HTML. Properties are defined by the +# DOM (Document Object Model)." +# +# Below are some references which talk to some descriptions and oddities of DOM attributes +# and properties. +# +# References: +# [1] HTML attributes and DOM properties: +# https://angular.io/guide/binding-syntax#html-attribute-vs-dom-property +# [2] W3C HTML Specification - Section 13.1.2.3 Attributes: +# https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 +# [3] JavaScript.Info - Attributes and properties: +# https://javascript.info/dom-attributes-and-properties +# [4] "Which CSS properties are inherited?" - StackOverflow +# https://stackoverflow.com/questions/5612302/which-css-properties-are-inherited +# [5] MDN Web Docs: Attribute +# https://developer.mozilla.org/en-US/docs/Glossary/Attribute +# [6] MDN Web Docs: HTML attribute reference +# https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes + +Get DOM Attribute + # Test get DOM attribute + ${id}= Get DOM Attribute link:Link with id id + Should Be Equal ${id} some_id + # Test custom attribute + ${existing_custom_attr}= Get DOM Attribute id:emptyDiv data-id + Should Be Equal ${existing_custom_attr} my_id + ${doesnotexist_custom_attr}= Get DOM Attribute id:emptyDiv data-doesnotexist + Should Be Equal ${doesnotexist_custom_attr} ${None} + # Get non existing DOM Attribute + ${class}= Get DOM Attribute link:Link with id class + Should Be Equal ${class} ${NONE} + +More DOM Attributes + [Setup] Go To Page "forms/enabled_disabled_fields_form.html" + # Test get empty boolean attribute + ${disabled}= Get DOM Attribute css:input[name="disabled_input"] disabled + Should Be Equal ${disabled} true + # Test boolean attribute whose value is a string + ${disabled}= Get DOM Attribute css:input[name="disabled_password"] disabled + Should Be Equal ${disabled} true + # Test empty string as the value for the attribute + ${empty_value}= Get DOM Attribute css:input[name="disabled_password"] value + Should Be Equal ${empty_value} ${EMPTY} + # Test non-existing attribute + ${disabled}= Get DOM Attribute css:input[name="enabled_password"] disabled + Should Be Equal ${disabled} ${NONE} + +Get Property + [Setup] Go To Page "forms/enabled_disabled_fields_form.html" + ${tagName_prop}= Get Property css:input[name="readonly_empty"] tagName + Should Be Equal ${tagName_prop} INPUT + # Get a boolean property + ${isConnected}= Get Property css:input[name="readonly_empty"] isConnected + Should Be Equal ${isConnected} ${True} + # Test property which returns webelement + ${children_prop}= Get Property id:table1 children + Length Should Be ${children_prop} ${1} + ${isWebElement}= Evaluate isinstance($children_prop[0], selenium.webdriver.remote.webelement.WebElement) modules=selenium + Should Be Equal ${isWebElement} ${True} + # ToDo: need to test own versus inherited property + # ToDo: Test enumerated property + +Get "Attribute" That Is Both An DOM Attribute and Property + [Setup] Go To Page "forms/enabled_disabled_fields_form.html" + ${value_property}= Get Property css:input[name="readonly_empty"] value + ${value_attribute}= Get DOM Attribute css:input[name="readonly_empty"] value + Should Be Equal ${value_property} ${value_attribute} + +Modify "Attribute" That Is Both An DOM Attribute and Property + [Setup] Go To Page "forms/prefilled_email_form.html" + ${initial_value_property}= Get Property css:input[name="email"] value + ${initial_value_attribute}= Get DOM Attribute css:input[name="email"] value + Should Be Equal ${initial_value_property} ${initial_value_attribute} + Should Be Equal ${initial_value_attribute} Prefilled Email + Input Text css:input[name="email"] robot@robotframework.org + ${changed_value_property}= Get Property css:input[name="email"] value + ${changed_value_attribute}= Get DOM Attribute css:input[name="email"] value + Should Not Be Equal ${changed_value_property} ${changed_value_attribute} + Should Be Equal ${changed_value_attribute} Prefilled Email + Should Be Equal ${changed_value_property} robot@robotframework.org + Get Element Attribute Value Should Be Should Be Succesfull Element Attribute Value Should Be link=Absolute external link href http://www.google.com/ Element Attribute Value Should Be link=Absolute external link nothere ${None} diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py index 94a901dec..140f527df 100644 --- a/src/SeleniumLibrary/keywords/element.py +++ b/src/SeleniumLibrary/keywords/element.py @@ -409,6 +409,38 @@ def get_element_attribute( """ return self.find_element(locator).get_attribute(attribute) + @keyword + def get_dom_attribute( + self, locator: Union[WebElement, str], attribute: str + ) -> str: + """Returns the value of ``attribute`` from the element ``locator``. `Get DOM Attribute` keyword + only returns attributes declared within the element's HTML markup. If the requested attribute + is not there, the keyword returns ${None}. + + See the `Locating elements` section for details about the locator + syntax. + + Example: + | ${id}= | `Get DOM Attribute` | css:h1 | id | + + """ + return self.find_element(locator).get_dom_attribute(attribute) + + @keyword + def get_property( + self, locator: Union[WebElement, str], property: str + ) -> str: + """Returns the value of ``property`` from the element ``locator``. + + See the `Locating elements` section for details about the locator + syntax. + + Example: + | ${text_length}= | `Get Property` | css:h1 | text_length | + + """ + return self.find_element(locator).get_property(property) + @keyword def element_attribute_value_should_be( self, diff --git a/utest/test/api/test_plugins.py b/utest/test/api/test_plugins.py index 9b5a34f44..ea92654fa 100644 --- a/utest/test/api/test_plugins.py +++ b/utest/test/api/test_plugins.py @@ -22,7 +22,7 @@ def setUpClass(cls): def test_no_libraries(self): for item in [None, "None", ""]: sl = SeleniumLibrary(plugins=item) - self.assertEqual(len(sl.get_keyword_names()), 178) + self.assertEqual(len(sl.get_keyword_names()), 180) def test_parse_library(self): plugin = "path.to.MyLibrary"