Trouble accessing some methods of a dynamic object - c#

I'm writing a utility that allows end users to perform some Selenium tests against my company's web app, so far so good. Here's the issue: I want to allow users to choose the type of selenum.webdriver object they are going to implement, based on whether they have chrome or firefox installed on their system.
so, I initialize a list of objects to let us know what the user has selected
public List<object> BuildBrowserAccess(string browserEngine)
{
OpenQA.Selenium.Chrome.ChromeDriver driverC = null;
OpenQA.Selenium.Firefox.FirefoxDriver driverF = null;
if (browserEngine == "Firefox")
{
driverF = new OpenQA.Selenium.Firefox.FirefoxDriver();
}
else if (browserEngine == "Chrome\t")
{
driverC = new OpenQA.Selenium.Chrome.ChromeDriver();
}
List<object> browserEngines = new List<object>();
browserEngines.Add(driverC);
browserEngines.Add(driverF);
return browserEngines;
}
Then back in the main routine, assign whichever is selected (i.e. not null) to a dynamic object then convert the object to static using "as":
List<object> browserEngines = mainActions.BuildBrowserAccess(browserEngine);
if (browserEngines[0] != null )
{
driver = browserEngines[0];
finDriver = driver as OpenQA.Selenium.Chrome.ChromeDriver;
}
else
{
driver = browserEngines[1];
finDriver = driver as OpenQA.Selenium.Firefox.FirefoxDriver;
}
Even at runtime, the conversion looks successful, and I'm able to use some methods of the FirefoxDriver namespace, but many other methods that work just fine with a statically typed object throw a RuntimeBinderException... I'm just about stumped, and any help is appreciated!

Why not create only one driver variable of type IWebDriver of it doesn't have to be specifically initialized, use var?
And in case you need to check the specific type of driver you could do
if(driver.GetType() == typeof(FirefoxDriver))
{
//do your Firefox stuff
}
else
{
//do another browser stuff
}

Related

What parameter i have to assign inside my variable so i can start it from my Main?

