0

Scope

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;
 }

Questions

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;

  • What can i do to cast 'System.Collections.ObjectModel.ReadOnlyCollection' into List?

More Info

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

3 Answers 3

2

You need to deserialize the result into the required List<Timings>.

  1. Refer the package JSON.Net from here
  2. 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

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. As you said to deserialize result it needs to be of type string. This is above not the case: Error CS1503 Argument 1: cannot convert from 'object' to 'string'. I will add more information shortly.
1

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]);
}

1 Comment

This is likely the route i am going to take. But my intend was to understand what was going on. Thanks for your help +1.
0

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 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:

Output of List of dictionaries

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.

Update

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 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.