Reputation: 5944
I want to do automated web testing with Selenium and execute JavaScript. The string func1
contains my js-function and it is passed to ExecuteScript(func1)
it returns an array of objects which look like this {label:'start', time: 121}
.
I want to cast the result of ExecuteScript
into List<timings>
var result = jsExecutor.ExecuteScript(func1);
var list = (ReadOnlyCollection<object>)result;
var timings = (List<Timing>)list;
I received the error
Cannot convert type 'System.Collections.ObjectModel.ReadOnlyCollection<object>'
to 'System.Collections.Generic.List<CoreConsoleApp.TestExecutions.Timing>'
This is func1
string func1= @"var t = window.performance.timing;
var timings = [];
timings.push({ label: 'navigationStart', time: t.navigationStart });
timings.push({ label: 'PageLoadTime', time: t.loadEventEnd - t.navigationStart });
return timings;" // result is an array of js-objects
The code below is a snippet of the selenium part
public struct Timing
{
public string label;
public int time;
}
using (var driver = new FirefoxDriver())
{
...
var jsExecutor = (IJavaScriptExecutor)driver;
var result = jsExecutor.ExecuteScript(func1);
var list = (ReadOnlyCollection<object>)result;
}
The selenium docs state that ExecuteScript attempts to return a List for an array.
Func1 should return array of {label: string, time: number}
it should be easy to cast the result var list = (ReadOnlyCollection<object>)result
into List<string,int> timings = (List<timings>)list;
Start Firefox var driver = new FirefoxDriver()
open a URL driver.Navigate().GoToUrl(url);
find a certain button IWebElement button = driver.FindElement(By.Name("btnK"));
submit the form button.Submit();
After the submission execute JavaScript ExecuteScript(func1)
and write the result to the console
The above all works. But i have trouble to cast the JavaScript into a list of c# objects.
So my workaround is this
var result = jsExecutor.ExecuteScript(func1);
var list = (ReadOnlyCollection<object>)result;
foreach (object item in list)
{
var timing = (Dictionary<string, object>)item;
foreach(KeyValuePair<string, object> kvp in timing)
{
Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}
}
This puts out:
Key = label, Value = navigationStart
Key = time, Value = 1529720672670
Key = label, Value = PageLoadTime
Key = time, Value = 1194
Key = label, Value = DOMContentLoadedTime
Key = time, Value = 589
Key = label, Value = ResponseTime
Upvotes: 0
Views: 1470
Reputation: 5944
The problem is that the structure of var result = jsExecutor.ExecuteScript(func1);
is different than expected.
The result
looks similar to List<object>()
i created this linqpad program:
var dictionary1= new Dictionary<string, object>();
dictionary1.Add("label", (object)"PageloadTime");
dictionary1.Add("time", (object)"1087");
var dictionary2= new Dictionary<string, object>()
{
{"label", (object)"DOMContentLoadedTime"},
{"time", (object)"494"}
};
var list = new List<object>(); // this is the structure of result
list.Add(dictionary1);
list.Add(dictionary2);
list.Dump();
If you call list.Dump();
it looks like this:
As you can see the structure contains n Dictionaries of this type Dictionary<string, object>();
Therefore i tried two nested loops to better understand the nesting of the objects
Object result = jsExecutor.ExecuteScript(func1); //
var resultCollection = (ReadOnlyCollection<object>)result;
foreach (Dictionary<string, object> item in resultCollection)
{
Console.WriteLine("{0} ", item.GetType());
foreach (KeyValuePair<string, object> kvp in item)
{
Console.WriteLine("Keys: {0} Values: {1}", kvp.Key, kvp.Value);
}
}
And finally
// create a structure similar to result
var list = new List<object>();
list.Add(dictionary1);
list.Add(dictionary2);
var timings = new List<Timing>();
foreach (Dictionary<string, object> dict in list)
{
Console.WriteLine("Label = {0} Value ={1} "
, (string)dict["label"]
, (string)dict["time"]);
// create a timing object
var t = new Timing();
t.label = (string)dict["label"];
t.time = (string)dict["time"];
timings.Add(t);
}
Since sometimes i got a invalid cast exception for (int)dict["time"]
i changed the property time from int to string.
As Steven Chong suggested i changed the function func1
to return a string:
public static string jsGetTiming(){
// func1 is the javascript function that will get executed
string func1= @"var t = window.performance.timing;
var PageLoadTime = t.loadEventEnd - t.navigationStart;
var ResponseTime = t.responseEnd - t.requestStart;
var timings = 'navigationStart=' + t.navigationStart;
timings += '|PageLoadTime=' + PageLoadTime;
timings += '|ResponseTime=' + ResponseTime;
return timings;";
return func1;
}
To execute the string func1
as a function you can call it like this
Object result = jsExecutor.ExecuteScript(MyClass.jsGetTiming());
// result is a string and looks like this
result =
navigationStart=1534377023791|PageLoadTime=943
|DOMContentLoadedTime=434|ResponseTime=337
|Response=269|DomainLookup=0
|LoadEvent=5|UnloadEvent=8
|DOMContentLoadedEvent=17
Upvotes: 0
Reputation: 1396
try serialize your data before return.
string func1= @"var t = window.performance.timing;
var timings = [];
timings.push({ label: 'navigationStart', time: t.navigationStart });
timings.push({ label: 'PageLoadTime', time: t.loadEventEnd - t.navigationStart });
return JSON.stringify(timings);" // result is string
and access your data in c# using Json.NET
using Newtonsoft.Json.Linq;
string result = Convert.ToString(jsExecutor.ExecuteScript(func1));
Console.Write("result = " + result);
List<Timing> list = JToken.Parse(result).ToObject<List<Timing>>();
Console.Write("result = " + JToken.FromObject(list));
// or access using dynamic
dynamic dynamicList = JToken.Parse(jsExecutor.ExecuteScript(func1));
for (var i = 0; i < dynamicList.Count; i++) {
Console.Write(dynamicList[i]);
}
Upvotes: 1
Reputation: 4487
You need to deserialize the result
into the required List<Timings>
.
Deserialize the result
(assuming to be a string
) as follows:
List<Timing> timings = JsonConvert.DeserializeObject<List<Timing>>(result);
Here is some basic help on serialization: https://www.newtonsoft.com/json/help/html/SerializingJSON.htm
Upvotes: 2