im new to C# and Tia Openness and have an problem. I dont know what parameter goes inside my ImportSingleTextList();.Its an example from Siemens but there is never mentioned how to call it inisde the main. That is my code.
private static void ImportSingleTextList(HmiTarget hmitarget)
{
TextListComposition textListsComposition = hmitarget.TextLists;
IList<TextList> importedTextLists = textListsComposition.Import(new FileInfo(#"D:\SamplesImport\myTextList.xml"), ImportOptions.Override);
}
I guess you have to look into your HmiTarget exactly. Is it a class, then you should instantiate a first instance of it; what constructor does this class have - with or without parameters? Click on HmiTarget and see what input it expects.
I guess you class has some kind of enumerable hmitarget.TextLists that you have to fill or get too.
Presumably you have a Project instance. You have to drill down from Project->Device->DeviceItem(->DeviceItem) until you find a DeviceItem that can provide a SoftwareContainer service. It may be that all such DeviceItems reside at the first level below Device; I haven't checked. Anyway, here's a method I wrote that searches the first and second DeviceItem levels:
public static HmiTarget GetHmiTarget(Device hmiDevice)
{
//search first level of DeviceItems
foreach (DeviceItem di in hmiDevice.DeviceItems)
{
SoftwareContainer container =
di.GetService<SoftwareContainer>();
if (container != null)
{
HmiTarget hmi = container.Software as HmiTarget;
if (hmi != null)
return hmi;
}
//search second level of DeviceItems
foreach (DeviceItem devItem in di.DeviceItems)
{
SoftwareContainer subContainer = devItem.GetService<SoftwareContainer>();
if(subContainer != null)
{
HmiTarget hmi = subContainer.Software as HmiTarget;
if (hmi != null)
return hmi;
}
}
}
return null; //nothing was found at the first or second levels
}
to get the Device, you can use PROJECT.Devices.Find(NAME) where PROJECT is your TIA portal project instance, and NAME is the string name of your HMI device.

Using Moq the Start() method keeps retuning a null object

I'm having trouble figuring out why I can't test my driver variable, it keeps coming back null with I call Start(). I basically would like to access that variable and test it.
My current test that isn't working:
[TestMethod]
public void Start_Default_IsChrome2()
{
var dummyManager = new Mock<IRemoteDriver>();
var mockDriver = new Mock<IWebDriver>();
dummyManager.Setup(x => x.CreateRemoteWebDriver(new ChromeOptions()))
.Returns(It.IsAny<RemoteWebDriver>());
var session = new SauceSession(dummyManager.Object);
//The Start() keeps returning a null object
var driver = session.Start();
var capabilities = driver.Capabilities;
capabilities.GetCapability("browserName").Should().Be("chrome");
}
Dependency to be mocked
public interface IRemoteDriver
{
IWebDriver CreateRemoteWebDriver(ChromeOptions chromeOptions);
}
Subject Under Test
public SauceSession(IRemoteDriver driverManager)
{
remoteDriverManager = driverManager;
}
public RemoteWebDriver Start()
{
sauceUserName = Environment.GetEnvironmentVariable("SAUCE_USERNAME", EnvironmentVariableTarget.User);
sauceAccessKey = Environment.GetEnvironmentVariable("SAUCE_ACCESS_KEY", EnvironmentVariableTarget.User);
sauceOptions = new Dictionary<string, object>
{
["username"] = sauceUserName,
["accessKey"] = sauceAccessKey
};
var chromeOptions = new ChromeOptions
{
BrowserVersion = "latest",
PlatformName = "Windows 10",
UseSpecCompliantProtocol = true
};
chromeOptions.AddAdditionalCapability("sauce:options", sauceOptions, true);
//This keeps returning a null
return (RemoteWebDriver)remoteDriverManager.CreateRemoteWebDriver(chromeOptions);
}
If it helps, the Concrete implementation works just fine and that test looks like this:
[TestMethod]
public void Start_Default_IsChrome()
{
var session = new SauceSession();
var driver = session.Start();
var capabilities = ((RemoteWebDriver)driver).Capabilities;
capabilities.GetCapability("browserName").Should().Be("chrome");
}
Everything else is the same except the object that I set here:
public SauceSession()
{
remoteDriverManager = new ConcreteRemoteWebDriver();
}
class ConcreteRemoteWebDriver : IRemoteDriver
{
public IWebDriver CreateRemoteWebDriver(ChromeOptions chromeOptions)
{
return new RemoteWebDriver(new Uri("https://ondemand.saucelabs.com/wd/hub"),
chromeOptions.ToCapabilities(), TimeSpan.FromSeconds(600));
}
}
Here's the RemoteWebDriver:
public class RemoteWebDriver : IWebDriver, ISearchContext, IDisposable, IJavaScriptExecutor, IFindsById, IFindsByClassName, IFindsByLinkText, IFindsByName, IFindsByTagName, IFindsByXPath, IFindsByPartialLinkText, IFindsByCssSelector, ITakesScreenshot, IHasInputDevices, IHasCapabilities, IHasWebStorage, IHasLocationContext, IHasApplicationCache, IAllowsFileDetection, IHasSessionId, IActionExecutor
You've done your setup improperly.
dummyManager.Setup(x => x.CreateRemoteWebDriver(new ChromeOptions()))
.Returns(It.IsAny<RemoteWebDriver>());
Two things here:
You're matching on precisely an instance of new ChromeOptions(). When determining which object to return, Moq will check if the arguments passed to CreateRemoteWebDriver are the same as the ones provided in the setup. It's unlikely that
new ChromeOptions
{
BrowserVersion = "latest",
PlatformName = "Windows 10",
UseSpecCompliantProtocol = true
};
and
new ChromeOptions()
will evaluate as equal, meaning that this setup won't be matched.
You probably just meant to use It.IsAny<ChromeOptions>(), like this
dummyManager.Setup(x => x.CreateRemoteWebDriver(It.IsAny<ChromeOptions>()))
The second issue is that your return value is explicitly null.
It.IsAny<T>() always returns the default value for T. The It methods are all only used for argument matching within the Setup expression. If you use them outside of a setup expression, you're just going to get the default value of the generic argument, which in this case is a null value. It's a shame that the Moq library doesn't make this improper usage a loud error message. Consequently, you'll need to provide an actual instance of RemoteWebDriver as a return value. (Or if you can decouple the implementation from a particular concrete type, you could just return something that implements IWebDriver.)
That value could be another Mock object, potentially, but it needs to be something you've either created ahead of time, or something that can be created via the Returns callback.
A correct setup might look something like:
var mockDriver = new Mock<RemoteWebDriver>();
dummyManager.Setup(x => x.CreateRemoteWebDriver(It.IsAny<ChromeOptions>()))
.Returns(mockDriver.Object); //This could throw an exception if RemoteWebDriver needs arguments.
A small caveat is that you will actually create a RemoteWebDriver instance as a result. If that has undesirable side effects (such as creating a chrome window), you will want to consider changing your strategy from using a particular concrete type to some interface or abstract class. If you did that, the setup might look something like the below:
var mockDriver = new Mock<IWebDriver>();
dummyManager.Setup(x => x.CreateRemoteWebDriver(It.IsAny<ChromeOptions>()))
.Returns(mockDriver.Object);

How do I find the data type of an AD attribute not having a value

I am struggling to find the data type of AD attributes that are not having a value already.
Thus far a resulting DirecoryEntry only contains a property for attributes that already have a value. I can't find a method to obtain information about all other attributes.
Adding a value to the 'PropertiesToLoad' doesn't seem to do anything. The returned DirectoryEntry object contains all attributes (with values) regardless of what is added here.
Code used:
public void Test(string ldapPath)
{
Type orgType;
try
{
using (DirectoryEntry searchRoot = GetSearchRoot(ldapPath))
{
using (DirectorySearcher search = new DirectorySearcher(searchRoot))
{
search.Filter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=coen))";
search.PropertiesToLoad.Add("msExchHomeServerName");
SearchResult searchResult = search.FindOne();
{
using (DirectoryEntry entry = searchResult.GetDirectoryEntry())
{
if (entry != null)
{
if (entry.Properties.Contains("msExchHomeServerName"))
{
orgType = entry.Properties["msExchHomeServerName"].GetType();
}
else // The value is empty and NOT present in the current 'entry.Properties' object.
{
throw new Exception("Unknown type");
}
}
}
}
}
}
}
catch (Exception e)
{
<handle exception>
}
}
Is there a way to find the data type of the 'msExchHomeServerName' (or any of the 'empty' attributes) attribute?
Any help would be highly appreciated!
Coen
Active Directory has a schema. Obvious to say, not obvious to think about since, by default, you cannot explore it.
However, you can Register the Active Directory Schema MMC Snap-In1 and then, in a fresh instance of MMC, add that Snap-In.
This then allows you to explore the Classes and Attributes that make up your current AD schema (and add new classes/attributes if you know what you're doing and choose to do so).
msExchHomeServerName can then be discovered to be a "Unicode String", which means a plain string from C# should be acceptable. Note also that some types (particularly numeric ones) may also specify Minimums and Maximums which should be observed.
You can also explore the schema programatically via the ActiveDirectorySchema class, by e.g. calling ActiveDirectorySchema.GetCurrentSchema(); and then explore from there.
1I cannot remember if you need to have installed the general Domain Admin tools (such as Users and Computers) in order for that DLL to exist on your system.
Thanks to Damien_The_Unbeliever who pointed me in the right direction, I managed to create the following method:
public Dictionary<string, ActiveDirectorySyntax> GetAttributeSyntaxes(List<string> lstAttributeNames)
{
Dictionary<string, ActiveDirectorySyntax> dictRes = new Dictionary<string, ActiveDirectorySyntax>();
if (lstAttributeNames.Count > 0)
{
DirectoryContext directoryContext = new DirectoryContext(DirectoryContextType.DirectoryServer,
m_Server, m_UserName, m_Password);
using (ActiveDirectorySchema currentSchema = ActiveDirectorySchema.GetSchema(directoryContext))
{
using (ActiveDirectorySchemaClass objClass = currentSchema.FindClass("user"))
{
if (objClass != null)
{
ReadOnlyActiveDirectorySchemaPropertyCollection propcol = objClass.GetAllProperties();
foreach (ActiveDirectorySchemaProperty schemaProperty in propcol)
{
foreach (string attrName in lstAttributeNames)
{
if (schemaProperty.Name.Equals(attrName))
{
dictRes.Add(attrName, schemaProperty.Syntax);
break;
}
}
}
}
}
}
}
return dictRes;
}
The returned 'schemaProperty.Syntax' contains sufficient information to determine the actual data type.
Thanks Damien!

Is it possible to execute C# code represented as string?

On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.

Selenium IWebElement to PhantomJSWebElement

Im using Ghost Driver (PhantomJS) in my C# project. I have a question.
Selenium has PhantomJSWebElement and PhantomJSDriver.
Im creating PhantomJSDriver
PhantomJSDriverService service = PhantomJSDriverService.CreateDefaultService();
service.IgnoreSslErrors = true;
service.LoadImages = false;
service.Start();
PhantomJSDriver ghostDriver = new PhantomJSDriver(service);
And then trying to find elements by xpath
List<string> retVal = new List<string>();
var aElements = ghostDriver.FindElementsByXPath("//div[#id='menu']//a[#href]");
foreach(PhantomJSWebElement link in aElements)
{
try
{
retVal.Add(link.GetAttribute("href"));
}
catch (Exception)
{
continue;
}
}
So i have an error while casting IWebElemet to PhantomJSWebElement.
PhantomJSWebElement el = (PhantomJSWebElement)link;
also not works (throwing casting exception). So the question is, how to get PhantomJSWebElement by PhantomJSDriver returns only IWebElement (or a collection of them) while finding.
In general, you should not be using the browser-specific classes when using the .NET bindings. Instead, you should be coding to the interfaces that you expect. This allows you to substitute in different implementations as needed. Your code would look something like this:
PhantomJSDriverService service = PhantomJSDriverService.CreateDefaultService();
service.IgnoreSslErrors = true;
service.LoadImages = false;
// Not sure I'd use Start here. The constructor will start the service
// for you.
service.Start();
// Use the IWebDriver interface. There's no real advantage to using
// the PhantomJSDriver class.
IWebDriver ghostDriver = new PhantomJSDriver(service);
// ...
List<string> retVal = new List<string>();
var aElements = ghostDriver.FindElements(By.XPath("//div[#id='menu']//a[#href]"));
// Use the IWebElement interface here. The concrete PhantomJSWebElement
// implementation gives you no advantages over coding to the interface.
foreach(IWebElement link in aElements)
{
try
{
retVal.Add(link.GetAttribute("href"));
}
catch (Exception)
{
continue;
}
}
You should also note that there is every possibility that the class documentation is incorrect. Knowing the source code for the language bindings, the PhantomJSWebElement class is never actually instantiated anywhere. I believe what you're actually getting back from your FindElements() call are RemoteWebElements, so attempting to cast them down the inheritance hierarchy to the more specific subclass is doomed to failure.

Categories

Resources