From bc88213b03d4062e1492a44358e6808f048e9c37 Mon Sep 17 00:00:00 2001 From: Richard Huang Date: Sat, 26 Mar 2016 21:49:42 -0700 Subject: [PATCH] Bugfixes, and various adjustments. - Bugfixes on issue #9 and issue #11 - Refactors form and select elements into its own module - Fix and add more unit tests - Add acceptance tests for fixed issues --- CHANGELOG.rst | 8 + doc/ExtendedSelenium2Library.html | 2 +- src/ExtendedSelenium2Library/__init__.py | 100 +----- .../keywords/__init__.py | 4 + .../keywords/extendedelement.py | 85 +++-- .../keywords/extendedformelement.py | 145 +++++++++ .../keywords/extendedselectelement.py | 132 ++++++++ .../keywords/extendedwaiting.py | 3 + src/ExtendedSelenium2Library/version.py | 2 +- test/atest/html/issue_11.html | 11 + test/atest/html/issue_3.html | 13 + test/atest/html/issue_5.html | 14 + test/atest/html/issue_8.html | 10 + test/atest/html/issue_9.html | 11 + test/atest/suites/003__issue_3_es2l.robot | 46 +++ test/atest/suites/003__issue_3_s2l.robot | 47 +++ test/atest/suites/005__issue_5.robot | 24 ++ test/atest/suites/008__issue_8.robot | 24 ++ test/atest/suites/009__issue_9.robot | 25 ++ test/atest/suites/011__issue_11.robot | 25 ++ test/utest/test_extendedelement.py | 290 +++++++++++------- test/utest/test_extendedformelement.py | 253 +++++++++++++++ test/utest/test_extendedjavascript.py | 12 +- test/utest/test_extendedselectelement.py | 108 +++++++ 24 files changed, 1164 insertions(+), 230 deletions(-) create mode 100644 src/ExtendedSelenium2Library/keywords/extendedformelement.py create mode 100644 src/ExtendedSelenium2Library/keywords/extendedselectelement.py create mode 100644 test/atest/html/issue_11.html create mode 100644 test/atest/html/issue_3.html create mode 100644 test/atest/html/issue_5.html create mode 100644 test/atest/html/issue_8.html create mode 100644 test/atest/html/issue_9.html create mode 100644 test/atest/suites/003__issue_3_es2l.robot create mode 100644 test/atest/suites/003__issue_3_s2l.robot create mode 100644 test/atest/suites/005__issue_5.robot create mode 100644 test/atest/suites/008__issue_8.robot create mode 100644 test/atest/suites/009__issue_9.robot create mode 100644 test/atest/suites/011__issue_11.robot create mode 100644 test/utest/test_extendedformelement.py create mode 100644 test/utest/test_extendedselectelement.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ecb3355..a5aa0d4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,11 @@ +0.9.0 (2016.03.26) +================== + +* Bugfixes on issue #9 and issue #11 +* Refactors form and select elements into its own module +* Fix and add more unit tests +* Add acceptance tests for fixed issues + 0.8.3 (2016.03.18) ================== diff --git a/doc/ExtendedSelenium2Library.html b/doc/ExtendedSelenium2Library.html index 2a6bfcf..fe823d6 100644 --- a/doc/ExtendedSelenium2Library.html +++ b/doc/ExtendedSelenium2Library.html @@ -468,7 +468,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a diff --git a/src/ExtendedSelenium2Library/__init__.py b/src/ExtendedSelenium2Library/__init__.py index 4e13a77..2beada6 100644 --- a/src/ExtendedSelenium2Library/__init__.py +++ b/src/ExtendedSelenium2Library/__init__.py @@ -25,7 +25,9 @@ from Selenium2Library import Selenium2Library from ExtendedSelenium2Library.decorators import inherit_docs from ExtendedSelenium2Library.keywords import ExtendedElementKeywords +from ExtendedSelenium2Library.keywords import ExtendedFormElementKeywords from ExtendedSelenium2Library.keywords import ExtendedJavascriptKeywords +from ExtendedSelenium2Library.keywords import ExtendedSelectElementKeywords from ExtendedSelenium2Library.keywords import ExtendedWaitingKeywords from ExtendedSelenium2Library.version import get_version @@ -35,7 +37,8 @@ # pylint: disable=too-many-ancestors @inherit_docs class ExtendedSelenium2Library(Selenium2Library, ExtendedElementKeywords, - ExtendedJavascriptKeywords, ExtendedWaitingKeywords): + ExtendedFormElementKeywords, ExtendedJavascriptKeywords, + ExtendedSelectElementKeywords, ExtendedWaitingKeywords): # pylint: disable=line-too-long """ExtendedSelenium2Library is a [http://goo.gl/boVQia|Selenium2 (WebDriver)] web testing library with [https://goo.gl/Kzz8Y3|AngularJS] support and @@ -168,23 +171,17 @@ def __init__(self, implicit_wait=15.0, **kwargs): self._builtin = BuiltIn() Selenium2Library.__init__(self, implicit_wait=implicit_wait, **kwargs) ExtendedElementKeywords.__init__(self) + ExtendedFormElementKeywords.__init__(self) ExtendedJavascriptKeywords.__init__(self) + ExtendedSelectElementKeywords.__init__(self) ExtendedWaitingKeywords.__init__(self) self._implicit_wait_in_secs = float(implicit_wait) if implicit_wait is not None else 15.0 self._page_ready_keyword_list = [] # pylint: disable=protected-access self._table_element_finder._element_finder = self._element_finder - # pylint: disable=arguments-differ - # pylint: disable=missing-docstring - def click_button(self, locator, skip_ready=False): - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedSelenium2Library, self).click_button(element) - if not skip_ready: - self._wait_until_page_ready() - def get_browser_logs(self): - """Returns the Javascript console logs from the browser. + """Returns the Javascript console logs from the browser. (Non Internet Explorer only). Please see [https://goo.gl/S7yvqR|Logging Preferences JSON object] to set how verbose the logging should be. (Default 'SEVERE') @@ -192,7 +189,8 @@ def get_browser_logs(self): Examples: | Get Browser Logs | """ - return self._current_browser().get_log('browser') + # IEDriverServer doesn't have log implementation yet + return [] if self._is_internet_explorer() else self._current_browser().get_log('browser') def get_location(self): # AngularJS support @@ -207,6 +205,7 @@ def get_location(self): response = self._current_browser().get_current_url() return response + # pylint: disable=arguments-differ # pylint: disable=too-many-arguments def open_browser(self, url, browser='firefox', alias=None, remote_url=False, desired_capabilities=None, ff_profile_dir=None, skip_ready=False): @@ -237,82 +236,3 @@ def remove_page_ready_keyword(self, keyword_name): | Remove Page Ready Keyword | My Keyword | """ self._page_ready_keyword_list.remove(keyword_name) - - def select_all_from_list(self, locator): - element = self._element_find(locator, True, True, 'select') - super(ExtendedSelenium2Library, self).select_all_from_list(element) - self._element_trigger_change(locator) - - def select_checkbox(self, locator): - self._info("Selecting checkbox '%s'." % locator) - element = self._get_checkbox(locator) - if not element.is_selected(): - self._select_checkbox_or_radio_button(locator) - - def select_from_list(self, locator, *items): - element = self._element_find(locator, True, True, 'select') - super(ExtendedSelenium2Library, self).select_from_list(element, *items) - self._element_trigger_change(locator) - - def select_from_list_by_index(self, locator, *indexes): - element = self._element_find(locator, True, True, 'select') - super(ExtendedSelenium2Library, self).select_from_list_by_index(element, *indexes) - self._element_trigger_change(locator) - - def select_from_list_by_label(self, locator, *labels): - element = self._element_find(locator, True, True, 'select') - super(ExtendedSelenium2Library, self).select_from_list_by_label(element, *labels) - self._element_trigger_change(locator) - - def select_from_list_by_value(self, locator, *values): - element = self._element_find(locator, True, True, 'select') - super(ExtendedSelenium2Library, self).select_from_list_by_value(element, *values) - self._element_trigger_change(locator) - - def select_radio_button(self, group_name, value): - self._info("Selecting '%s' from radio button '%s'." % (value, group_name)) - element = self._get_radio_button_with_value(group_name, value) - if not element.is_selected(): - self._select_checkbox_or_radio_button('css=input[name="%s"][value="%s"]' % - (group_name, value)) - - def submit_form(self, locator=None, skip_ready=False): - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedSelenium2Library, self).submit_form(element) - if not skip_ready: - self._wait_until_page_ready() - - def _element_trigger_change(self, locator): - """Trigger change event on target element when AngularJS is ready.""" - self._wait_until_page_ready(locator, - skip_stale_check=True, - prefix='var cb=arguments[arguments.length-1];' - 'var el=arguments[0];if(window.angular){', - handler='function(){$(el).trigger(\'change\').' - 'trigger(\'focusout\');cb(true)}') - - def _input_text_into_text_field(self, locator, text, skip_ready=False): - """Send keys to text field with AngularJS synchronization.""" - element = self._element_find(locator, True, True) - if element is None: - raise AssertionError("Element '%s' not found." % locator) - element.clear() - element.send_keys(text) - if not skip_ready: - self._wait_until_page_ready(locator, - skip_stale_check=True, - prefix='var cb=arguments[arguments.length-1];' - 'var el=arguments[0];if(window.angular){', - handler='function(){$(el).trigger(\'change\').' - 'trigger(\'focusout\');cb(true)}') - - def _select_checkbox_or_radio_button(self, locator): - """Select checkbox or radio button with AngularJS support.""" - self._wait_until_page_ready(locator, - skip_stale_check=True, - prefix='var cb=arguments[arguments.length-1];' - 'var el=arguments[0];if(window.angular){', - handler='function(){angular.element(el).' - 'prop(\'checked\',true).triggerHandler(\'click\');' - 'cb(true)}', - suffix='}else{el.click();cb(false)}') diff --git a/src/ExtendedSelenium2Library/keywords/__init__.py b/src/ExtendedSelenium2Library/keywords/__init__.py index fdea79a..fa4e7b9 100644 --- a/src/ExtendedSelenium2Library/keywords/__init__.py +++ b/src/ExtendedSelenium2Library/keywords/__init__.py @@ -22,11 +22,15 @@ """ from ExtendedSelenium2Library.keywords.extendedelement import ExtendedElementKeywords +from ExtendedSelenium2Library.keywords.extendedformelement import ExtendedFormElementKeywords from ExtendedSelenium2Library.keywords.extendedjavascript import ExtendedJavascriptKeywords +from ExtendedSelenium2Library.keywords.extendedselectelement import ExtendedSelectElementKeywords from ExtendedSelenium2Library.keywords.extendedwaiting import ExtendedWaitingKeywords __all__ = [ 'ExtendedElementKeywords', + 'ExtendedFormElementKeywords', 'ExtendedJavascriptKeywords', + 'ExtendedSelectElementKeywords', 'ExtendedWaitingKeywords', ] diff --git a/src/ExtendedSelenium2Library/keywords/extendedelement.py b/src/ExtendedSelenium2Library/keywords/extendedelement.py index 9a870db..37fb2d8 100644 --- a/src/ExtendedSelenium2Library/keywords/extendedelement.py +++ b/src/ExtendedSelenium2Library/keywords/extendedelement.py @@ -21,19 +21,23 @@ Extended Selenium2 Library - a web testing library with AngularJS support. """ +from robot.api import logger +from selenium.webdriver.common.action_chains import ActionChains +from selenium.webdriver.remote.webelement import WebElement from Selenium2Library.keywords import _ElementKeywords from ExtendedSelenium2Library.locators import ExtendedElementFinder class ExtendedElementKeywords(_ElementKeywords): - """ExtendedElementKeywords are web element related execution in the requested browser.""" + """ExtendedElementKeywords are web element execution in the requested browser.""" + def __init__(self): super(ExtendedElementKeywords, self).__init__() self._element_finder = ExtendedElementFinder() # pylint: disable=arguments-differ def click_element(self, locator, skip_ready=False): - """Click element identified by ``locator``. + """Clicks an element identified by ``locator``. Arguments: - ``locator``: The locator to find requested element. Key attributes for @@ -45,14 +49,15 @@ def click_element(self, locator, skip_ready=False): | Click Element | css=div.class | | Click Element | css=div.class | True | """ - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedElementKeywords, self).click_element(element) + # pylint: disable=no-member + self._info("Clicking element '%s'." % locator) + self._get_element_and_scroll_into_view_on_iexplore(locator).click() if not skip_ready: # pylint: disable=no-member self._wait_until_page_ready() def click_element_at_coordinates(self, locator, xoffset, yoffset, skip_ready=False): - """Click element identified by ``locator`` at x/y coordinates of the element. + """Clicks an element identified by ``locator`` at x/y coordinates of the element. Cursor is moved at the center of the element and x/y coordinates are calculated from that point. @@ -68,15 +73,19 @@ def click_element_at_coordinates(self, locator, xoffset, yoffset, skip_ready=Fal | Click Element At Coordinates | css=div.class | 0 | 0 | | Click Element At Coordinates | css=div.class | 0 | 0 | True | """ - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedElementKeywords, self). \ - click_element_at_coordinates(element, xoffset, yoffset) + # pylint: disable=no-member + self._info("Clicking element '%s' in coordinates '%s', '%s'." % + (locator, xoffset, yoffset)) + element = self._get_element_and_scroll_into_view_on_iexplore(locator) + # pylint: disable=no-member + ActionChains(self._current_browser()).move_to_element(element). \ + move_by_offset(xoffset, yoffset).click().perform() if not skip_ready: # pylint: disable=no-member self._wait_until_page_ready() def click_image(self, locator, skip_ready=False): - """Click an image identified by ``locator``. + """Clicks an image identified by ``locator``. Arguments: - ``locator``: The locator to find requested image. Key attributes for @@ -85,17 +94,22 @@ def click_image(self, locator, skip_ready=False): - ``skip_ready``: A boolean flag to skip the wait for page ready. (Default False) Examples: - | Click Image | css=div.class | - | Click Image | css=div.class | True | + | Click Image | css=img.class | + | Click Image | css=img.class | True | """ - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedElementKeywords, self).click_image(element) + # pylint: disable=no-member + self._info("Clicking image '%s'." % locator) + element = self._get_element_and_scroll_into_view_on_iexplore(locator, False, 'image') + if element is None: + # A form may have an image as it's submit trigger. + element = self._get_element_and_scroll_into_view_on_iexplore(locator, True, 'input') + element.click() if not skip_ready: # pylint: disable=no-member self._wait_until_page_ready() def click_link(self, locator, skip_ready=False): - """Click a link identified by ``locator``. + """Clicks a link identified by ``locator``. Arguments: - ``locator``: The locator to find requested link. Key attributes for @@ -104,17 +118,18 @@ def click_link(self, locator, skip_ready=False): - ``skip_ready``: A boolean flag to skip the wait for page ready. (Default False) Examples: - | Click Link | css=div.class | - | Click Link | css=div.class | True | + | Click Link | css=a.class | + | Click Link | css=a.class | True | """ - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedElementKeywords, self).click_link(element) + # pylint: disable=no-member + self._info("Clicking link '%s'." % locator) + self._get_element_and_scroll_into_view_on_iexplore(locator, tag='a').click() if not skip_ready: # pylint: disable=no-member self._wait_until_page_ready() def double_click_element(self, locator, skip_ready=False): - """Double click element identified by ``locator``. + """Double clicks an element identified by ``locator``. Arguments: - ``locator``: The locator to find requested element. Key attributes for @@ -126,8 +141,11 @@ def double_click_element(self, locator, skip_ready=False): | Double Click Element | css=div.class | | Double Click Element | css=div.class | True | """ - element = self._scroll_into_view_on_internet_explorer(locator) - super(ExtendedElementKeywords, self).double_click_element(element) + # pylint: disable=no-member + self._info("Double clicking element '%s'." % locator) + element = self._get_element_and_scroll_into_view_on_iexplore(locator) + # pylint: disable=no-member + ActionChains(self._current_browser()).double_click(element).perform() if not skip_ready: # pylint: disable=no-member self._wait_until_page_ready() @@ -187,7 +205,7 @@ def is_element_visible(self, locator): return self._is_visible(locator) def scroll_element_into_view(self, locator): - """Scroll element from given ``locator`` into view. + """Scrolls an element from given ``locator`` into view. Arguments: - ``locator``: The locator to find requested element. Key attributes for @@ -197,9 +215,11 @@ def scroll_element_into_view(self, locator): Examples: | Scroll Element Into View | css=div.class | """ - element = self._element_find(locator, True, True) - if element is None: - raise AssertionError("Element '%s' not found." % locator) + if isinstance(locator, WebElement): + element = locator + else: + logger.info("Scrolling element '%s' into view." % locator) + element = self._element_find(locator, True, True) script = 'arguments[0].scrollIntoView()' # pylint: disable=no-member self._current_browser().execute_script(script, element) @@ -210,15 +230,16 @@ def _get_browser_name(self): # pylint: disable=no-member return self._current_browser().capabilities['browserName'].strip().lower() + def _get_element_and_scroll_into_view_on_iexplore(self, locator, required=True, tag=None): + """Scrolls a target element into view. (Internet Explorer only).""" + element = self._element_find(locator, True, required, tag) + if element and self._is_internet_explorer(): + self.scroll_element_into_view(element) + return element + def _is_internet_explorer(self, browser_name=None): """Returns true if current browser is Internet Explorer.""" if not browser_name: browser_name = self._get_browser_name() + browser_name = browser_name.replace(' ', '') return browser_name == 'internetexplorer' or browser_name == 'ie' - - def _scroll_into_view_on_internet_explorer(self, locator): - """Scroll target element into view. (Internet Explorer only).""" - element = self._element_find(locator, True, True) - if self._is_internet_explorer(): - self.scroll_element_into_view(element) - return element diff --git a/src/ExtendedSelenium2Library/keywords/extendedformelement.py b/src/ExtendedSelenium2Library/keywords/extendedformelement.py new file mode 100644 index 0000000..43b2c48 --- /dev/null +++ b/src/ExtendedSelenium2Library/keywords/extendedformelement.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" +Extended Selenium2 Library - a web testing library with AngularJS support. +""" + +from Selenium2Library.keywords import _FormElementKeywords + + +class ExtendedFormElementKeywords(_FormElementKeywords): + """ExtendedFormElementKeywords are form element execution in the requested browser.""" + + def __init__(self): + super(ExtendedFormElementKeywords, self).__init__() + + # pylint: disable=arguments-differ + def click_button(self, locator, skip_ready=False): + """Clicks a button identified by ``locator``. + + Arguments: + - ``locator``: The locator to find requested button. Key attributes for + arbitrary buttons are ``id``, ``name``, and ``value``. + See `introduction` for details about locating elements. + - ``skip_ready``: A boolean flag to skip the wait for page ready. (Default False) + + Examples: + | Click Button | css=button.class | + | Click Button | css=button.class | True | + """ + # pylint: disable=no-member + self._info("Clicking button '%s'." % locator) + # pylint: disable=no-member + element = self._get_element_and_scroll_into_view_on_iexplore(locator, False, 'input') + if element is None: + # pylint: disable=no-member + element = self._get_element_and_scroll_into_view_on_iexplore(locator, True, 'button') + element.click() + if not skip_ready: + # pylint: disable=no-member + self._wait_until_page_ready() + + def select_checkbox(self, locator): + """Selects checkbox identified by ``locator``. + Does nothing if checkbox is already selected. + + Arguments: + - ``locator``: The locator to find requested checkbox. Key attributes for + arbitrary checkboxes are ``id``, and ``name``. + See `introduction` for details about locating elements. + + Examples: + | Select Checkbox | css=input[type="checkbox"] | + """ + # pylint: disable=no-member + self._info("Selecting checkbox '%s'." % locator) + element = self._get_checkbox(locator) + if not element.is_selected(): + self._select_checkbox_or_radio_button(locator) + + def select_radio_button(self, group_name, value): + """Sets selection of radio button group identified by ``group_name`` to ``value``. + The XPath used to locate the correct radio button and it looks like this: + //input[@type='radio' and @name='group_name' and (@value='value' or @id='value')] + + Arguments: + - ``group_name``: The name of the radio input + - ``value``: The value attribute or the id attribute + + Examples: + | # Matches HTML like XL | + | Select Radio Button | size | XL | + | # Matches HTML like XL | + | Select Radio Button | size | sizeXL | + """ + # pylint: disable=no-member + self._info("Selecting '%s' from radio button '%s'." % (value, group_name)) + element = self._get_radio_button_with_value(group_name, value) + if not element.is_selected(): + self._select_checkbox_or_radio_button('css=input[name="%s"][value="%s"]' % + (group_name, value)) + + # pylint: disable=arguments-differ + def submit_form(self, locator=None, skip_ready=False): + """Submits a form identified by `locator`. + + Arguments: + - ``locator``: The locator to find requested form. If ``locator`` is empty, + first form in the page will be submitted. Key attributes for + arbitrary forms are ``id``, and ``name``. + See `introduction` for details about locating elements. + - ``skip_ready``: A boolean flag to skip the wait for page ready. (Default False) + + Examples: + | Submit Form | css=form.class | + | Submit Form | css=form.class | True | + """ + if not locator: + locator = 'xpath=//form' + # pylint: disable=no-member + self._info("Submitting form '%s'." % locator) + # pylint: disable=no-member + self._get_element_and_scroll_into_view_on_iexplore(locator, tag='form').submit() + if not skip_ready: + # pylint: disable=no-member + self._wait_until_page_ready() + + # pylint: disable=arguments-differ + def _input_text_into_text_field(self, locator, text, skip_ready=False): + """Send keys to text field with AngularJS synchronization.""" + # pylint: disable=no-member + element = self._element_find(locator, True, True) + element.clear() + element.send_keys(text) + if not skip_ready: + # pylint: disable=no-member + self._element_trigger_change(locator) + + def _select_checkbox_or_radio_button(self, locator): + """Select checkbox or radio button with AngularJS support.""" + # pylint: disable=no-member + self._wait_until_page_ready(locator, + skip_stale_check=True, + prefix='var cb=arguments[arguments.length-1];' + 'var el=arguments[0];if(window.angular){', + handler='function(){angular.element(el).' + 'prop(\'checked\',true).triggerHandler(\'click\');' + 'cb(true)}', + suffix='}else{el.click();cb(false)}') diff --git a/src/ExtendedSelenium2Library/keywords/extendedselectelement.py b/src/ExtendedSelenium2Library/keywords/extendedselectelement.py new file mode 100644 index 0000000..61b9a6b --- /dev/null +++ b/src/ExtendedSelenium2Library/keywords/extendedselectelement.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" +Extended Selenium2 Library - a web testing library with AngularJS support. +""" + +from Selenium2Library.keywords import _SelectElementKeywords + + +class ExtendedSelectElementKeywords(_SelectElementKeywords): + """ExtendedSelectElementKeywords are select element execution in the requested browser.""" + + def __init__(self): + super(ExtendedSelectElementKeywords, self).__init__() + + def select_all_from_list(self, locator): + """Selects all values from multi-select list identified by ``locator``. + + Arguments: + - ``locator``: The locator to find requested select list. Key attributes for + select lists are ``id`` and ``name``. See `introduction` for + details about locating elements. + + Examples: + | Select All From List | css=select.class | + """ + super(ExtendedSelectElementKeywords, self).select_all_from_list(locator) + self._element_trigger_change(locator) + + def select_from_list(self, locator, *items): + """Selects ``*items`` from list identified by ``locator`` + + If more than one value is given for a single-selection list, the last + value will be selected. If the target list is a multi-selection list, + and ``*items`` is an empty list, all values of the list will be selected. + + It's faster to use 'by index/value/label' keywords. + + An exception is raised for a single-selection list if the last + value does not exist in the list and a warning for all other non- + existing items. For a multi-selection list, an exception is raised + for any and all non-existing values. + + Select list keywords work on both lists and combo boxes. + + Arguments: + - ``locator``: The locator to find requested select list. Key attributes for + select lists are ``id`` and ``name``. See `introduction` for + details about locating elements. + - ``*items``: A list of items where it will be use to try to select + by value then by label. + + Examples: + | Select From List | css=select.class | item | + """ + super(ExtendedSelectElementKeywords, self).select_from_list(locator, *items) + self._element_trigger_change(locator) + + def select_from_list_by_index(self, locator, *indexes): + """Selects ``*indexes`` from list identified by ``locator``. + Select list keywords work on both lists and combo boxes. + + Arguments: + - ``locator``: The locator to find requested select list. Key attributes for + select lists are ``id`` and ``name``. See `introduction` for + details about locating elements. + - ``*indexes``: A list of indexes to be selected. + + Examples: + | Select From List By Index | css=select.class | index | + """ + super(ExtendedSelectElementKeywords, self).select_from_list_by_index(locator, *indexes) + self._element_trigger_change(locator) + + def select_from_list_by_label(self, locator, *labels): + """Selects ``*labels`` from list identified by ``locator``. + Select list keywords work on both lists and combo boxes. + + Arguments: + - ``locator``: The locator to find requested select list. Key attributes for + select lists are ``id`` and ``name``. See `introduction` for + details about locating elements. + - ``*labels``: A list of labels to be selected. + + Examples: + | Select From List By Label | css=select.class | label | + """ + super(ExtendedSelectElementKeywords, self).select_from_list_by_label(locator, *labels) + self._element_trigger_change(locator) + + def select_from_list_by_value(self, locator, *values): + """Selects ``*values`` from list identified by ``locator``. + Select list keywords work on both lists and combo boxes. + + Arguments: + - ``locator``: The locator to find requested select list. Key attributes for + select lists are ``id`` and ``name``. See `introduction` for + details about locating elements. + - ``*values``: A list of values to be selected. + + Examples: + | Select From List By Value | css=select.class | value | + """ + super(ExtendedSelectElementKeywords, self).select_from_list_by_value(locator, *values) + self._element_trigger_change(locator) + + def _element_trigger_change(self, locator): + """Trigger change event on target element when AngularJS is ready.""" + # pylint: disable=no-member + self._wait_until_page_ready(locator, + skip_stale_check=True, + prefix='var cb=arguments[arguments.length-1];' + 'var el=arguments[0];if(window.angular){', + handler='function(){$(el).trigger(\'change\').' + 'trigger(\'focusout\');cb(true)}') diff --git a/src/ExtendedSelenium2Library/keywords/extendedwaiting.py b/src/ExtendedSelenium2Library/keywords/extendedwaiting.py index c46e47b..c5ef05d 100644 --- a/src/ExtendedSelenium2Library/keywords/extendedwaiting.py +++ b/src/ExtendedSelenium2Library/keywords/extendedwaiting.py @@ -160,6 +160,7 @@ def wait_until_angular_ready(self, timeout=None, error=None): except TimeoutException: # prevent double wait pass + # pylint: disable=bare-except except: self._debug(exc_info()[0]) # still inflight, second chance. let the browser take a deep breath... @@ -167,6 +168,7 @@ def wait_until_angular_ready(self, timeout=None, error=None): try: WebDriverWait(browser, timeout, self._inputs['poll_frequency']).\ until(lambda driver: driver.execute_async_script(script), error) + # pylint: disable=bare-except except: # instead of halting the process because AngularJS is not ready # in , we try our luck... @@ -338,6 +340,7 @@ def _wait_until_html_ready(self, browser, timeout): # pylint: disable=no-member WebDriverWait(None, timeout, self._inputs['poll_frequency']).\ until_not(staleness_of(browser.find_element_by_tag_name('html')), '') + # pylint: disable=bare-except except: # instead of halting the process because document is not ready # in , we try our luck... diff --git a/src/ExtendedSelenium2Library/version.py b/src/ExtendedSelenium2Library/version.py index c7a3dce..ed6e7e4 100644 --- a/src/ExtendedSelenium2Library/version.py +++ b/src/ExtendedSelenium2Library/version.py @@ -21,7 +21,7 @@ Extended Selenium2 Library - a web testing library with AngularJS support. """ -VERSION = '0.8.3' +VERSION = '0.9.0' def get_version(): diff --git a/test/atest/html/issue_11.html b/test/atest/html/issue_11.html new file mode 100644 index 0000000..05166da --- /dev/null +++ b/test/atest/html/issue_11.html @@ -0,0 +1,11 @@ + + + + Issue #11 + + +

