1515# limitations under the License.
1616
1717import os
18+ from collections import namedtuple
19+
20+ from robot .utils import plural_or_not , seq2str
1821
1922from SeleniumLibrary .base import LibraryComponent , keyword
2023
2124
2225class JavaScriptKeywords (LibraryComponent ):
2326
27+ js_marker = 'JAVASCRIPT'
28+ arg_marker = 'ARGUMENTS'
29+
2430 @keyword
2531 def execute_javascript (self , * code ):
26- """Executes the given JavaScript code.
32+ """Executes the given JavaScript code with possible arguments .
2733
28- ``code`` may contain multiple lines of code and may be divided into
29- multiple cells in the test data. In that case, the parts are
30- concatenated together without adding spaces.
34+ ``code`` may be divided into multiple cells in the test data and
35+ ``code`` may contain multiple lines of code and arguments. In that case,
36+ the JavaScript code parts are concatenated together without adding
37+ spaces and optional arguments are separated from ``code``.
3138
32- If ``code`` is an absolute path to an existing file, the JavaScript
39+ If ``code`` is a path to an existing file, the JavaScript
3340 to execute will be read from that file. Forward slashes work as
3441 a path separator on all operating systems.
3542
@@ -42,19 +49,30 @@ def execute_javascript(self, *code):
4249 This keyword returns whatever the executed JavaScript code returns.
4350 Return values are converted to the appropriate Python types.
4451
52+ Starting from SeleniumLibrary 3.2 it is possible to provide JavaScript
53+ [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_remote/selenium.webdriver.remote.webdriver.html#selenium.webdriver.remote.webdriver.WebDriver.execute_script|
54+ arguments] as part of ``code`` argument. The JavaScript code and
55+ arguments must be separated with `JAVASCRIPT` and `ARGUMENTS` markers
56+ and must used exactly with this format. If the Javascript code is
57+ first, then the `JAVASCRIPT` marker is optional. The order of
58+ `JAVASCRIPT` and `ARGUMENTS` markers can swapped, but if `ARGUMENTS`
59+ is first marker, then `JAVASCRIPT` marker is mandatory. It is only
60+ allowed to use `JAVASCRIPT` and `ARGUMENTS` markers only one time in the
61+ ``code`` argument.
62+
4563 Examples:
4664 | `Execute JavaScript` | window.myFunc('arg1', 'arg2') |
4765 | `Execute JavaScript` | ${CURDIR}/js_to_execute.js |
48- | ${sum} = | `Execute JavaScript` | return 1 + 1; |
49- | `Should Be Equal` | ${sum} | ${2} |
66+ | `Execute JavaScript` | alert(arguments[0]); | ARGUMENTS | 123 |
67+ | `Execute JavaScript` | ARGUMENTS | 123 | JAVASCRIPT | alert(arguments[0]); |
5068 """
51- js = self ._get_javascript_to_execute (code )
52- self .info ( " Executing JavaScript: \n %s" % js )
53- return self .driver .execute_script (js )
69+ js_code , js_args = self ._get_javascript_to_execute (code )
70+ self ._js_logger ( ' Executing JavaScript' , js_code , js_args )
71+ return self .driver .execute_script (js_code , * js_args )
5472
5573 @keyword
5674 def execute_async_javascript (self , * code ):
57- """Executes asynchronous JavaScript code.
75+ """Executes asynchronous JavaScript code with possible arguments .
5876
5977 Similar to `Execute Javascript` except that scripts executed with
6078 this keyword must explicitly signal they are finished by invoking the
@@ -64,6 +82,11 @@ def execute_async_javascript(self, *code):
6482 Scripts must complete within the script timeout or this keyword will
6583 fail. See the `Timeouts` section for more information.
6684
85+ Starting from SeleniumLibrary 3.2 it is possible to provide JavaScript
86+ [https://seleniumhq.github.io/selenium/docs/api/py/webdriver_remote/selenium.webdriver.remote.webdriver.html#selenium.webdriver.remote.webdriver.WebDriver.execute_async_script|
87+ arguments] as part of ``code`` argument. See `Execute Javascript` for
88+ more details.
89+
6790 Examples:
6891 | `Execute Async JavaScript` | var callback = arguments[arguments.length - 1]; window.setTimeout(callback, 2000); |
6992 | `Execute Async JavaScript` | ${CURDIR}/async_js_to_execute.js |
@@ -73,19 +96,72 @@ def execute_async_javascript(self, *code):
7396 | ... | window.setTimeout(answer, 2000); |
7497 | `Should Be Equal` | ${result} | text |
7598 """
76- js = self ._get_javascript_to_execute (code )
77- self .info ("Executing Asynchronous JavaScript:\n %s" % js )
78- return self .driver .execute_async_script (js )
79-
80- def _get_javascript_to_execute (self , lines ):
81- code = '' .join (lines )
82- path = code .replace ('/' , os .sep )
83- if os .path .isabs (path ) and os .path .isfile (path ):
84- code = self ._read_javascript_from_file (path )
85- return code
99+ js_code , js_args = self ._get_javascript_to_execute (code )
100+ self ._js_logger ('Executing Asynchronous JavaScript' , js_code , js_args )
101+ return self .driver .execute_async_script (js_code , * js_args )
102+
103+ def _js_logger (self , base , code , args ):
104+ message = '%s:\n %s\n ' % (base , code )
105+ if args :
106+ message = ('%sBy using argument%s:\n %s'
107+ % (message , plural_or_not (args ), seq2str (args )))
108+ else :
109+ message = '%sWithout any arguments.' % message
110+ self .info (message )
111+
112+ def _get_javascript_to_execute (self , code ):
113+ js_code , js_args = self ._separate_code_and_args (code )
114+ if not js_code :
115+ raise ValueError ('JavaScript code was not found from code argument.' )
116+ js_code = '' .join (js_code )
117+ path = js_code .replace ('/' , os .sep )
118+ if os .path .isfile (path ):
119+ js_code = self ._read_javascript_from_file (path )
120+ return js_code , js_args
121+
122+ def _separate_code_and_args (self , code ):
123+ code = list (code )
124+ self ._check_marker_error (code )
125+ index = self ._get_marker_index (code )
126+ if self .arg_marker not in code :
127+ return code [index .js + 1 :], []
128+ if self .js_marker not in code :
129+ return code [0 :index .arg ], code [index .arg + 1 :]
130+ else :
131+ if index .js == 0 :
132+ return code [index .js + 1 :index .arg ], code [index .arg + 1 :]
133+ else :
134+ return code [index .js + 1 :], code [index .arg + 1 :index .js ]
135+
136+ def _check_marker_error (self , code ):
137+ if not code :
138+ raise ValueError ('There must be at least one argument defined.' )
139+ message = None
140+ template = '%s marker was found two times in the code.'
141+ if code .count (self .js_marker ) > 1 :
142+ message = template % self .js_marker
143+ if code .count (self .arg_marker ) > 1 :
144+ message = template % self .arg_marker
145+ index = self ._get_marker_index (code )
146+ if index .js > 0 and index .arg != 0 :
147+ message = template % self .js_marker
148+ if message :
149+ raise ValueError (message )
150+
151+ def _get_marker_index (self , code ):
152+ Index = namedtuple ('Index' , 'js arg' )
153+ if self .js_marker in code :
154+ js = code .index (self .js_marker )
155+ else :
156+ js = - 1
157+ if self .arg_marker in code :
158+ arg = code .index (self .arg_marker )
159+ else :
160+ arg = - 1
161+ return Index (js = js , arg = arg )
86162
87163 def _read_javascript_from_file (self , path ):
88164 self .info ('Reading JavaScript from file <a href="file://%s">%s</a>.'
89- . format (path .replace (os .sep , '/' ), path ), html = True )
165+ % (path .replace (os .sep , '/' ), path ), html = True )
90166 with open (path ) as file :
91167 return file .read ().strip ()
0 commit comments