Monday, November 24, 2014

Capturing traffic from .Net app to https://localhost using Fiddler

Recently I was trying to capture traffic from my .Net help to a site hosted in IIS Express at  https://localhost:port
The issue was that fiddler wasn't capturing these requests. I found various alternatives like using IP address or machine name but that would results in certificate errors due to host name mismatch.

Finally found the option of using https://localhost.fiddler:port instead of https://localhost:port which would then capture the traffic in Fiddler.

Tuesday, November 18, 2014

Configuring timeout on an existing Task using Extension method

I was using SignalR IHubProxy and wanted to configure timeout on the task returned.
The problem is that I didn't create this task and hence couldn't set a timeout using cancellation token, etc
Thanks to the support from SignalR team, I was able to set Timeout using Extension method
Below is the sample code

public static class TaskExtensions
{
    public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout)
    {
        var delayTask = Task.Delay(timeout);
        var finishedFirst = await Task.WhenAny(task, delayTask);

        if (finishedFirst != delayTask)
        {
            // Task finished before the timeout
            return ((Task<T>)finishedFirst).Result;
        } 

        // Task didn't finish before the timeout
        // Note: Technically the task wasn't canceled at all, it's still running but there's no
        //       way to stop it from here but the caller can handle this exception and retrieve
        //       the task from the InnerException.Task property if further action is desired.
        throw new TaskCanceledException(
            "The task didn't complete within the specified time.",
            new TaskCanceledException(task));
    } 

    private static async void ExampleUsage()
    {
        var exampleAsync = new Func<Task<int>>(() => Task.FromResult(1)); 

        try
        {
            var result = await exampleAsync().TimeoutAfter(TimeSpan.FromSeconds(10));
        }
        catch (TaskCanceledException)
        {
            // The task timed out
        }
    }
}

Wednesday, November 5, 2014

Using FiddlerCore on Remote devices

Some time back, I wrote a blog on FiddlerCore http://www.anujchaudhary.com/2013/05/automated-website-testing-with.html
I had created a windows application using FiddlerCore and was interested in using it on remote devices like WindowsPhone/iOS/Android/etc
After some investigations, I was able to achieve this goal by doing the following:

Create Fiddler Root certificate using CertMaker.dll and BCMakeCert.dll


CertMaker.dll uses the BouncyCastle C# library (BCMakeCert.dll) to generate new certificates which are compatible with iOS devices.
Make sure these dlls are in the folder where your executable and FiddlerCore.dll live.
The root certificate is created in the below method
public void MakeCert()
        {
            if (!CertMaker.rootCertExists())
            {
                if (!CertMaker.createRootCert())
                {
                    throw new Exception("Unable to create cert for FiddlerCore.");
                }
                else
                {
                    Logger.Log("Created Fiddler Root Cert for Https Inspection");
                }
            }
            X509Store certStore = new X509Store(StoreName.Root,StoreLocation.LocalMachine);
            certStore.Open(OpenFlags.ReadWrite);
            try
            {
                certStore.Add(CertMaker.GetRootCertificate());
            }
            finally
            {
                certStore.Close();
            }
        }

Start Fiddler Application on a well-known port


Start Fiddler Application on a well-known port which will allow inbound connections e.g. 9999
FiddlerApplication.Startup(9999, FiddlerCoreStartupFlags.Default);

Use FiddlerCoreStartupFlags.Default


Start FiddlerApplication with FiddlerCoreStartupFlags.Default. This will ensure that remote connections are allowed and https decryption is enabled
FiddlerApplication.Startup(9999, FiddlerCoreStartupFlags.Default);

Enable Echo Service

Echo service needs to be enabled so that the root certificate can be downloaded and installed on the remote devices for https inspection. This is done by setting the below preference
FiddlerApplication.Prefs.SetBoolPref("fiddler.echoservice.enabled", true); 

Configure proxy on Remote devices

1.       On the remote device, go to http://<local machine IP>:9999 and download and install the Fiddler Root certificate
2.       On the remote device, go to network settings and add/change the proxy to local machine IP and port 9999
3.       Now you will be able to dogfood new stuff on the remote device and view metrics/logs on the local windows machine.
 Once the above steps are done, everything else is similar to what’s described in http://www.anujchaudhary.com/2013/05/automated-website-testing-with.html

Enjoy!

Thursday, October 9, 2014

Automating Windows Store apps using Coded UI

Automating Windows Store apps with Coded UI is not as straight forward as automating desktop app. This is because recording actions is not allowed for Windows Store apps.
Here, we will learn how to automate Windows Store apps using Coded UI and launch and close the app programmatically using IApplicationActivationManager

Add a Windows Store Coded UI Test Project

    1.       Add a new Coded UI Test Project from Store App-> Windows Apps

 
 
 
 
 
 
 
 
 

     2.       Select Edit UI Map or add assertions

 
 
 
 
 
 
 
 
3.       Note that recording actions is not allowed for Windows Store apps





 
      4.       Launch the app
      5.       Drag to cross hair on to a control and select Add control to UI Map

 
 
 
 
 
 
 
 
 
6.       Add all controls and click Generate Code->Generate 

 
 
 
 
 
 
 
 
 
 
 
 


      7.       Close the Coded UI Test Builder
        8.       UIMap.uitest will be created with the selected controls

Launch and Close the App

Add the following code
public enum ActivateOptions
    {
        None = 0x00000000,  // No flags set

        DesignMode = 0x00000001,  // The application is being activated for design mode, and thus will not be able to

        // to create an immersive window. Window creation must be done by design tools which

        // load the necessary components by communicating with a designer-specified service on

        // the site chain established on the activation manager.  The splash screen normally

        // shown when an application is activated will also not appear.  Most activations

        // will not use this flag.

        NoErrorUI = 0x00000002,  // Do not show an error dialog if the app fails to activate.                               

        NoSplashScreen = 0x00000004,  // Do not show the splash screen when activating the app.
    } 

    [ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IApplicationActivationManager
    {
        // Activates the specified immersive application for the "Launch" contract, passing the provided arguments

        // string into the application.  Callers can obtain the process Id of the application instance fulfilling this contract.

        IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId);

        IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId);

        IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId);
    }

    [ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]//Application Activation Manager
    class ApplicationActivationManager : IApplicationActivationManager
    {
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/]

        public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] ActivateOptions options, [Out] out UInt32 processId);

        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

        public extern IntPtr ActivateForFile([In] String appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] String verb, [Out] out UInt32 processId);

        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

        public extern IntPtr ActivateForProtocol([In] String appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out UInt32 processId);
    }
Then create the following variables which can be accessed in TestInitialize and TestCleanup
ApplicationActivationManager appActiveManager = new ApplicationActivationManager();

uint pid;
Add the following code in TestInitialize and TestCleanup
[TestInitialize()]
        public void MyTestInitialize()
        {
            appActiveManager.ActivateApplication(appUserModelId, null, ActivateOptions.None, out pid);
        } 
[TestCleanup()]
        public void MyTestCleanup()
        {
            Process.GetProcessById((int)pid).Kill();
        }
The appUserModelId in TestInitialize can be obtained using the following steps:
1.       If you own the source code of the app, find the package name in package.appxmanifest
2.       Run regedit from command prompt
3.       On the device where the app is installed, find the package name at HKEY_CURRENT_USER\Software\Classes\ActivatableClasses\Package
4.       Find the AppUserModelId value in key in \Server\App.App...
5.       For example, the appUserModelId for Calculator app on my device is Microsoft.WindowsCalculator_8wekyb3d8bbwe!App  as shown below

 
 
 
 
 
 
 
  

Automate the App

 Now instantiate the UIMap and automate the app in a test method like below
[TestMethod]
        public void CodedUITestMethod1()
        {
            UIMap uiMap = new UIMap();

            Keyboard.SendKeys(uiMap.UICalculatorWindow.UICalculatorResultsText, "1");

            Mouse.Click(uiMap.UICalculatorWindow.UIPlusButton, new Point(50, 50));

            Keyboard.SendKeys(uiMap.UICalculatorWindow.UICalculatorResultsText, "1");

            Mouse.Click(uiMap.UICalculatorWindow.UIEqualsButton, new Point(50, 50));

            Assert.AreEqual("Display is  2 ", uiMap.UICalculatorWindow.UICalculatorResultsText.DisplayText);
        }