Skip to content

Commit dcb6ad4

Browse files
committed
Adds the ExecuteAsyncScript method on WebDriver, WebElement and WebElements
1 parent abad6f3 commit dcb6ad4

7 files changed

Lines changed: 166 additions & 58 deletions

File tree

Selenium/ComInterfaces/_WebDriver.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ public interface _WebDriver {
4848

4949
[DispId(31), Description("Sends a customized command.")]
5050
object Send(string method, string relativeUri,
51-
string param1 = null, string value1 = null,
52-
string param2 = null, string value2 = null,
53-
string param3 = null, string value3 = null,
54-
string param4 = null, string value4 = null);
51+
string param1 = null, object value1 = null,
52+
string param2 = null, object value2 = null,
53+
string param3 = null, object value3 = null,
54+
string param4 = null, object value4 = null);
5555

5656
#endregion
5757

@@ -143,11 +143,14 @@ object Send(string method, string relativeUri,
143143

144144
#region Javascript
145145

146-
[DispId(601), Description("Execute JavaScrip on the page")]
146+
[DispId(601), Description("Execute a piece of JavaScript in the current frame or window. Returns the value specified by the return statement.")]
147147
object ExecuteScript(string script, object arguments = null);
148148

149+
[DispId(602), Description("Execute an asynchronous piece of JavaScript in the current frame or window. Returns the first argument of the callback function.")]
150+
object ExecuteAsyncScript(string script, object arguments = null, int timeout = -1);
151+
149152
[DispId(605), Description("Waits for the Javascript engine to return true or not null")]
150-
object WaitForScript(string script, object arguments, int timeout = -1);
153+
object WaitForScript(string script, object arguments = null, int timeout = -1);
151154

152155
#endregion
153156

Selenium/ComInterfaces/_WebElement.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,14 @@ public interface _WebElement {
242242

243243
#region Javascript
244244

245-
[DispId(601), Description("Execute JavaScrip on the page")]
245+
[DispId(601), Description("Executes a piece of JavaScript in the context of the current element. Returns the value specified by the return statement.")]
246246
object ExecuteScript(string script, object arguments = null);
247247

248-
[DispId(605), Description("Waits for the Javascript engine to return true or not null")]
249-
object WaitForScript(string script, object arguments, int timeout = -1);
248+
[DispId(602), Description("Executes an asynchronous piece of JavaScript in the context of the current element. Returns the first argument of the callback function.")]
249+
object ExecuteAsyncScript(string script, object arguments = null, int timeout = -1);
250+
251+
[DispId(605), Description("Waits for a piece of JavaScript to return true or not null")]
252+
object WaitForScript(string script, object arguments = null, int timeout = -1);
250253

251254
#endregion
252255

Selenium/ComInterfaces/_WebElements.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ public interface _WebElements : System.Collections.IEnumerable {
2525
[DispId(67), Description("Return and array containing the attribute for each web element")]
2626
List Attribute(string attributeName, bool withAttributeOnly = true);
2727

28-
[DispId(70), Description("Executes a script for each element and returns the results. Ex: return element.tagName;")]
28+
[DispId(601), Description("Executes a script for each element and returns the results. Ex: return element.tagName;")]
2929
List ExecuteScript(string script, bool includeNullResults = false);
3030

31+
[DispId(602), Description("Execute an asynchronous piece of JavaScript and returns the results.")]
32+
List ExecuteAsyncScript(string script, int timeout = -1);
33+
3134
[DispId(86), Description("Returns a list containing the text for each element")]
3235
List Text(int offsetStart = 0, int offsetEnd = 0, bool trim = true);
3336

Selenium/Common/WebElement.cs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ public TableElement AsTable() {
647647
#region Javascript
648648

649649
/// <summary>
650-
/// Executes a JavaScript function in the context of the current element and returns the return value of the function.
650+
/// Executes a piece of JavaScript in the context of the current element.
651651
/// </summary>
652652
/// <param name="script">The JavaScript code to execute.</param>
653653
/// <param name="arguments">The arguments to the script.</param>
@@ -659,27 +659,50 @@ public TableElement AsTable() {
659659
/// </code>
660660
/// </example>
661661
public object ExecuteScript(string script, object arguments = null) {
662-
string newscript = "return (function(){" + script + "}).apply(arguments[0],arguments[1]);";
663-
object[] newargs = FormatArguments(this, arguments);
664-
var result = session.javascript.Execute(newscript, newargs, true);
662+
string script_ex = WrapScript(script);
663+
object[] args_ex = WrapArguments(this, arguments);
664+
var result = session.javascript.Execute(script_ex, args_ex, true);
665+
return result;
666+
}
667+
668+
/// <summary>
669+
/// Execute an asynchronous piece of JavaScript in the context of the current element
670+
/// </summary>
671+
/// <param name="script">The JavaScript code to execute.</param>
672+
/// <param name="arguments">Optional arguments for the script.</param>
673+
/// <param name="timeout">Optional timeout in milliseconds.</param>
674+
/// <returns>The first argument of the callback function.</returns>
675+
/// <example>
676+
/// <code lang="vb">
677+
/// href = ele.ExecuteAsyncScript("callback(this.href);");"
678+
/// </code>
679+
/// </example>
680+
public object ExecuteAsyncScript(string script, object arguments = null, int timeout = -1) {
681+
string script_ex = WrapScript(script);
682+
object[] args_ex = WrapArguments(this, arguments);
683+
var result = session.javascript.ExecuteAsync(script_ex, args_ex, true, timeout);
665684
return result;
666685
}
667686

668687
/// <summary>
669688
/// Waits for a script to return true or not null.
670689
/// </summary>
671-
/// <param name="script"></param>
672-
/// <param name="arguments"></param>
673-
/// <param name="timeout"></param>
674-
/// <returns></returns>
675-
public object WaitForScript(string script, object arguments, int timeout = -1) {
676-
string newscript = "return (function(){" + script + "}).apply(arguments[0],arguments[1]);";
677-
object[] newargs = FormatArguments(this, arguments);
678-
var result = session.javascript.WaitFor(newscript, newargs, timeout);
690+
/// <param name="script">The JavaScript code to execute.</param>
691+
/// <param name="arguments">The arguments to the script.</param>
692+
/// <param name="timeout">Optional timeout in milliseconds.</param>
693+
/// <returns>Value not null</returns>
694+
public object WaitForScript(string script, object arguments = null, int timeout = -1) {
695+
string script_ex = WrapScript(script);
696+
object[] args_ex = WrapArguments(this, arguments);
697+
var result = session.javascript.WaitFor(script_ex, args_ex, timeout);
679698
return result;
680699
}
681700

682-
private static object[] FormatArguments(object item, object arguments) {
701+
private static string WrapScript(string script) {
702+
return "return (function(){" + script + "}).apply(arguments[0],arguments[1]);";
703+
}
704+
705+
private static object[] WrapArguments(object item, object arguments) {
683706
if (arguments == null) {
684707
return new[] { item, new object[0] };
685708
} else if (arguments is IEnumerable && !(arguments is string || arguments is Dictionary)) {

Selenium/Common/WebElements.cs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public List Attribute(string attributeName, bool withAttributeOnly = true) {
106106
}
107107

108108
/// <summary>
109-
/// Execute a script against each web element and returns all the results;
109+
/// Execute a piece of JavaScript against each web element and returns all the results;
110110
/// </summary>
111111
/// <param name="script">Javascript script</param>
112112
/// <param name="ignoreNulls">Null elements are skiped</param>
@@ -123,13 +123,41 @@ public List ExecuteScript(string script, bool ignoreNulls = true) {
123123
if (this.Count == 0)
124124
return null;
125125
var session = ((WebElement)base[0])._session;
126-
object[] args = new object[] { this };
127-
string script2 = "var f=function(){" + script + "};"
128-
+ @"var e=arguments[0],r=[];"
129-
+ "for(var i=0;i<e.length;i++){"
130-
+ (ignoreNulls ? "v=f.apply(e[i]);if(v!=null)r.push(v);" : "r.push(f.apply(e[i]));")
131-
+ "}return r;";
132-
var results = (List)session.javascript.Execute(script2, args, true);
126+
object[] args = { this };
127+
string script_ex
128+
= "var args=arguments[0],arr=[],fn=function(){" + script + "};"
129+
+ "for(var i=0,v;i<args.length;i++){"
130+
+ " v=fn.apply(args[i]);" + (ignoreNulls ? "v && " : "") + "arr.push(v);"
131+
+ "} return arr;";
132+
var results = (List)session.javascript.Execute(script_ex, args, true);
133+
return results;
134+
}
135+
136+
/// <summary>
137+
/// Execute an asynchronous piece of JavaScript against each web element and returns all the results;
138+
/// </summary>
139+
/// <param name="script">Javascript script</param>
140+
/// <param name="ignoreNulls">Null elements are skiped</param>
141+
/// <returns>List</returns>
142+
/// <example>
143+
/// <code lang="vbs">
144+
/// Set driver = CreateObject("Selenium.FirefoxDriver")
145+
/// driver.Get "https://www.google.co.uk/search?q=Eiffel+tower"
146+
/// Set links = driver.FindElementsByTagName("a").ExecuteScript("var e=this; setTimeout(function(){callback(e.getAttribute('href'))}, 100);")
147+
/// WScript.Echo "Count:" &amp; links.Count &amp; " Values:" &amp; vbCr &amp; Join(links.Values, vbCr)
148+
/// </code>
149+
/// </example>
150+
public List ExecuteAsyncScript(string script, int timeout = -1) {
151+
if (this.Count == 0)
152+
return null;
153+
var session = ((WebElement)base[0])._session;
154+
object[] args = { this };
155+
string script_ex
156+
= "var arr=[],args=arguments[0],len=args.length-1,cpt=len;"
157+
+ "var fn=function(callback){" + script + "};"
158+
+ "var cb=function(i){return function(v){arr[i]=v;if(--cpt<1)callback(arr);}};"
159+
+ "for(var i=0;i<len;i++)fn.apply(args[i],cb(i));";
160+
var results = (List)session.javascript.ExecuteAsync(script_ex, args, true, timeout);
133161
return results;
134162
}
135163

Selenium/Core/JavascriptContext.cs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,49 @@ public object Execute(string script, object arguments = null, bool unbox = true)
2929
return result;
3030
}
3131

32+
/// <summary>
33+
/// Inject a snippet of JavaScript into the page for execution in the context
34+
/// of the currently selected frame.
35+
/// </summary>
36+
/// <param name="script">The script to execute.</param>
37+
/// <param name="arguments">Optional - The script arguments.</param>
38+
/// <param name="unbox">Optional - Converts web elements to objects</param>
39+
/// <param name="timeout">Optional - Optional timeout in milliseconds.</param>
40+
/// <returns></returns>
41+
public object ExecuteAsync(string script, object arguments = null, bool unbox = true, int timeout = -1) {
42+
if (timeout != -1)
43+
session.timeouts.Script = timeout;
44+
45+
string script_ex = "var callback=arguments[--arguments.length];" + script;
46+
47+
object result = this.session.Send(RequestMethod.POST, "/execute_async", "script", script_ex, "args", arguments);
48+
if (unbox)
49+
return Unbox(result);
50+
return result;
51+
}
52+
3253
/// <summary>
3354
/// Waits for a script to return true or not null.
3455
/// </summary>
35-
/// <param name="script"></param>
36-
/// <param name="arguments"></param>
37-
/// <param name="timeout"></param>
56+
/// <param name="script">Piece of JavaScript code to execute.</param>
57+
/// <param name="arguments">Optional arguments for the script.</param>
58+
/// <param name="timeout">Optional timeout in milliseconds.</param>
3859
/// <returns></returns>
39-
public object WaitFor(string script, object arguments, int timeout = -1) {
40-
if (!script.TrimStart().StartsWith("return"))
41-
script = "return " + script;
60+
public object WaitFor(string script, object arguments = null, int timeout = -1) {
61+
if (timeout != -1)
62+
session.timeouts.Script = timeout;
4263

43-
if (!script.TrimEnd().EndsWith(";"))
44-
script = script + ";";
64+
bool has_return = (script.IndexOf("return") & -4) == 0;
65+
string script_ex
66+
= "var callback=arguments[--arguments.length];"
67+
+ "var evl=function(){" + (has_return ? string.Empty : "return ") + script + "};"
68+
+ "var tst=function(){"
69+
+ " var res=evl();"
70+
+ " if(res) callback(res); else setTimeout(tst, 60);"
71+
+ "};"
72+
+ "tst();";
4573

46-
object result = this.session.SendUntil(timeout,
47-
() => Execute(script, arguments, false),
48-
(r) => r != null && !false.Equals(r)
49-
);
74+
object result = this.session.Send(RequestMethod.POST, "/execute_async", "script", script_ex, "args", arguments);
5075
return Unbox(result);
5176
}
5277

Selenium/WebDriver.cs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,10 @@ private string ExpendBrowserName(string browser) {
389389
/// </code>
390390
/// </example>
391391
public object Send(string method, string relativeUri,
392-
string param1 = null, string value1 = null,
393-
string param2 = null, string value2 = null,
394-
string param3 = null, string value3 = null,
395-
string param4 = null, string value4 = null) {
392+
string param1 = null, object value1 = null,
393+
string param2 = null, object value2 = null,
394+
string param3 = null, object value3 = null,
395+
string param4 = null, object value4 = null) {
396396

397397
RequestMethod mth = 0;
398398
switch (method.ToUpper()) {
@@ -692,27 +692,50 @@ public Image TakeScreenshot(int delay = 0) {
692692
#region Javascript
693693

694694
/// <summary>
695-
/// Executes JavaScript in the context of the currently selected frame or window
695+
/// Execute a piece of JavaScript in the context of the currently selected frame or window
696696
/// </summary>
697697
/// <param name="script">The JavaScript code to execute.</param>
698698
/// <param name="arguments">The arguments to the script.</param>
699-
/// <returns>The value returned by the script.</returns>
699+
/// <returns>The value specified by the return statement.</returns>
700+
/// <example>
701+
/// <code lang="vb">
702+
/// txt = driver.ExecuteScript("return 'xyz' + arguments[0];", "123")
703+
/// </code>
704+
/// </example>
700705
public object ExecuteScript(string script, object arguments = null) {
701-
object args = FormatArguments(arguments);
702-
object result = session.javascript.Execute(script, args, true);
706+
object args_ex = FormatArguments(arguments);
707+
object result = session.javascript.Execute(script, args_ex, true);
703708
return result;
704709
}
705710

706711
/// <summary>
707-
/// Waits for a script to return true or not null.
712+
/// Execute an asynchronous piece of JavaScript in the context of the current frame or window.
708713
/// </summary>
709-
/// <param name="script"></param>
710-
/// <param name="arguments"></param>
711-
/// <param name="timeout"></param>
712-
/// <returns></returns>
714+
/// <param name="script">Piece of JavaScript code to execute.</param>
715+
/// <param name="arguments">Optional arguments for the script.</param>
716+
/// <param name="timeout">Optional timeout in milliseconds.</param>
717+
/// <returns>The first argument of the callback function.</returns>
718+
/// <example>
719+
/// <code lang="vb">
720+
/// txt = driver.ExecuteAsyncScript("callback('xyz')");"
721+
/// </code>
722+
/// </example>
723+
public object ExecuteAsyncScript(string script, object arguments = null, int timeout = -1) {
724+
object args_ex = FormatArguments(arguments);
725+
object result = session.javascript.ExecuteAsync(script, args_ex, true, timeout);
726+
return result;
727+
}
728+
729+
/// <summary>
730+
/// Waits for a piece of JavaScript to return true or not null.
731+
/// </summary>
732+
/// <param name="script">Piece of JavaScript code to execute.</param>
733+
/// <param name="arguments">Optional arguments for the script.</param>
734+
/// <param name="timeout">Optional timeout in milliseconds.</param>
735+
/// <returns>Value not null</returns>
713736
public object WaitForScript(string script, object arguments, int timeout = -1) {
714-
object args = FormatArguments(arguments);
715-
object result = session.javascript.WaitFor(script, args, timeout);
737+
object args_ex = FormatArguments(arguments);
738+
object result = session.javascript.WaitFor(script, args_ex, timeout);
716739
return result;
717740
}
718741

0 commit comments

Comments
 (0)