Hello!

+
+ + + diff --git a/test/atest/html/issue_3.html b/test/atest/html/issue_3.html new file mode 100644 index 0000000..1fac984 --- /dev/null +++ b/test/atest/html/issue_3.html @@ -0,0 +1,13 @@ + + + + Issue #3 + + + + +

Hello!

+
+ Delete + + diff --git a/test/atest/html/issue_5.html b/test/atest/html/issue_5.html new file mode 100644 index 0000000..32fd265 --- /dev/null +++ b/test/atest/html/issue_5.html @@ -0,0 +1,14 @@ + + + + Issue #5 + + + +
+ + + diff --git a/test/atest/html/issue_8.html b/test/atest/html/issue_8.html new file mode 100644 index 0000000..a030a55 --- /dev/null +++ b/test/atest/html/issue_8.html @@ -0,0 +1,10 @@ + + + + Issue #8 + + + +
+ + diff --git a/test/atest/html/issue_9.html b/test/atest/html/issue_9.html new file mode 100644 index 0000000..2836140 --- /dev/null +++ b/test/atest/html/issue_9.html @@ -0,0 +1,11 @@ + + + + Issue #9 + + +

Hello!

+
+ Get Teapot + + diff --git a/test/atest/suites/003__issue_3_es2l.robot b/test/atest/suites/003__issue_3_es2l.robot new file mode 100644 index 0000000..929eaf5 --- /dev/null +++ b/test/atest/suites/003__issue_3_es2l.robot @@ -0,0 +1,46 @@ +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +*** Settings *** +Library ${CURDIR}/../../../src/ExtendedSelenium2Library +Suite Setup Suite Setup +Suite Teardown Close Browser +Test Setup Test Setup + +*** Test Cases *** +Test ES2L Without Confirm Action + [Documentation] Click link without confirm action in ES2L + ${error} = Run Keyword And Expect Error * + ... Page Should Contain teapot + Should Contain ${error} UnexpectedAlertPresentException + Should Contain ${error} WebDriverException + +Test ES2L With Confirm Action + [Documentation] Click link with confirm action in ES2L + Confirm Action + Page Should Contain teapot + +*** Keywords *** +Suite Setup + [Documentation] Suite setup for all ES2L test cases + Open Browser file://${CURDIR}/../html/issue_3.html ff + Set Selenium Implicit Wait 0 + Choose Ok On Next Confirmation + +Test Setup + [Documentation] Test setup for ES2L test cases + Go Back + Click Link Delete skip_ready=${true} diff --git a/test/atest/suites/003__issue_3_s2l.robot b/test/atest/suites/003__issue_3_s2l.robot new file mode 100644 index 0000000..991f9d3 --- /dev/null +++ b/test/atest/suites/003__issue_3_s2l.robot @@ -0,0 +1,47 @@ +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +*** Settings *** +Library ${CURDIR}/../../../src/ExtendedSelenium2Library +Suite Setup Suite Setup +Suite Teardown Close Browser +Test Setup Test Setup + +*** Test Cases *** +Test S2L Without Confirm Action + [Documentation] Click link without confirm action in S2L + ${error} = Run Keyword And Expect Error * + ... Page Should Contain teapot + Should Contain ${error} UnexpectedAlertPresentException + Should Contain ${error} WebDriverException + +Test S2L With Confirm Action + [Documentation] Click link with confirm action in S2L + Confirm Action + Page Should Contain teapot + +*** Keywords *** +Suite Setup + [Documentation] Suite setup for all S2L test cases + Import Library Selenium2Library + Set Library Search Order Selenium2Library + Open Browser file://${CURDIR}/../html/issue_3.html ff + Choose Ok On Next Confirmation + +Test Setup + [Documentation] Test setup for S2L test cases + Go Back + Click Link Delete diff --git a/test/atest/suites/005__issue_5.robot b/test/atest/suites/005__issue_5.robot new file mode 100644 index 0000000..665ed90 --- /dev/null +++ b/test/atest/suites/005__issue_5.robot @@ -0,0 +1,24 @@ +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +*** Settings *** +Library ${CURDIR}/../../../src/ExtendedSelenium2Library +Test Teardown Close Browser + +*** Test Cases *** +Test ES2L Undefined Interdimensional Cartography Injector + [Documentation] Should find the injector for AngularJS 1.2.8 + Open Browser file://${CURDIR}/../html/issue_5.html firefox
 diff --git a/test/atest/suites/008__issue_8.robot b/test/atest/suites/008__issue_8.robot new file mode 100644 index 0000000..9e7d90c --- /dev/null +++ b/test/atest/suites/008__issue_8.robot @@ -0,0 +1,24 @@ +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +*** Settings *** +Library ${CURDIR}/../../../src/ExtendedSelenium2Library +Test Teardown Close Browser + +*** Test Cases *** +Test ES2L Undefined Emoji Clairvoyance Injector + [Documentation] Should find the injector for AngularJS 1.2.7 + Open Browser file://${CURDIR}/../html/issue_8.html firefox
 diff --git a/test/atest/suites/009__issue_9.robot b/test/atest/suites/009__issue_9.robot new file mode 100644 index 0000000..94f9348 --- /dev/null +++ b/test/atest/suites/009__issue_9.robot @@ -0,0 +1,25 @@ +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +*** Settings *** +Library ${CURDIR}/../../../src/ExtendedSelenium2Library +Test Teardown Close Browser + +*** Test Cases *** +Test ES2L Click Element + [Documentation] Should log locator during click element + [Setup] Open Browser file://${CURDIR}/../html/issue_9.html firefox + Click Element link=Get Teapot diff --git a/test/atest/suites/011__issue_11.robot b/test/atest/suites/011__issue_11.robot new file mode 100644 index 0000000..a426eb4 --- /dev/null +++ b/test/atest/suites/011__issue_11.robot @@ -0,0 +1,25 @@ +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +*** Settings *** +Library ${CURDIR}/../../../src/ExtendedSelenium2Library +Test Teardown Close Browser + +*** Test Cases *** +Test ES2L Click Button + [Documentation] Should be able to find the button using default locator + [Setup] Open Browser file://${CURDIR}/../html/issue_11.html firefox + Click Button Get Teapot diff --git a/test/utest/test_extendedelement.py b/test/utest/test_extendedelement.py index cf97f3d..5286c43 100644 --- a/test/utest/test_extendedelement.py +++ b/test/utest/test_extendedelement.py @@ -23,11 +23,11 @@ from sys import path path.append('src') +import unittest +import mock from ExtendedSelenium2Library.keywords import ExtendedElementKeywords from selenium.webdriver.remote.webelement import WebElement from Selenium2Library.keywords import _ElementKeywords -import mock -import unittest class ExtendedElementTests(unittest.TestCase): @@ -35,121 +35,191 @@ class ExtendedElementTests(unittest.TestCase): def setUp(self): """Instantiate the extended element class.""" + self.driver = mock.Mock() + self.driver.session_id = 'session' self.element = ExtendedElementKeywords() + # pylint: disable=protected-access self.element._current_browser = mock.Mock() + self.element._info = mock.Mock() self.element._wait_until_page_ready = mock.Mock() self.locator = 'css=.selector' self.locator_attribute = 'css=.selector@class' self.locator_class = 'selector' - self.web_element = WebElement('element', False) + self.web_element = WebElement(self.driver, 'element', False) + self.web_element.click = mock.Mock() def test_should_inherit_keywords(self): """Extended element instance should inherit Selenium2 element instances.""" self.assertIsInstance(self.element, _ElementKeywords) - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement._ElementKeywords.click_element") - def test_should_click_element(self, mock_click_element): + def test_should_click_element(self): """Should click an element.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_element(self.locator) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_element.assert_called_with(self.web_element) + self.element._info.assert_called_with("Clicking element '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.assert_called_with(self.locator) + self.web_element.click.assert_called_with() self.element._wait_until_page_ready.assert_called_with() - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement._ElementKeywords.click_element") - def test_should_click_elementi_and_skip_ready(self, mock_click_element): + def test_should_click_element_and_skip_ready(self): """Should click an element with skip_ready.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_element(self.locator, True) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_element.assert_called_with(self.web_element) + self.element._info.assert_called_with("Clicking element '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.assert_called_with(self.locator) + self.web_element.click.assert_called_with() self.assertFalse(self.element._wait_until_page_ready.called) - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement." - "_ElementKeywords.click_element_at_coordinates") - def test_should_click_element_at_coordinates(self, mock_click_element_at_coordinates): + @mock.patch("ExtendedSelenium2Library.keywords.extendedelement.ActionChains") + def test_should_click_element_at_coordinates(self, mock_action_chains): """Should click an element at given coordinates.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_element_at_coordinates(self.locator, 0, 0) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_element_at_coordinates.assert_called_with(self.web_element, 0, 0) + self.element._info.assert_called_with("Clicking element '%s' in coordinates '%s', '%s'." % + (self.locator, 0, 0)) + self.element._get_element_and_scroll_into_view_on_iexplore.assert_called_with(self.locator) + mock_action_chains.assert_called_with(self.element._current_browser()) + action_chains = mock_action_chains(self.element._current_browser()) + action_chains.move_to_element.assert_called_with(self.web_element) + move_to_element = action_chains.move_to_element(self.web_element) + move_to_element.move_by_offset.assert_called_with(0, 0) + move_by_offset = move_to_element.move_by_offset(0, 0) + move_by_offset.click.assert_called_with() + click = move_by_offset.click() + click.perform.assert_called_with() self.element._wait_until_page_ready.assert_called_with() - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement." - "_ElementKeywords.click_element_at_coordinates") - def test_should_click_element_at_coordinates_and_skip_ready(self, - mock_click_element_at_coordinates): + @mock.patch("ExtendedSelenium2Library.keywords.extendedelement.ActionChains") + def test_should_click_el_at_coords_and_skip_ready(self, mock_action_chains): """Should click an element at given coordinates with skip_ready.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_element_at_coordinates(self.locator, 0, 0, True) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_element_at_coordinates.assert_called_with(self.web_element, 0, 0) + self.element._info.assert_called_with("Clicking element '%s' in coordinates '%s', '%s'." % + (self.locator, 0, 0)) + self.element._get_element_and_scroll_into_view_on_iexplore.assert_called_with(self.locator) + mock_action_chains.assert_called_with(self.element._current_browser()) + action_chains = mock_action_chains(self.element._current_browser()) + action_chains.move_to_element.assert_called_with(self.web_element) + move_to_element = action_chains.move_to_element(self.web_element) + move_to_element.move_by_offset.assert_called_with(0, 0) + move_by_offset = move_to_element.move_by_offset(0, 0) + move_by_offset.click.assert_called_with() + click = move_by_offset.click() + click.perform.assert_called_with() self.assertFalse(self.element._wait_until_page_ready.called) - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement._ElementKeywords.click_image") - def test_should_click_image(self, mock_click_image): + def test_should_click_image(self): """Should click an image.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_image(self.locator) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_image.assert_called_with(self.web_element) + self.element._info.assert_called_with("Clicking image '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, False, 'image') + self.web_element.click.assert_called_with() self.element._wait_until_page_ready.assert_called_with() - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement._ElementKeywords.click_image") - def test_should_click_image_and_skip_ready(self, mock_click_image): + def test_should_click_image_and_skip_ready(self): """Should click an image with skip_ready.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_image(self.locator, True) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_image.assert_called_with(self.web_element) + self.element._info.assert_called_with("Clicking image '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, False, 'image') + self.web_element.click.assert_called_with() self.assertFalse(self.element._wait_until_page_ready.called) - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement._ElementKeywords.click_link") - def test_should_click_link(self, mock_click_link): + def test_should_click_input_image(self): + """Should click an input image.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.side_effect = \ + [None, self.web_element] + self.element.click_image(self.locator) + self.element._info.assert_called_with("Clicking image '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, True, 'input') + self.web_element.click.assert_called_with() + self.element._wait_until_page_ready.assert_called_with() + + def test_should_click_input_image_and_skip_ready(self): + """Should click an input image with skip_ready.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.side_effect = \ + [None, self.web_element] + self.element.click_image(self.locator, True) + self.element._info.assert_called_with("Clicking image '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, True, 'input') + self.web_element.click.assert_called_with() + self.assertFalse(self.element._wait_until_page_ready.called) + + def test_should_click_link(self): """Should click a link.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_link(self.locator) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_link.assert_called_with(self.web_element) + self.element._info.assert_called_with("Clicking link '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, tag='a') + self.web_element.click.assert_called_with() self.element._wait_until_page_ready.assert_called_with() - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement._ElementKeywords.click_link") - def test_should_click_link_and_skip_ready(self, mock_click_link): + def test_should_click_link_and_skip_ready(self): """Should click a link with skip_ready.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.click_link(self.locator, True) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_click_link.assert_called_with(self.web_element) + self.element._info.assert_called_with("Clicking link '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, tag='a') + self.web_element.click.assert_called_with() self.assertFalse(self.element._wait_until_page_ready.called) - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement." - "_ElementKeywords.double_click_element") - def test_should_double_click_element(self, mock_double_click_element): + @mock.patch("ExtendedSelenium2Library.keywords.extendedelement.ActionChains") + def test_should_double_click_element(self, mock_action_chains): """Should double click an element.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.double_click_element(self.locator) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_double_click_element.assert_called_with(self.web_element) + self.element._info.assert_called_with("Double clicking element '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.assert_called_with(self.locator) + mock_action_chains.assert_called_with(self.element._current_browser()) + action_chains = mock_action_chains(self.element._current_browser()) + action_chains.double_click.assert_called_with(self.web_element) + double_click = action_chains.double_click(self.web_element) + double_click.perform.assert_called_with() self.element._wait_until_page_ready.assert_called_with() - @mock.patch("ExtendedSelenium2Library.keywords.extendedelement." - "_ElementKeywords.double_click_element") - def test_should_double_click_element_and_skip_ready(self, mock_double_click_element): - """Should double click an element.""" - self.element._scroll_into_view_on_internet_explorer = mock.Mock() - self.element._scroll_into_view_on_internet_explorer.return_value = self.web_element + @mock.patch("ExtendedSelenium2Library.keywords.extendedelement.ActionChains") + def test_should_double_click_element_and_skip_ready(self, mock_action_chains): + """Should double click an element with skip ready.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element self.element.double_click_element(self.locator, True) - self.element._scroll_into_view_on_internet_explorer.assert_called_with(self.locator) - mock_double_click_element.assert_called_with(self.web_element) + self.element._info.assert_called_with("Double clicking element '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.assert_called_with(self.locator) + mock_action_chains.assert_called_with(self.element._current_browser()) + action_chains = mock_action_chains(self.element._current_browser()) + action_chains.double_click.assert_called_with(self.web_element) + double_click = action_chains.double_click(self.web_element) + double_click.perform.assert_called_with() self.assertFalse(self.element._wait_until_page_ready.called) def test_element_attribute_should_contain(self): @@ -172,7 +242,7 @@ def test_element_attribute_should_raise_exception(self): self.element.get_element_attribute.assert_called_with(self.locator_attribute) self.assertEqual(' '.join(context.exception.args).strip(), message) - def test_element_attribute_should_raise_custom_message_exception(self): + def test_el_attr_should_raise_custom_msg_exception(self): """Element attribute should raise exception with custom message.""" actual = 'nada' message = "Locator '%s' should have contained '%s' but it was '%s'." %\ @@ -195,7 +265,7 @@ def test_element_attribute_should_not_contain(self): self.locator_class) self.element.get_element_attribute.assert_called_with(self.locator_attribute) - def test_element_attribute_not_contain_should_raise_exception(self): + def test_el_attr_not_contain_should_raise_exception(self): """Element attribute not contain should raise exception.""" message = "Element attribute '%s' should not contain '%s' but it did." %\ (self.locator_attribute, self.locator_class) @@ -207,7 +277,7 @@ def test_element_attribute_not_contain_should_raise_exception(self): self.element.get_element_attribute.assert_called_with(self.locator_attribute) self.assertEqual(' '.join(context.exception.args).strip(), message) - def test_element_attribute_not_contain_should_raise_custom_message_exception(self): + def test_el_attr_not_contain_raise_custom_msg_ex(self): """Element attribute not contain should raise exception with custom message.""" message = "Locator '%s' should not contain '%s' but it did." %\ (self.locator_attribute, self.locator_class) @@ -222,6 +292,7 @@ def test_element_attribute_not_contain_should_raise_custom_message_exception(sel def test_is_element_visible(self): """Element should be visible.""" + # pylint: disable=protected-access self.element._is_visible = mock.Mock() self.element._is_visible.return_value = True self.assertTrue(self.element.is_element_visible(self.locator)) @@ -229,74 +300,85 @@ def test_is_element_visible(self): def test_is_element_not_visible(self): """Element should not be visible.""" + # pylint: disable=protected-access self.element._is_visible = mock.Mock() self.element._is_visible.return_value = False self.assertFalse(self.element.is_element_visible(self.locator)) self.element._is_visible.assert_called_with(self.locator) - def test_scroll_element_into_view(self): + @mock.patch("ExtendedSelenium2Library.keywords.extendedelement.logger") + def test_scroll_element_into_view(self, mock_logger): """Scroll element into view.""" + # pylint: disable=protected-access + self.element._current_browser().execute_script = mock.Mock() self.element._element_find = mock.Mock() self.element._element_find.return_value = self.web_element - self.element._current_browser().execute_script = mock.Mock() self.assertEqual(self.element.scroll_element_into_view(self.locator), self.web_element) + mock_logger.info.assert_called_with("Scrolling element '%s' into view." % self.locator) self.element._element_find.assert_called_with(self.locator, True, True) self.element._current_browser().\ execute_script.assert_called_with('arguments[0].scrollIntoView()', self.web_element) - def test_scroll_element_into_view_raise_exception(self): - """Scroll element into view raise exception.""" - message = "Element '%s' not found." % self.locator + def test_scroll_element_into_view_with_web_element(self): + """Scroll element into view with web element.""" + # pylint: disable=protected-access + self.element._current_browser().execute_script = mock.Mock() self.element._element_find = mock.Mock() - self.element._element_find.return_value = None - with self.assertRaises(AssertionError) as context: - self.assertIsNone(self.element.scroll_element_into_view(self.locator)) - self.assertEqual(' '.join(context.exception.args).strip(), message) - self.element._element_find.assert_called_with(self.locator, True, True) - self.assertFalse(self.element._current_browser().execute_script.called) + self.assertEqual(self.element.scroll_element_into_view(self.web_element), + self.web_element) + self.assertFalse(self.element._info.called) + self.assertFalse(self.element._element_find.called) + self.element._current_browser().\ + execute_script.assert_called_with('arguments[0].scrollIntoView()', + self.web_element) def test_get_browser_name(self): """Should return browser name.""" + # pylint: disable=protected-access self.element._current_browser().capabilities = {'browserName': 'chrome'} self.assertEqual(self.element._get_browser_name(), 'chrome') - def test_is_internet_explorer(self): - """Browser name should be internet explorer.""" - self.element._get_browser_name = mock.Mock() - self.element._get_browser_name.return_value = 'ie' - self.assertTrue(self.element._is_internet_explorer()) - self.element._get_browser_name.assert_called_with() - - def test_is_not_internet_explorer(self): - """Browser name should not be internet explorer.""" - self.element._get_browser_name = mock.Mock() - self.assertFalse(self.element._is_internet_explorer('chrome')) - self.assertFalse(self.element._get_browser_name.called) - - def test_scroll_into_view_on_internet_explorer(self): - """Should scroll into view on internet explorer.""" + def test_get_element_and_scroll_into_view_on_iexplore(self): + """Should scroll into view on internet explorer and returns the element.""" + # pylint: disable=protected-access self.element._element_find = mock.Mock() self.element._element_find.return_value = self.web_element self.element._is_internet_explorer = mock.Mock() self.element._is_internet_explorer.return_value = True self.element.scroll_element_into_view = mock.Mock() - self.assertEqual(self.element._scroll_into_view_on_internet_explorer(self.locator), + self.assertEqual(self.element._get_element_and_scroll_into_view_on_iexplore(self.locator), self.web_element) - self.element._element_find.assert_called_with(self.locator, True, True) + self.element._element_find.assert_called_with(self.locator, True, True, None) self.element._is_internet_explorer.assert_called_with() self.element.scroll_element_into_view.assert_called_with(self.web_element) - def test_not_scroll_into_view_on_non_internet_explorer(self): - """Should not scroll into view on non internet explorer.""" + def test_get_el_and_not_scroll_on_non_iexplore(self): + """Should not scroll into view on non internet explorer and returns element.""" + # pylint: disable=protected-access self.element._element_find = mock.Mock() self.element._element_find.return_value = self.web_element self.element._is_internet_explorer = mock.Mock() self.element._is_internet_explorer.return_value = False self.element.scroll_element_into_view = mock.Mock() - self.assertEqual(self.element._scroll_into_view_on_internet_explorer(self.locator), + self.assertEqual(self.element._get_element_and_scroll_into_view_on_iexplore(self.locator), self.web_element) - self.element._element_find.assert_called_with(self.locator, True, True) + self.element._element_find.assert_called_with(self.locator, True, True, None) self.element._is_internet_explorer.assert_called_with() self.assertFalse(self.element.scroll_element_into_view.called) + + def test_is_internet_explorer(self): + """Browser name should be internet explorer.""" + # pylint: disable=protected-access + self.element._get_browser_name = mock.Mock() + self.element._get_browser_name.return_value = 'ie' + self.assertTrue(self.element._is_internet_explorer()) + self.element._get_browser_name.assert_called_with() + + def test_is_not_internet_explorer(self): + """Browser name should not be internet explorer.""" + # pylint: disable=protected-access + self.element._get_browser_name = mock.Mock() + self.assertFalse(self.element._is_internet_explorer('chrome')) + self.assertFalse(self.element._get_browser_name.called) diff --git a/test/utest/test_extendedformelement.py b/test/utest/test_extendedformelement.py new file mode 100644 index 0000000..35edd3a --- /dev/null +++ b/test/utest/test_extendedformelement.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" +Extended Selenium2 Library - a web testing library with AngularJS support. +""" + +from sys import path +path.append('src') +import unittest +import mock +from ExtendedSelenium2Library.keywords import ExtendedFormElementKeywords +from selenium.webdriver.remote.webelement import WebElement +from Selenium2Library.keywords import _FormElementKeywords + + +class ExtendedFormElementTests(unittest.TestCase): + """Extended form element keyword test class.""" + + def setUp(self): + """Instantiate the extended form element class.""" + self.driver = mock.Mock() + self.driver.session_id = 'session' + self.element = ExtendedFormElementKeywords() + # pylint: disable=protected-access + self.element._info = mock.Mock() + self.element._wait_until_page_ready = mock.Mock() + self.group_name = 'group' + self.locator = 'css=.selector' + self.value = 'value' + self.web_element = WebElement(self.driver, 'element', False) + self.web_element.click = mock.Mock() + + def test_should_inherit_keywords(self): + """Extended form element instance should inherit Selenium2 form element instances.""" + self.assertIsInstance(self.element, _FormElementKeywords) + + def test_should_click_input_button(self): + """Should click an input button.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element + self.element.click_button(self.locator) + self.element._info.assert_called_with("Clicking button '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, False, 'input') + self.web_element.click.assert_called_with() + self.element._wait_until_page_ready.assert_called_with() + + def test_should_click_input_button_and_skip_ready(self): + """Should click an input button with skip_ready.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element + self.element.click_button(self.locator, True) + self.element._info.assert_called_with("Clicking button '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, False, 'input') + self.web_element.click.assert_called_with() + self.assertFalse(self.element._wait_until_page_ready.called) + + def test_should_click_button(self): + """Should click a button.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.\ + side_effect = [None, self.web_element] + self.element.click_button(self.locator) + self.element._info.assert_called_with("Clicking button '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, True, 'button') + self.web_element.click.assert_called_with() + self.element._wait_until_page_ready.assert_called_with() + + def test_should_click_button_and_skip_ready(self): + """Should click a button with skip_ready.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.\ + side_effect = [None, self.web_element] + self.element.click_button(self.locator, True) + self.element._info.assert_called_with("Clicking button '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore.\ + assert_called_with(self.locator, True, 'button') + self.web_element.click.assert_called_with() + self.assertFalse(self.element._wait_until_page_ready.called) + + def test_should_select_checkbox(self): + """Should select a checkbox.""" + # pylint: disable=protected-access + self.element._get_checkbox = mock.Mock() + self.element._get_checkbox.return_value = self.web_element + self.element._select_checkbox_or_radio_button = mock.Mock() + self.web_element.is_selected = mock.Mock() + self.web_element.is_selected.return_value = False + self.element.select_checkbox(self.locator) + self.element._info.assert_called_with("Selecting checkbox '%s'." % self.locator) + self.element._get_checkbox.assert_called_with(self.locator) + self.web_element.is_selected.assert_called_with() + self.element._select_checkbox_or_radio_button.assert_called_with(self.locator) + + def test_should_ignore_selected_checkbox(self): + """Should ignore selected checkbox.""" + # pylint: disable=protected-access + self.element._get_checkbox = mock.Mock() + self.element._get_checkbox.return_value = self.web_element + self.element._select_checkbox_or_radio_button = mock.Mock() + self.web_element.is_selected = mock.Mock() + self.web_element.is_selected.return_value = True + self.element.select_checkbox(self.locator) + self.element._info.assert_called_with("Selecting checkbox '%s'." % self.locator) + self.element._get_checkbox.assert_called_with(self.locator) + self.web_element.is_selected.assert_called_with() + self.assertFalse(self.element._select_checkbox_or_radio_button.called) + + def test_should_select_radio_button(self): + """Should select a radio button.""" + # pylint: disable=protected-access + self.element._get_radio_button_with_value = mock.Mock() + self.element._get_radio_button_with_value.return_value = self.web_element + self.element._select_checkbox_or_radio_button = mock.Mock() + self.web_element.is_selected = mock.Mock() + self.web_element.is_selected.return_value = False + self.element.select_radio_button(self.group_name, self.value) + self.element._info.assert_called_with("Selecting '%s' from radio button '%s'." % + (self.value, self.group_name)) + self.element._get_radio_button_with_value.assert_called_with(self.group_name, self.value) + self.web_element.is_selected.assert_called_with() + self.element._select_checkbox_or_radio_button.\ + assert_called_with('css=input[name="%s"][value="%s"]' % (self.group_name, self.value)) + + def test_should_ignore_selected_radio_button(self): + """Should ignore selected radio button.""" + # pylint: disable=protected-access + self.element._get_radio_button_with_value = mock.Mock() + self.element._get_radio_button_with_value.return_value = self.web_element + self.element._select_checkbox_or_radio_button = mock.Mock() + self.web_element.is_selected = mock.Mock() + self.web_element.is_selected.return_value = True + self.element.select_radio_button(self.group_name, self.value) + self.element._info.assert_called_with("Selecting '%s' from radio button '%s'." % + (self.value, self.group_name)) + self.element._get_radio_button_with_value.assert_called_with(self.group_name, self.value) + self.web_element.is_selected.assert_called_with() + self.assertFalse(self.element._select_checkbox_or_radio_button.called) + + def test_should_submit_form(self): + """Should submit form.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element + self.web_element.submit = mock.Mock() + self.element.submit_form(self.locator) + self.element._info.assert_called_with("Submitting form '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore(self.locator, tag='form') + self.web_element.submit.assert_called_with() + self.element._wait_until_page_ready.assert_called_with() + + def test_should_submit_form_and_skip_ready(self): + """Should submit form with skip_ready.""" + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element + self.web_element.submit = mock.Mock() + self.element.submit_form(self.locator, skip_ready=True) + self.element._info.assert_called_with("Submitting form '%s'." % self.locator) + self.element._get_element_and_scroll_into_view_on_iexplore(self.locator, tag='form') + self.web_element.submit.assert_called_with() + self.assertFalse(self.element._wait_until_page_ready.called) + + def test_should_submit_form_without_locator(self): + """Should submit form without locator.""" + locator = 'xpath=//form' + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element + self.web_element.submit = mock.Mock() + self.element.submit_form() + self.element._info.assert_called_with("Submitting form '%s'." % locator) + self.element._get_element_and_scroll_into_view_on_iexplore(locator, tag='form') + self.web_element.submit.assert_called_with() + self.element._wait_until_page_ready.assert_called_with() + + def test_should_submit_form_no_locator_and_skip_ready(self): + """Should submit form without locator with skip_ready.""" + locator = 'xpath=//form' + # pylint: disable=protected-access + self.element._get_element_and_scroll_into_view_on_iexplore = mock.Mock() + self.element._get_element_and_scroll_into_view_on_iexplore.return_value = self.web_element + self.web_element.submit = mock.Mock() + self.element.submit_form(skip_ready=True) + self.element._info.assert_called_with("Submitting form '%s'." % locator) + self.element._get_element_and_scroll_into_view_on_iexplore(locator, tag='form') + self.web_element.submit.assert_called_with() + self.assertFalse(self.element._wait_until_page_ready.called) + + def test_should_fill_text_field(self): + """Should fill text field.""" + # pylint: disable=protected-access + self.element._element_find = mock.Mock() + self.element._element_find.return_value = self.web_element + self.element._element_trigger_change = mock.Mock() + self.web_element.clear = mock.Mock() + self.web_element.send_keys = mock.Mock() + self.element._input_text_into_text_field(self.locator, self.value) + self.element._element_find(self.locator, True, True) + self.web_element.clear.assert_called_with() + self.web_element.send_keys.assert_called_with(self.value) + self.element._element_trigger_change.assert_called_with(self.locator) + + def test_should_fill_text_field_and_skip_ready(self): + """Should fill text field with skip_ready.""" + # pylint: disable=protected-access + self.element._element_find = mock.Mock() + self.element._element_find.return_value = self.web_element + self.element._element_trigger_change = mock.Mock() + self.web_element.clear = mock.Mock() + self.web_element.send_keys = mock.Mock() + self.element._input_text_into_text_field(self.locator, self.value, True) + self.element._element_find(self.locator, True, True) + self.web_element.clear.assert_called_with() + self.web_element.send_keys.assert_called_with(self.value) + self.assertFalse(self.element._element_trigger_change.called) + + def test_should_select_checkbox_or_radio_button(self): + """Should select checkbox or radio button.""" + # pylint: disable=protected-access + self.element._wait_until_page_ready = mock.Mock() + self.element._select_checkbox_or_radio_button(self.locator) + self.element._wait_until_page_ready.\ + assert_called_with(self.locator, skip_stale_check=True, + prefix='var cb=arguments[arguments.length-1];' + 'var el=arguments[0];if(window.angular){', + handler='function(){angular.element(el).' + 'prop(\'checked\',true).triggerHandler(\'click\');' + 'cb(true)}', + suffix='}else{el.click();cb(false)}') diff --git a/test/utest/test_extendedjavascript.py b/test/utest/test_extendedjavascript.py index fc4562c..b44439e 100644 --- a/test/utest/test_extendedjavascript.py +++ b/test/utest/test_extendedjavascript.py @@ -23,10 +23,10 @@ from sys import path path.append('src') +import unittest +import mock from ExtendedSelenium2Library.keywords import ExtendedJavascriptKeywords from Selenium2Library.keywords import _JavaScriptKeywords -import mock -import unittest class ExtendedJavascriptTests(unittest.TestCase): @@ -38,6 +38,7 @@ def setUp(self): self.js_code_py = 'return True' self.js_code_vars = 'return ${true}' self.script = ExtendedJavascriptKeywords() + # pylint: disable=protected-access self.script._current_browser = mock.Mock() self.script._debug = mock.Mock() self.script._warn = mock.Mock() @@ -48,6 +49,7 @@ def test_should_inherit_keywords(self): def test_execute_async_js_with_replaced_vars(self): """Should execute async js with replaced vars.""" + # pylint: disable=protected-access self.script._get_javascript_to_execute = mock.Mock() self.script._get_javascript_to_execute.return_value = self.js_code_vars self.script._replace_variables_in_javascript_code = mock.Mock() @@ -64,6 +66,7 @@ def test_execute_async_js_with_replaced_vars(self): def test_execute_js_with_replaced_vars(self): """Should execute js with replaced vars.""" + # pylint: disable=protected-access self.script._get_javascript_to_execute = mock.Mock() self.script._get_javascript_to_execute.return_value = self.js_code_vars self.script._replace_variables_in_javascript_code = mock.Mock() @@ -79,6 +82,7 @@ def test_execute_js_with_replaced_vars(self): def test_get_screen_size(self): """Should return the screen size.""" + # pylint: disable=protected-access self.script._current_browser().execute_script = mock.Mock() self.script._current_browser().execute_script.return_value = [0, 0] self.assertEqual(self.script.get_screen_size(), [0, 0]) @@ -92,6 +96,7 @@ def test_warn_any_js_errors(self): self.script.get_browser_logs.return_value = logs self.script.warn_any_javascript_errors() self.script.get_browser_logs.assert_called_with() + # pylint: disable=protected-access self.script._warn.assert_called_with(' %s' % logs) def test_warn_any_js_errors_with_excludes(self): @@ -102,6 +107,7 @@ def test_warn_any_js_errors_with_excludes(self): self.script.get_browser_logs.return_value = logs self.script.warn_any_javascript_errors(['miny']) self.script.get_browser_logs.assert_called_with() + # pylint: disable=protected-access self.script._warn.assert_called_with(' %s' % filtered_logs) def test_warn_any_js_errors_with_label(self): @@ -112,10 +118,12 @@ def test_warn_any_js_errors_with_label(self): self.script.get_browser_logs.return_value = logs self.script.warn_any_javascript_errors(label=label) self.script.get_browser_logs.assert_called_with() + # pylint: disable=protected-access self.script._warn.assert_called_with('%s %s' % (label, logs)) def test_replace_variables_in_js_code(self): """Should replace all variables in js code.""" + # pylint: disable=protected-access self.script._builtin = mock.Mock() self.script._builtin.replace_variables = mock.Mock() self.script._builtin.replace_variables.return_value = self.js_code_py diff --git a/test/utest/test_extendedselectelement.py b/test/utest/test_extendedselectelement.py new file mode 100644 index 0000000..1b90630 --- /dev/null +++ b/test/utest/test_extendedselectelement.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Extended Selenium2 Library - a web testing library with AngularJS support. +# Copyright (c) 2015, 2016 Richard Huang +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" +Extended Selenium2 Library - a web testing library with AngularJS support. +""" + +from sys import path +path.append('src') +import unittest +import mock +from ExtendedSelenium2Library.keywords import ExtendedSelectElementKeywords +from Selenium2Library.keywords import _SelectElementKeywords + + +class ExtendedSelectElementTests(unittest.TestCase): + """Extended select element keyword test class.""" + + def setUp(self): + """Instantiate the extended select element class.""" + self.element = ExtendedSelectElementKeywords() + self.indexes = (1, 2, 3, 4, 5) + self.items = ('1', '2', '3', '4', '5') + self.labels = ('1', '2', '3', '4', '5') + self.locator = 'css=.selector' + self.values = ('1', '2', '3', '4', '5') + + def test_should_inherit_keywords(self): + """Extended select element instance should inherit Selenium2 select element instances.""" + self.assertIsInstance(self.element, _SelectElementKeywords) + + @mock.patch("ExtendedSelenium2Library.keywords.extendedselectelement." + "_SelectElementKeywords.select_all_from_list") + def test_should_select_all_from_list(self, mock_select_all_from_list): + """Should select all option items from list.""" + # pylint: disable=protected-access + self.element._element_trigger_change = mock.Mock() + self.element.select_all_from_list(self.locator) + mock_select_all_from_list.assert_called_with(self.locator) + self.element._element_trigger_change.assert_called_with(self.locator) + + @mock.patch("ExtendedSelenium2Library.keywords.extendedselectelement." + "_SelectElementKeywords.select_from_list") + def test_should_select_from_list(self, mock_select_from_list): + """Should select option items from list by item.""" + # pylint: disable=protected-access + self.element._element_trigger_change = mock.Mock() + self.element.select_from_list(self.locator, self.items) + mock_select_from_list.assert_called_with(self.locator, self.items) + self.element._element_trigger_change.assert_called_with(self.locator) + + @mock.patch("ExtendedSelenium2Library.keywords.extendedselectelement." + "_SelectElementKeywords.select_from_list_by_index") + def test_should_select_from_list_by_index(self, mock_select_from_list_by_index): + """Should select option items from list by index.""" + # pylint: disable=protected-access + self.element._element_trigger_change = mock.Mock() + self.element.select_from_list_by_index(self.locator, self.indexes) + mock_select_from_list_by_index.assert_called_with(self.locator, self.indexes) + self.element._element_trigger_change.assert_called_with(self.locator) + + @mock.patch("ExtendedSelenium2Library.keywords.extendedselectelement." + "_SelectElementKeywords.select_from_list_by_label") + def test_should_select_from_list_by_label(self, mock_select_from_list_by_label): + """Should select option items from list by label.""" + # pylint: disable=protected-access + self.element._element_trigger_change = mock.Mock() + self.element.select_from_list_by_label(self.locator, self.labels) + mock_select_from_list_by_label.assert_called_with(self.locator, self.labels) + self.element._element_trigger_change.assert_called_with(self.locator) + + @mock.patch("ExtendedSelenium2Library.keywords.extendedselectelement." + "_SelectElementKeywords.select_from_list_by_value") + def test_should_select_from_list_by_value(self, mock_select_from_list_by_value): + """Should select option items from list by value.""" + # pylint: disable=protected-access + self.element._element_trigger_change = mock.Mock() + self.element.select_from_list_by_value(self.locator, self.values) + mock_select_from_list_by_value.assert_called_with(self.locator, self.values) + self.element._element_trigger_change.assert_called_with(self.locator) + + def test_should_trigger_change(self): + """Should trigger change event.""" + # pylint: disable=protected-access + self.element._wait_until_page_ready = mock.Mock() + self.element._element_trigger_change(self.locator) + self.element._wait_until_page_ready.\ + assert_called_with(self.locator, skip_stale_check=True, + prefix='var cb=arguments[arguments.length-1];' + 'var el=arguments[0];if(window.angular){', + handler='function(){$(el).trigger(\'change\').' + 'trigger(\'focusout\');cb(true)}')