Calling .Net function on UI test instance in TestComplete - wpf

I have a simple wpf app which has a button that increments a value on clicking. I also have a function that returns the latest value. The default value is 5. I also have a UI test in testcomplete that clicks the button 3 times (so 8). I need to call the .Net function to get this value and assert it. Below is my test code.
After some search I figured out the CLRbridge details and implemented it. However, As you can see below, the UI test instance and the instance on which I am claling the function are different. So, the function returns 5.
My question is, how do I invoke the function from the same instance loaded by testcomplete. Or am I going completely the wrong way for this? I tried both script and UI test with if..then, nothing worked. I have tried both direct instance and calling on the appdomain, both doesnt seem to work.
NOTE: I do understand that I can display the value in a UI control and validate the control. However, i am specifically trying this out for a more complex testing functionality we need in a project.
function Test2()
{
var Increment;
Increment = 0;
//Runs the "TCompTest" tested application.
TestedApps.TCompTest.Run();
//Clicks the 'button1' button.
Aliases.TCompTest.HwndSource_MainWindow.MainWindow.Grid.button1.ClickButton();
//Clicks the 'button1' button.
Aliases.TCompTest.HwndSource_MainWindow.MainWindow.Grid.button1.ClickButton();
//Clicks the 'button1' button.
Aliases.TCompTest.HwndSource_MainWindow.MainWindow.Grid.button1.ClickButton();
//Increment = dotNET.Incrementer.Incr1.zctor().IntValue(true);
Increment = dotNET.Incrementer.Incr1.zctor().IntValue(true);
**OR**
Increment = Sys.Process("TCompTest").AppDomain("TCompTest.exe").dotNET.Incrementer.Incr1.zctor().IntValue(true)
// if(Increment == 8)
// {//Posts an information message to the test log.
Log.Message(Increment);
// }
//Closes the 'HwndSource_MainWindow' window.
Aliases.TCompTest.HwndSource_MainWindow.Close();
}

It should be possible to do what you need from TestComplete. But first of all, to avoid misunderstanding, let me explain the problems with the approaches you tried:
Addressing a class through the "dotNET" object.
When you do this, TestComplete initializes .NET in its service process, loads the specified assembly into it, and works with the classes of this assembly loaded to TestComplete's AppDomain (though living in a separate process). This means that this instance of your assembly has nothing to do with your tested application. So, you can't access your application's data through the dotNET object.
Addressing the Incrementer assembly through the tested application's AppDomain.
OK, in this case you are closer to a solution - you work with the AppDomain of the tested application, so you can access the application's data. However, in your code, you create a new instance of the Incr1 class (via calling zctor). This means that the new class instance will initialize its counter in the constructor, and it will be 5. And this is the value you are getting in your code.
So, the right approach:
Unless the counter field of the Incr1 class containing the current counter value is a static field, you need to address an existing object of the Incr1 class to get the current value of the property, not to create a new class instance. The actual implementation will depend on where you are storing the Incr1 class instance reference in your application. Let's suppose, you store the reference in the Counter property of the MainWindow object:
// Creating an instance of the class somewhere in your code
MainWindow.Counter = new Incr1();
// ...
// And this line of code is in the button click handler
MainWindow.Counter.Increment();
In the described case, you will be able to get the current counter value in your TestComplete script as follows:
var MainWnd = Aliases.TCompTest.HwndSource_MainWindow.MainWindow;
Log.Message(MainWnd.Counter.IntValue(true));
If your setup is different, please describe it - I will try to help accordingly.

Related

Move specific application to a specific screen

I have read over here how to move an application to a specific screen.
In my case I have a variation of this. In this case I want to open for example Todoist on a specific screen. This code below opens Todoist but on my wrong screen.
How can I solve this?
local screens = hs.screen.allScreens()
hs.application.open("Todoist")
local win = hs.application:findWindow("Todoist")
win.moveToScreen(screens[1])
findWindow() is an instance method, so it cannot be called directly as hs.application:findWindow(). To properly call this method, you must create an instance of the hs.application class and then call findWindow() on that instance.
The following snippet should work, although you may need to adjust the wait time (and the screens index). It is generally recommended to use hs.application.watcher to watch for when an app has been launched, rather than using a timer.
local notes = hs.application.open("Notes")
hs.timer.doAfter(1, function()
-- `notes:mainWindow()` will return `nil` if called immediately after opening the app,
-- so we wait for a second to allow the window to be launched.
local notesMainWindow = notes:mainWindow()
local screens = hs.screen.allScreens()
notesMainWindow:moveToScreen(screens[1])
end)

titanium alloy global functions

i have a question, are the global functions set in alloy.js runs every time the app is started or every time a new window is open?
because i actually would like to create a unique user id for the user and assign it a global variable so it can be used through the app but i ain't sure if it will re-create and re-assign the generated user id whenever the app tries to open another window or will it use same user_id that the app generated after instantiating the mobile app.
hope someone could answer me. >.<
The functions in alloy.js will run every time the app is loaded, and they will load near the beginning of the execution of the app. So the answer is NO, your unique ID will NOT be re-created every time a new window opens.
From the Titanium DOC:
The initializer file app/alloy.js can be used to execute some code near the beginning of the application's lifecycle. The contents of this file will be executed right before the initial index.js controller is loaded, allowing you to execute code before any UI components are loaded and to override builtin Alloy functions before they are executed.
Also, here's an example of how to run a function and store the result in a global variable (your unique_id function would work the same way):
Alloy.Globals.myId = function(){
var id = (CODE TO GENERATE ID)
return id;
}

Implementing Google Analytics 2 in IOS 6

While updating an app to ios 6, I found google analytics also needs update.
Following this method, I discovered it needs a ViewController which extends GAITrackedViewController class
https://developers.google.com/analytics/devguides/collection/ios/v2/
The guide mentions the following code in ViewDidLoad method, whereas I want it in some specific method. It doesn't seems to work. Kindly advise.
self.trackedViewName = #"My App";
Using GAITrackedViewController means giving up control over when [tracker trackView:viewName] gets called. Setting the trackedViewName allows GAITrackedViewController to call [tracker trackView:self.trackedViewName] whenever it wants (presumably in viewDidAppear).
Also, GAITrackedViewController uses a KVO on this property, so view tracking doesn't get turned on until self.trackedViewName = #"view name" is performed.
Calling this in viewDidLoad assures that it's setup correctly by the time the first viewDidAppear gets called.

"The calling thread must be STA, because many UI components require this." Error in WPF?

I am creating a xps document as below.
Assembly assembly = Assembly.GetExecutingAssembly();
//read embedded xpsDocument file
Stream helpStream = assembly.GetManifestResourceStream(resourceNameOfContext);
if (helpStream != null)
{
Package package = Package.Open(helpStream);
string inMemoryPackageName = "memorystream://" + topicName + ".xps";
Uri packageUri = new Uri(inMemoryPackageName);
//Add package to PackageStore
PackageStore.AddPackage(packageUri, package);
docXps = new XpsDocument(package, CompressionOption.Maximum, inMemoryPackageName);
}
return docXps;
When i am trying to get docXps.GetFixedDocumentSequence();
I am getting the above error. Can anyone help?
Thanks,
Your problem has nothing to do with the code surrounding the creation or use of the XPS document. It has everything to do with what thread you are running under.
You will receive the The calling thread must be STA, because many UI components require this error whenever any of the following are attempted on a MTA thread:
You construct any object derived from FrameworkElement (including Controls and Panels)
You construct any object derived from BitmapEffect
You construct any object derived from TextComposition
You construct any object derived from HwndSource
You access the current InputManager
You access the primary KeyboardDevice, StylusDevice, or TabletDevice
You attempt to change the focus on a FrameworkContentElement
You provide mouse, keyboard or IME input to any control that accepts text input
You make WPF content visible or update its layout
You manipulate the visual tree in such a way as to cause a re-evaluation for rendering
Several other changes, mostly having to do with display and input
For example, I received this error last year when I tried to deserialize some XAML that contained <Button> and other WPF objects from within a WCF service. The problem was simple to solve: I just switch to a STA thread to do the processing.
Obviously most work with XPS documents will trigger one or more of the above conditions. In your case I suspect that GetFixedDocumentSequence ends up using TextComposition or one of its subclasses.
No doubt the my solution of switching to a STA thread will also work for you, but first you need to figure out how your code that works with XpsDocuments is getting executed run from a MTA thread. Normally any code from from the GUI (eg a button press) is automatically run in a STA thread.
Is it possible that your code that manipulates XPS Documents may be being executed without a GUI? From a user-created thread? From a WCF service or a web service? From an ASPX page? Track that down and you'll probably find your solution.
If that doesn't work, let us know the details of the path through which GetFixedDocumentSequence is called, so we can diagnose it. The directly surrounding code isn't nearly as important as the call stack and how it is originally being invoked. If it is hard to explain you probably should add a call stack to prevent misunderstandings and help us diagnose the problem further, or tell you how to start a STA thread in your particular situation.
Is your code trying to access the xps doc from a background thread? If this is the case, you'll want to use the dispatcher. Info on that here.
If this doesn't help, could you post the code where you're actually calling GetFixedDocumentSequence()?

How to "bypass" constructor call if instance is not null (WinForms designer code)

When we place a component on a Windows Form, the default behavior for the designer is to serialize a contructor call in the IntializeComponent method of the form.
Is there any way to have a custom serializer output the following code:
if (componentInstance == null)
componentInstance = new componentClass();
instead of just:
componentInstance = new componentClass();
The reason is simply that the component is instantiated before the Form, and I want to pass it to one of the Form's constructors.
The component is designed to allow quick selection of properties (in the same way you add your DataBinding to Controls) at design time, but the call to the constructor in InitializeComponent() assumes I want to create a new instance when in fact, I already have a reference to it. By checking for null, then it would work both at design time and run time.
Thank you.
It's simple really - don't use the Form Designer to add your component to the Winform. Instead manually add a member variable of the component type that you are after into the underlying code (.cs) file and handle it directly in code.
Added in response to your comment
As far as I am aware, there is no way in which you can avoid this behaviour for components placed at design time. You could delete references from initialise components method, but this may be overwritten by the designer at some point.
Taking into account your comment below, then I would suggest that you have a member variable on the form (as suggested above) and then set this to refer to the design time component if the runtime variable is null or the runtime variable if it isn't.
i.e.
CSomeComponentType liveComponent;
...
this.liveComponent = runtimeVariable;
if(this.liveComponent == null)
{
this.liveComponent = this.designTimeComponent;
}

Resources