given 2 applications:
1 WPF application, where not all controls have a AutomationId, and which I cannot change. In addition the application adds controls at runtime without setting the AutomationId.
1 console application which automates the above WPF application
I need that, because I want to access all elements within nearly the same amount of time. (searching for an automationelement right before using seems to differ a lot (from ms to s) in the time - depending on the amount of elements / and tree-tiers)
I would like to set AutomationIds of WPFs controls within the console application during WPFs runtime. Would be great to know, if you can think of any solution for this problem!
What I have tried until now:
1.)Reflections
Type type = element.GetType();
FieldInfo fi = type.GetField("AutomationIdProperty");
fi.SetValue(element, "x"); //Error
Error message: "Object of type 'System.String' cannot be converted to type 'System.Windows.Automation.AutomationProperty'" But I would like to hand over a value, not a property type...
if I use the following instead, it throws no error, but changes nothing in the XAML
fi.SetValue(element, AutomationElement.AutomationIdProperty);
2.)directly
AutomationElement element; // = my AutomationElement
element.Current.AutomationId = "x"; //since AutomationId is Readonly - its not possible
3.) DependencyObjects and DependencyProperties seem also promising, but I couldn't come up with an solution so far. Does someone have experience with that??
IDK how it is possible but WPF Inspector is exactly able to do what I was looking for (now I need to know how they attach to the WPF application :) ).
____OLD ANSWER____
It seems impossible to change the XAML of other programs. If the developers are "too lazy" to set the AutomationId, I've come up with a alternative solution.
The automation app iterates over all controls in the beginning, giving them unique names which are stored in a dictionary, together with their references. In case a component gets added/deleted/changed in the hierarchy, the component and their descendants get deleted in the dictionary and the app re-iterates over this sub-tree again.
Related
I am attempting to use Specflow to automate web tests using Selenium. So far, things are going mostly fine, but I am now running into a problem. One of my steps allow for a user to input a variable, the step looks like this:
Given I click the (VARIABLE) Menu
And the code behind it is fairly simple, just clicking on a link based on the text that is passed:
driver.FindElement(By.XPath("Xpath to get to the variable")).Click();
However, there is a later step that must use this information. That is fine, you can use "ScenarioContext.Current.Add(string, variable)" and I know about that and have been using it. It functions for the needs that I was first informed of.
My problem is that now the business wants to be able to add multiple items at the same time. This presents two problems. Attempting to just call the step a second time throws an exception: "An item with the same key has already been added." and if I put this into a Scenario Outline, which would allow me to call the variable a second time in a second run, I cannot use the first variable in the final step.
Logically, this means that passing in a variable multiple times is the problem (which makes sense, given it's passing in as a string) and so passing the variable in as an array seems the logical way to go. The idea is that when I pass the parameter from one step to another as an array instead of as a string I theoretically won't run into this error and then I will be able to iterate through the items in the array in that later step with a for loop. This seems like something that SpecFlow should be able to do, but I am having issues finding out just how to achieve this. Does anyone have an idea on how to do this? I attempted to merely use:
Scenario.Context.Current.Add(string, variable).ToArray();
However, that does not work, and all of the examples of "ToArray" I can find in the SpecFlow documentation doesn't seem to be actually changing the variables you pass from one step to another into an array, it seems to be used solely inside of individual steps and never passed between steps. Is passing parameters using ScenarioContext.Current.Add(string, variable) as an array possible in SpecFlow?
Thanks in advance.
the simplest solution to your problem is to add an array (or list) to the context in the first step and then to get it out and add to it and then replace it again in future steps:
List<string> list = new List<String>();
list.Add(variable)
ScenarioContext.Current.Add(name, list);
then later
List<String> currentList = (List<String>) ScenarioContext.Current[string];
currentList.Add(variable);
ScenarioContext.Current[name]=list;
However I feel duty bound to point out some issues with your current solution. You should investigate the PageObject pattern and hide your element selection XPath inside your page objects. Imagine the business decides to change the element that information is stored in. Now you have to change every test that does this:
driver.FindElement(By.XPath("Xpath to get to the variable")).Click();
for that variable. Using the page object pattern this is hidden inside the page object and you would only have a single place to change.
I personally would also consider sharing data using context injection as I find this allows strong typing of the data (so no cast is required like in the example above) and it allows you to know what data is stored, its not just a random bag of stuff).
I recently started tampering with Windows store apps. I'm having trouble referencing my objects properly. At first, when trying to automatically update a ScrollViewer object when a ComboBox object changed, I couldn't get the value of the changed index.
Using menuDay.SelectedIndex within my menuDay_SelectionChanged event caused a System.NullReferenceException. I fixed that by referencing my menuDay object as such:
((ComboBox)sender).SelectedIndex
Now, within the same menuDay_SelectionChanged event, I'm getting the same error when I try accessing the content of my ScrollViewer, outSetList.
I'm trying to set my ScrollViewer's content to a list of exercise activities. Too much to post. But I concatenate them using a single string.
string += "this stuff\n" repeat.
And then I try to set it into the ScrollViewer
outSetList.content = string
I get the same System.NullReferenceException error. It tells me that the object reference is not set to an instance of an object.
I'm pretty familiar with C#, objects, instances but nothing I've tried seems to have worked. I tried casting it, creating a variable using it as a type(outSetlist setlist = new outSetList) among other things.
I'm sure it's a fairly simple answer but I can't find one anywhere.
I have a specific scenario where parts of an Internet Explorer are accessible through COM and IHtmlElement, but not (directly) through the GUI Tree of AutomationElements. (I have to use the tree/hierarchy to find my element. And the GUI Tree of that particular Internet Explorer is broken (don't know whether it is through customization or because it is an old version - it is a given).)
Now there are properties of the AutomationElement that I need and cannot get off the IHtmlElement (actually, getting properties off IHtmlElement for this version of IE is broken, but getting them off AutomationElement works).
My naive approach is to simply get the coordinates of the IHtmlElement (recursively via offsetParent), and then convert them to an AutomationElement using AutomationElement.FromPoint(x, y). This of course is not really stable (though working kind of ok).
I tried converting through Window Handles, but.. HTMLElements seem to only possess the Window Handle of the outmost element (else websites would use up a lot of handles, I understand).
I tried some more "fancy" approaches trying to get (again) a COM object with IID_IAccessible interface (which seems to work if I use the Main Document as a IServiceProvider), but unfortunately, i do not know later how to make an AutomationElement out of my IAccessible object (idea from IHTMLElement -> IAccessible ).
Any help with this approach or my general problem as stated above is very welcome!
Some rough code to give you an idea on my approach (though now I see that I should not use mMainDocument in the first place, but rather my current element - but this fills ae with null):
Guid IID_IAccessible = new Guid("618736E0-3C3D-11CF-810C-00AA00389B71");
IServiceProvider sp = (IServiceProvider)mMainDocument;
Object ae = null;
sp.QueryService(ref IID_IAccessible, ref IID_IAccessible, out ae);
System.Windows.Automation.AutomationElement ae2 = System.Windows.Automation.AutomationElement.FromLocalProvider((System.Windows.Automation.Provider.IRawElementProviderSimple)ae);
// ae is no AutomationElement, and the last line (with ae2) fails
In my test application, I am constantly opening and re-opening a form. Everytime the form is opened, I must get all the elements on the form into an AutomationElementCollection so that I can operate on the elements. However, it seems expensive to repeatedly get these elements (due to tree navigation/context-switches etc.).
I attempted to set a boolean around the method that gets the elements. If the method was called for the first time, it would run normally, and set the boolean to true. If the method gets called a second time it will do nothing, as the array has already been populated.
However when I try to perform operations on any AutomationElement in the array (for a second time), the elements do not seem to be available. Does closing the form somehow "disable" these elements? Do I HAVE to find these elements each time I open the form, so that they are "fresh"?
I looked at the CacheRequest way, but that seems to only pertain to accessing properties/patterns, not elements.
Here is code/error message:
AutomationElement GAP;
AutomationElementcollection GAP1;
private bool initGAP1 = false;
public void initGAP()
{
if (!initGAP1)
{
int refnum = ...;
int refnum2 = ...;
AutomationElementCollection temp = MMChildren[refnum].FindAll(TreeScope.Children, findCondition);
GAP = temp.FindAll(TreeScope.Children, findCondition)[refnum2];
GAP1 = GAP.FindAll(TreeScope.Children, findCondition); //this contains the elements I want to operate on
initGAP1 = true;
}
}
System.Windows.Automation.ElementNotEnabledException: Exception of type 'System.Windows.Automation.ElementNotEnabledException' was thrown.
You would need to re-get the Automation Elements for each new window. As I understand the UI Automation framework it gives you the means to investigate running windows. It will collect information with different techniques, depending on what kind of framework the target application uses. In your case, if you create and destroy instances of windows, they are treated as different AutomationElements since they are different windows (basically they have different window handles in the OS). Even if the underlying controlling code is the same they are different instances towards the OS, and therefore UI automation.
If you experience that you are suffering from the performance in the traversion, it might be worth considering to use the UI Automation COM API instead, that is vastly faster on some operations.
I am having a problem overriding the GetHeight method when developing an iPad application with MonoTouch.Dialog. I am implementing IElementSizing but my GetHeight method never gets called.
Has anyone else ran into this problem? Thanks.
I made sure that my root.UnevenRows = true;
I also tried including the dialog project and placing a breakpoint in the GetHeight() for any of the Elements that implement IElementSizing with no luck. In a previous iPhone project this worked fine but on the iPad I am still stuck. Are there any other 'gotchas' that I could be missing?
This is sort of a bug in MonoTouch.Dialog. Basically, when you assign the Root Element it wants to know whether there are uneven rows so it can create and cache the appropriate UITableViewSource object.
However, if you add Sections with Elements that implement IElementSizing to the Root element after the Source object has already been created. Then it will not call your GetHeight override. An example of this is if you fetch data async and callback and add the resulting Sections / Elements to to the RootElement...or essentially anytime you add Sections / Elements after PrepareRoot() is called.
There is a simple workaround for this:
this.Root.UnevenRows = true;
Without looking at your code however, I cannot say if this is the bug you are seeing.