Eval with WatiN

I just run into a situation where, for the tests, I need to run a script in IE, and get result back for the test. The problem is that IE doesn't expose anything like the eval() command in javascript. It does, however, exposes an execScript() command, but this has no return value. A bit of thinking produced this code:

protected string Eval(string code)

{

       string elementId = Guid.NewGuid().ToString();

       string create =string.Format(@"

var elem = document.createElement('INPUT');

elem.id = '{0}';

elem.type = 'HIDDEN';

elem.value = {1};

document.body.appendChild(elem);

",elementId, code);

       RunScript(create);

       string result = ie.TextField(elementId).Value;

 

       string remove = string.Format(@"

var elem = document.getElementById('{0}');

document.body.removeChild(elem);

", elementId);

       RunScript(remove);

       return result;

}

 

protected void RunScript(string js)

{

       IHTMLWindow2 window = ((HTMLDocument) ie.HtmlDocument).parentWindow;

       window.execScript(js, "javascript");

}

The isn't very pretty code, but it works. We create a new hidden input field element, set its value to whatever the code that we were given is doing, getting the value of the field, and then remove it. Simple when I see it now, but it was fairly hard to figure out.

Print | posted on Tuesday, April 03, 2007 3:11 PM

Feedback


Gravatar

# re: Eval with WatiN 4/4/2007 12:22 AM Stan Mitranic

Another approach might be to insert the following into the DOM:
<SCRIPT>
function myeval() { return ... code goes here... }
</SCRIPT>

Then just execute that function using the document.Script property (defined on IHTMLDocument), which would allow you to capture the return value.

It's a bit of a pain to use though because it only exposes the IDispatch interface so you either have to use reflection to make the appropriate call or use a loosely-typed language (Boo? ;)


Gravatar

# re: Eval with WatiN 4/4/2007 12:35 AM Ayende Rahien

@Stan,
Can you give ore details about it?
document.execScript() has no return value.


Gravatar

# re: Eval with WatiN 4/4/2007 12:58 AM Stan Mitranic

Ayende,

Here is a quick example:

Assuming you have already inserted the following into the DOM:
<script>
function myeval() {
return 33 + 44;
}
</script>

The following code:
Type scriptEngType = ie.HtmlDocument.Script.GetType();
int result = (int)scriptEngType.InvokeMember("myeval", BindingFlags.InvokeMethod, null, ie.HtmlDocument.Script, null);
Console.WriteLine("Result: {0}", result);

...will print out: Result: 77

Hope that helps.


Gravatar

# re: Eval with WatiN 4/4/2007 1:15 AM Ayende Rahien

@Stan,
VERY cool!
I wasn't aware of this possiblity


Gravatar

# re: Eval with WatiN 4/5/2007 9:57 PM Jeff Brown

@Stan: Neat trick!

This is what I put into our little WebTestFramework here. It's similar to WatiN.

/// <summary>
/// Evaluates the specified JavaScript code within the scope of this
/// document. Returns the value computed by the last expression in the
/// code block after implicit conversion to a string.
/// </summary>
/// <example>
/// The following example shows an alert dialog then returns the string "4".
///
/// EvaluateJavaScript("window.alert('Hello world!'); 2 + 2");
///

/// </example>
/// <param name="javaScriptCode">The JavaScript code</param>
/// <returns>The result converted to a string</returns>
/// <exception cref="JavaScriptException">Thrown when the JavaScript code cannot be evaluated
/// or throws an exception during evaluation</exception>
public string EvaluateJavaScript(string javaScriptCode)
{
const string resultPropertyName = "___expressionResult___";
const string errorPropertyName = "___expressionError___";

string exprWithAssignment = "try {\n"
+ "document." + resultPropertyName + "= String(eval('" + javaScriptCode.Replace("'", "\\'") + "'))\n"
+ "} catch (error) {\n"
+ "document." + errorPropertyName + "= 'message' in error ? error.name + ': ' + error.message : String(error)\n"
+ "}";

string result = null, error = null;

Browser.Sync(delegate
{
// Prepare a property to acquire the result.
IHTMLDocument2 domDocument = (IHTMLDocument2) DomReference;
IHTMLWindow2 domWindow = domDocument.parentWindow;
IExpando domDocumentExpando = (IExpando) domDocument;

PropertyInfo resultProperty = InitializeExpandoProperty(domDocumentExpando, resultPropertyName);
PropertyInfo errorProperty = InitializeExpandoProperty(domDocumentExpando, errorPropertyName);

// Run the script.
// The result value is not meaningful.
domWindow.execScript(exprWithAssignment, "JavaScript");

// Obtain the result and error.
result = (string)resultProperty.GetValue(domDocument, null);
error = (string)errorProperty.GetValue(domDocument, null);
});

if (error != null)
throw new JavaScriptException(error);

return result;
}

private static PropertyInfo InitializeExpandoProperty(IExpando expando, string propertyName)
{
PropertyInfo propertyInfo = expando.GetProperty(propertyName, BindingFlags.Instance);
if (propertyInfo == null)
propertyInfo = expando.AddProperty(propertyName);

propertyInfo.SetValue(expando, null, null);
return propertyInfo;
}

Comments have been closed on this topic.