From ba3982f3c0242394673f63e6602200eec48eb2f1 Mon Sep 17 00:00:00 2001 From: jannek76 Date: Tue, 26 Oct 2021 10:55:27 +0300 Subject: [PATCH 1/2] add strip_spaces and collapse_spaces --- .../keywords/content_assertions.robot | 16 +++++ src/SeleniumLibrary/__init__.pyi | 4 ++ src/SeleniumLibrary/keywords/element.py | 60 ++++++++++++++++++- .../test_keyword_arguments_element.py | 12 ++++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/atest/acceptance/keywords/content_assertions.robot b/atest/acceptance/keywords/content_assertions.robot index e39a52ad6..ff91a3f0c 100644 --- a/atest/acceptance/keywords/content_assertions.robot +++ b/atest/acceptance/keywords/content_assertions.robot @@ -162,14 +162,24 @@ Element Should Not Contain Element Text Should Be Element Text Should Be some_id This text is inside an identified element Element Text Should Be some_id This TEXT IS INSIDE AN IDENTIFIED ELEMENT ignore_case=True + Element Text Should Be some_id This text is inside an identified element${SPACE} strip_spaces=True + Element Text Should Be some_id This${SPACE}${SPACE} text is inside an identified element collapse_spaces=True Run Keyword And Expect Error ... The text of element 'some_id' should have been 'inside' but it was 'This text is inside an identified element'. ... Element Text Should Be some_id inside + Run Keyword And Expect Error + ... The text of element 'some_id' should have been 'This text is inside an identified element ' but it was 'This text is inside an identified element'. + ... Element Text Should Be some_id This text is inside an identified element${SPACE} strip_spaces=False + Run Keyword And Expect Error + ... The text of element 'some_id' should have been 'This${SPACE}${SPACE} text is inside an identified element' but it was 'This text is inside an identified element'. + ... Element Text Should Be some_id This${SPACE}${SPACE} text is inside an identified element collapse_spaces=False Element Text Should Not Be Element Text Should Not Be some_id Foo This text is inside an identified element Element Text Should Not Be some_id This TEXT IS INSIDE AN IDENTIFIED ELEMENT ignore_case=False Element Text Should Not Be some_id FOO This text is inside an identified element ignore_case=True + Element Text Should Not Be some_id This text is inside an identified element${SPACE} strip_spaces=False + Element Text Should Not Be some_id This text${SPACE}${SPACE} is inside an identified element collapse_spaces=False Run Keyword And Expect Error ... The text of element 'some_id' was not supposed to be 'This text is inside an identified element'. ... Element Text Should Not Be some_id This text is inside an identified element @@ -179,6 +189,12 @@ Element Text Should Not Be Run Keyword And Expect Error ... The text of element 'some_id' was not supposed to be 'THIS TEXT is inside an identified element'. ... Element Text Should Not Be some_id THIS TEXT is inside an identified element ignore_case=True + Run Keyword And Expect Error + ... The text of element 'some_id' was not supposed to be 'This text is inside an identified element '. + ... Element Text Should Not Be some_id This text is inside an identified element${SPACE} strip_spaces=True + Run Keyword And Expect Error + ... The text of element 'some_id' was not supposed to be 'This text${SPACE}${SPACE} is inside an identified element'. + ... Element Text Should Not Be some_id This text${SPACE}${SPACE} is inside an identified element collapse_spaces=True Get Text ${str} = Get Text some_id diff --git a/src/SeleniumLibrary/__init__.pyi b/src/SeleniumLibrary/__init__.pyi index 9ab35f752..5ddc711d3 100644 --- a/src/SeleniumLibrary/__init__.pyi +++ b/src/SeleniumLibrary/__init__.pyi @@ -163,6 +163,8 @@ class SeleniumLibrary: expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, + strip_spaces: bool = False, + collapse_spaces: bool = False, ): ... def element_text_should_not_be( self, @@ -170,6 +172,8 @@ class SeleniumLibrary: not_expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, + strip_spaces: bool = False, + collapse_spaces: bool = False, ): ... def execute_async_javascript( self, *code: Union[selenium.webdriver.remote.webelement.WebElement, str] diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py index 51e3710cc..e19a569e8 100644 --- a/src/SeleniumLibrary/keywords/element.py +++ b/src/SeleniumLibrary/keywords/element.py @@ -13,12 +13,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import re + from collections import namedtuple from typing import List, Optional, Tuple, Union from SeleniumLibrary.utils import is_noney from SeleniumLibrary.utils.events.event import _unwrap_eventfiring_element -from robot.utils import plural_or_not, is_truthy +from robot.utils import plural_or_not, is_truthy, is_string from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.remote.webelement import WebElement @@ -329,6 +331,8 @@ def element_text_should_be( expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, + strip_spaces: bool = False, + collapse_spaces: bool = False, ): """Verifies that element ``locator`` contains exact the text ``expected``. @@ -341,7 +345,19 @@ def element_text_should_be( The ``ignore_case`` argument can be set to True to compare case insensitive, default is False. + If ``strip_spaces`` is given a true value (see `Boolean arguments`) + and both arguments are strings, the comparison is done without leading + and trailing spaces. If ``strip_spaces`` is given a string value + ``LEADING`` or ``TRAILING`` (case-insensitive), the comparison is done + without leading or trailing spaces, respectively. + + If ``collapse_spaces`` is given a true value (see `Boolean arguments`) and both + arguments are strings, the comparison is done with all white spaces replaced by + a single space character. + ``ignore_case`` argument is new in SeleniumLibrary 3.1. + ``strip_spaces`` is new in SeleniumLibrary 5.x.x and + ``collapse_spaces`` is new in SeleniumLibrary 5.x.x. Use `Element Should Contain` if a substring match is desired. """ @@ -350,6 +366,12 @@ def element_text_should_be( if ignore_case: text = text.lower() expected = expected.lower() + if strip_spaces: + text = self._strip_spaces(text, strip_spaces) + expected = self._strip_spaces(expected, strip_spaces) + if collapse_spaces: + text = self._collapse_spaces(text) + expected = self._collapse_spaces(expected) if text != expected: if message is None: message = ( @@ -365,6 +387,8 @@ def element_text_should_not_be( not_expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, + strip_spaces: bool = False, + collapse_spaces: bool = False, ): """Verifies that element ``locator`` does not contain exact the text ``not_expected``. @@ -377,7 +401,19 @@ def element_text_should_not_be( The ``ignore_case`` argument can be set to True to compare case insensitive, default is False. - New in SeleniumLibrary 3.1.1 + If ``strip_spaces`` is given a true value (see `Boolean arguments`) + and both arguments are strings, the comparison is done without leading + and trailing spaces. If ``strip_spaces`` is given a string value + ``LEADING`` or ``TRAILING`` (case-insensitive), the comparison is done + without leading or trailing spaces, respectively. + + If ``collapse_spaces`` is given a true value (see `Boolean arguments`) and both + arguments are strings, the comparison is done with all white spaces replaced by + a single space character. + + ``ignore_case`` is new in SeleniumLibrary 3.1.1 + ``strip_spaces`` is new in SeleniumLibrary 5.x.x and + ``collapse_spaces`` is new in SeleniumLibrary 5.x.x. """ self.info( f"Verifying element '{locator}' does not contain exact text '{not_expected}'." @@ -387,11 +423,31 @@ def element_text_should_not_be( if ignore_case: text = text.lower() not_expected = not_expected.lower() + if strip_spaces: + text = self._strip_spaces(text, strip_spaces) + not_expected = self._strip_spaces(not_expected, strip_spaces) + if collapse_spaces: + text = self._collapse_spaces(text) + not_expected = self._collapse_spaces(not_expected) if text == not_expected: if message is None: message = f"The text of element '{locator}' was not supposed to be '{before_not_expected}'." raise AssertionError(message) + def _strip_spaces(self, value, strip_spaces): + if not is_string(value): + return value + if not is_string(strip_spaces): + return value.strip() if strip_spaces else value + if strip_spaces.upper() == 'LEADING': + return value.lstrip() + if strip_spaces.upper() == 'TRAILING': + return value.rstrip() + return value.strip() if is_truthy(strip_spaces) else value + + def _collapse_spaces(self, value): + return re.sub(r'\s+', ' ', value) if is_string(value) else value + @keyword def get_element_attribute( self, locator: Union[WebElement, str], attribute: str diff --git a/utest/test/keywords/test_keyword_arguments_element.py b/utest/test/keywords/test_keyword_arguments_element.py index c5223afd1..a655a1f7c 100644 --- a/utest/test/keywords/test_keyword_arguments_element.py +++ b/utest/test/keywords/test_keyword_arguments_element.py @@ -27,3 +27,15 @@ def test_element_text_should_be(element): with pytest.raises(AssertionError) as error: element.element_text_should_be(locator, "not text", "foobar") assert "foobar" in str(error.value) + + webelement.text = "text " + when(element).find_element(locator).thenReturn(webelement) + with pytest.raises(AssertionError) as error: + element.element_text_should_be(locator, "text", strip_spaces=False) + assert "should have been" in str(error.value) + + webelement.text = "testing is cool" + when(element).find_element(locator).thenReturn(webelement) + with pytest.raises(AssertionError) as error: + element.element_text_should_be(locator, "testing is cool", collapse_spaces=False) + assert "should have been" in str(error.value) From 8a79b7d9cb051359495445730adceec0c739d795 Mon Sep 17 00:00:00 2001 From: jannek76 Date: Tue, 26 Oct 2021 16:46:45 +0300 Subject: [PATCH 2/2] Fixed argument definition --- atest/acceptance/keywords/content_assertions.robot | 2 ++ src/SeleniumLibrary/__init__.pyi | 4 ++-- src/SeleniumLibrary/keywords/element.py | 4 ++-- utest/test/keywords/test_keyword_arguments_element.py | 10 ++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/atest/acceptance/keywords/content_assertions.robot b/atest/acceptance/keywords/content_assertions.robot index ff91a3f0c..cf2c55506 100644 --- a/atest/acceptance/keywords/content_assertions.robot +++ b/atest/acceptance/keywords/content_assertions.robot @@ -163,6 +163,8 @@ Element Text Should Be Element Text Should Be some_id This text is inside an identified element Element Text Should Be some_id This TEXT IS INSIDE AN IDENTIFIED ELEMENT ignore_case=True Element Text Should Be some_id This text is inside an identified element${SPACE} strip_spaces=True + Element Text Should Be some_id ${SPACE}This text is inside an identified element strip_spaces=LEADING + Element Text Should Be some_id This text is inside an identified element${SPACE} strip_spaces=TRAILING Element Text Should Be some_id This${SPACE}${SPACE} text is inside an identified element collapse_spaces=True Run Keyword And Expect Error ... The text of element 'some_id' should have been 'inside' but it was 'This text is inside an identified element'. diff --git a/src/SeleniumLibrary/__init__.pyi b/src/SeleniumLibrary/__init__.pyi index 5ddc711d3..64b1bd50f 100644 --- a/src/SeleniumLibrary/__init__.pyi +++ b/src/SeleniumLibrary/__init__.pyi @@ -163,7 +163,7 @@ class SeleniumLibrary: expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, - strip_spaces: bool = False, + strip_spaces: Union[bool, str] = False, collapse_spaces: bool = False, ): ... def element_text_should_not_be( @@ -172,7 +172,7 @@ class SeleniumLibrary: not_expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, - strip_spaces: bool = False, + strip_spaces: Union[bool, str] = False, collapse_spaces: bool = False, ): ... def execute_async_javascript( diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py index e19a569e8..6901b06bc 100644 --- a/src/SeleniumLibrary/keywords/element.py +++ b/src/SeleniumLibrary/keywords/element.py @@ -331,7 +331,7 @@ def element_text_should_be( expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, - strip_spaces: bool = False, + strip_spaces: Union[bool, str] = False, collapse_spaces: bool = False, ): """Verifies that element ``locator`` contains exact the text ``expected``. @@ -387,7 +387,7 @@ def element_text_should_not_be( not_expected: Union[None, str], message: Optional[str] = None, ignore_case: bool = False, - strip_spaces: bool = False, + strip_spaces: Union[bool, str] = False, collapse_spaces: bool = False, ): """Verifies that element ``locator`` does not contain exact the text ``not_expected``. diff --git a/utest/test/keywords/test_keyword_arguments_element.py b/utest/test/keywords/test_keyword_arguments_element.py index a655a1f7c..874b02b3f 100644 --- a/utest/test/keywords/test_keyword_arguments_element.py +++ b/utest/test/keywords/test_keyword_arguments_element.py @@ -34,6 +34,16 @@ def test_element_text_should_be(element): element.element_text_should_be(locator, "text", strip_spaces=False) assert "should have been" in str(error.value) + with pytest.raises(AssertionError) as error: + element.element_text_should_be(locator, "text", strip_spaces="LEADING") + assert "should have been" in str(error.value) + + webelement.text = " text" + when(element).find_element(locator).thenReturn(webelement) + with pytest.raises(AssertionError) as error: + element.element_text_should_be(locator, "text", strip_spaces="TRAILING") + assert "should have been" in str(error.value) + webelement.text = "testing is cool" when(element).find_element(locator).thenReturn(webelement) with pytest.raises(AssertionError) as error: