From 242f028a639e47205c5f15d1f03a3e55d4719797 Mon Sep 17 00:00:00 2001 From: zephraph Date: Wed, 6 May 2015 15:18:16 -0500 Subject: [PATCH 1/2] execute javascript w/ args --- src/Selenium2Library/keywords/_javascript.py | 44 ++++++++++++++++---- src/Selenium2Library/utils/__init__.py | 21 +++++++--- test/acceptance/keywords/javascript.txt | 12 +++++- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/Selenium2Library/keywords/_javascript.py b/src/Selenium2Library/keywords/_javascript.py index 7ff78378a..4c11ed5ef 100644 --- a/src/Selenium2Library/keywords/_javascript.py +++ b/src/Selenium2Library/keywords/_javascript.py @@ -2,6 +2,8 @@ from selenium.common.exceptions import WebDriverException from keywordgroup import KeywordGroup +from Selenium2Library.utils import isstr + class _JavaScriptKeywords(KeywordGroup): def __init__(self): @@ -34,10 +36,10 @@ def choose_ok_on_next_confirmation(self): return true, as if the user had manually clicked OK, so you shouldn't need to use this command unless for some reason you need to change your mind prior to the next confirmation. After any confirmation, Selenium will resume using the - default behavior for future confirmations, automatically returning + default behavior for future confirmations, automatically returning true (OK) unless/until you explicitly use `Choose Cancel On Next Confirmation` for each confirmation. - + Note that every time a confirmation comes up, you must consume it by using a keywords such as `Get Alert Message`, or else the following selenium operations will fail. @@ -65,7 +67,7 @@ def confirm_action(self): self._cancel_on_next_confirmation = False return text - def execute_javascript(self, *code): + def execute_javascript(self, args, *code): """Executes the given JavaScript code. `code` may contain multiple lines of code and may be divided into @@ -82,6 +84,16 @@ def execute_javascript(self, *code): document object of the current frame or window, e.g. _document.getElementById('foo')_. + `args` is an optional way of specifying arguments to be passed to the context + of the executing javascript code. `args` must either be a list or any other + non-string type. Multiple arguments and strings must be added to a list to be used + from `args`. Arguments are accessed in the javascript function using the _arguments_ + variable. + + Example: + | @{arg list} | Hello | World | + | Execute Javascript | ${arg list} | console.log(arguments[0], arguments[1]); | + This keyword returns None unless there is a return statement in the JavaScript. Return values are converted to the appropriate type in Python, including WebElements. @@ -92,11 +104,20 @@ def execute_javascript(self, *code): | ${sum}= | Execute JavaScript | return 1 + 1; | | Should Be Equal | ${sum} | ${2} | """ + if len(code) == 0: + code = args + args = [] + elif isstr(args): + code = list(code).insert(0, args) + args = [] + elif not isinstance(args, list): + args = [args] + js = self._get_javascript_to_execute(''.join(code)) self._info("Executing JavaScript:\n%s" % js) - return self._current_browser().execute_script(js) + return self._current_browser().execute_script(js, *args) - def execute_async_javascript(self, *code): + def execute_async_javascript(self, args, *code): """Executes asynchronous JavaScript code. Similar to `Execute Javascript` except that scripts executed with @@ -116,9 +137,18 @@ def execute_async_javascript(self, *code): | ... | window.setTimeout(answer, 2000); | | | Should Be Equal | ${retval} | text | """ + if len(code) == 0: + code = args + args = [] + elif isstr(args): + code = list(code).insert(0, args) + args = [] + elif not isinstance(args, list): + args = [args] + js = self._get_javascript_to_execute(''.join(code)) self._info("Executing Asynchronous JavaScript:\n%s" % js) - return self._current_browser().execute_async_script(js) + return self._current_browser().execute_async_script(js, *args) def get_alert_message(self): """Returns the text of current JavaScript alert. @@ -152,4 +182,4 @@ def _get_javascript_to_execute(self, code): try: return codefile.read().strip() finally: - codefile.close() \ No newline at end of file + codefile.close() diff --git a/src/Selenium2Library/utils/__init__.py b/src/Selenium2Library/utils/__init__.py index c73348123..b211fc022 100644 --- a/src/Selenium2Library/utils/__init__.py +++ b/src/Selenium2Library/utils/__init__.py @@ -1,4 +1,4 @@ -import os +import os, sys from fnmatch import fnmatch from browsercache import BrowserCache @@ -7,18 +7,29 @@ "get_module_names_under", "import_modules_under", "escape_xpath_value", - "BrowserCache" + "BrowserCache", + "isstr" ] +PY3 = sys.version_info[0] == 3 + +if PY3: + string_type = str +else: + string_type = basestring + # Public +def isstr(value): + return isinstance(value, string_type) + def get_child_packages_in(root_dir, include_root_package_name=True, exclusions=None): packages = [] root_package_str = os.path.basename(root_dir) + '.' if include_root_package_name else "" _discover_child_package_dirs( root_dir, _clean_exclusions(exclusions), - lambda abs_path, relative_path, name: + lambda abs_path, relative_path, name: packages.append(root_package_str + relative_path.replace(os.sep, '.'))) return packages @@ -29,7 +40,7 @@ def get_module_names_under(root_dir, include_root_package_name=True, exclusions= root_dir, _clean_exclusions(exclusions), pattern if pattern is not None else "*.*", - lambda abs_path, relative_path, name: + lambda abs_path, relative_path, name: module_names.append(root_package_str + os.path.splitext(relative_path)[0].replace(os.sep, '.'))) return module_names @@ -65,7 +76,7 @@ def _discover_child_package_dirs(root_dir, exclusions, callback, relative_dir=No item_abs_path = os.path.join(root_dir, item_relative_path) if os.path.isdir(item_abs_path): if os.path.exists(os.path.join(item_abs_path, "__init__.py")): - exclusion_matches = [ exclusion for exclusion in exclusions + exclusion_matches = [ exclusion for exclusion in exclusions if os.sep + item_relative_path.lower() + os.sep == exclusion ] if not exclusion_matches: callback(item_abs_path, item_relative_path, item) diff --git a/test/acceptance/keywords/javascript.txt b/test/acceptance/keywords/javascript.txt index a0f621a20..e67b7e72b 100644 --- a/test/acceptance/keywords/javascript.txt +++ b/test/acceptance/keywords/javascript.txt @@ -53,6 +53,16 @@ Execute Javascript Execute Javascript window.add_content('button_target', 'Inserted directly') Page Should Contain Inserted directly +Execute Javascript With Array Arguments + @{args}= Create List Hello World + ${result}= Execute Javascript ${args} return arguments[0] + ' ' + arguments[1]; + Should Be Equal As Strings ${result} Hello World + +Execute Javascript With Object Argument + ${target}= Execute Javascript return window.document.getElementById('target'); + ${result}= Execute Javascript ${target} return arguments[0].id + Should Be Equal As Strings ${result} target + Execute Javascript from File [Documentation] LOG 2:1 REGEXP: Reading JavaScript from file .* LOG 2:2 Executing JavaScript:\n window.add_content('button_target', 'Inserted via file') Execute Javascript ${CURDIR}/executed_by_execute_javascript.js @@ -76,5 +86,3 @@ Drag and Drop by Offset Element Text Should Be id=droppable Drop here Drag and Drop by Offset id=draggable ${100} ${20} Element Text Should Be id=droppable Dropped! - - From 6b0f84f70db35bc3f0e9b8f26d363500b6fe2ff2 Mon Sep 17 00:00:00 2001 From: zephraph Date: Thu, 7 May 2015 10:30:21 -0500 Subject: [PATCH 2/2] Fixed logic error, test name --- src/Selenium2Library/keywords/_javascript.py | 10 +++++++--- test/acceptance/keywords/javascript.txt | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Selenium2Library/keywords/_javascript.py b/src/Selenium2Library/keywords/_javascript.py index 4c11ed5ef..c5d3eeccb 100644 --- a/src/Selenium2Library/keywords/_javascript.py +++ b/src/Selenium2Library/keywords/_javascript.py @@ -1,9 +1,11 @@ import os from selenium.common.exceptions import WebDriverException from keywordgroup import KeywordGroup - from Selenium2Library.utils import isstr +from robot.api import logger + + class _JavaScriptKeywords(KeywordGroup): def __init__(self): @@ -108,7 +110,8 @@ def execute_javascript(self, args, *code): code = args args = [] elif isstr(args): - code = list(code).insert(0, args) + code = list(code) + code.insert(0, args) args = [] elif not isinstance(args, list): args = [args] @@ -141,7 +144,8 @@ def execute_async_javascript(self, args, *code): code = args args = [] elif isstr(args): - code = list(code).insert(0, args) + code = list(code) + code.insert(0, args) args = [] elif not isinstance(args, list): args = [args] diff --git a/test/acceptance/keywords/javascript.txt b/test/acceptance/keywords/javascript.txt index e67b7e72b..9faa974d3 100644 --- a/test/acceptance/keywords/javascript.txt +++ b/test/acceptance/keywords/javascript.txt @@ -53,7 +53,7 @@ Execute Javascript Execute Javascript window.add_content('button_target', 'Inserted directly') Page Should Contain Inserted directly -Execute Javascript With Array Arguments +Execute Javascript With List Arguments @{args}= Create List Hello World ${result}= Execute Javascript ${args} return arguments[0] + ' ' + arguments[1]; Should Be Equal As Strings ${result} Hello World