New WPF Quick Tips Series

Hello Morning Dew readers.

I wanted to let my WPF loving readers know that I’ve started a new series of WPF quick tips. This will be a weekly (or more frequent) series of WPF tips based on my experiences developing WPF applications over the last 10 years or so. Most of the tips will be simple, quick hints. Occasionally, I’ll dive a little deeper into topics, as needed.

You can follow these tips over at WPF.tips or just keep an eye on the Xaml section of the Dew Drop posts right here. I’ll be sure to link to each tip.

Thanks for following!

Alvin

del.icio.us Tags:

The Dew Review – DevExpress TestCafe

Hot on the heels of my last review for DevExpress Universal 2014.2, I will next dive into my recent experiences with DevExpress TestCafe.

Introduction

TestCafe is the functional web testing tool from DevExpress. While many web test products work in conjunction with browser plugins, TestCafe works without plugins on any browser that supports HTML5. In today’s world, that translates to ‘any modern browser’.

You can also run on the big three operating systems, Windows, Mac OSX and Linux. I have been working with TestCafe on Windows 8.1, Windows 10 and OSX Yosemite. This operating system and browser agnostic ability affords maximum flexibility for web developers to test applications across multiple versions of browsers. All that is required is another physical or virtual machine running the desired browser(s).

That all sounds great, but you probably hear a lot about cross-browser, cross-platform ability these days. Take a minute and think about what that really means for a functional web testing tool. I can open TestCafe from any browser that can access the machine(s) where TestCafe and my application under test have been installed. So, I can pull out my Ultrabook on the road and record new tests over my company’s VPN. I could also break out my iPod Touch, tablet or Windows Phone and execute test fixtures. That is pretty powerful.

Getting Started

TestCafe claims to be “Functional Web Test, Made Easy”. Let’s see how easy it is to get started using the product. Until last month, I have never used TestCafe. In fact, I never used any kind of web test automation product. I’ve been lucky enough to work for organizations that understand the value to QA and hire dedicated testers who find my bugs that get past my own unit testing and manual testing.

So, how does a newbie like me get started?

DevExpress Demo Pages

A great first place to start is by trying TestCafe through the online demo pages. There are three pages available for selection.

  • TestCafe Example Page
  • ASPxGridView Demo Page
  • ASPxFileManager Demo Page

I decide to try the ASPxGridView Demo Page, so I select it from the list and click the ‘Run Demo’ button. Here is what I see initially.

TestCafe 3 - Demo Step 1 

Figure 1 – Run the online demo

I click the Start Demo link and click through some controls on the grid to auto generate a few test steps.

TestCafe 4 - Demo Step 2

Figure 2 – Online demo test steps

TestCafe includes all of my clicks and keystrokes into the test steps (even my screen capture keystrokes in Step 5 J). Now I can add some assertions to my test fixture or playback the steps that have been generated so far.

After completing this quick demo, I feel ready to jump into the Online Tutorial and create some of my own tests on my development machine.

Online Tutorial

From the main page for TestCafe, there’s a big button that will take you to an online tutorial with videos and step-by-step instructions for getting started with your first tests.

TestCafe 01 - The big button

Figure 3 – The big button.

The online tutorial steps you through creating a project, recording tests, running them, and finally running on remote devices. Here’s a look at the complete outline of the tutorial’s steps.

Test Cafe 02 - Tutorial Steps

Figure 4 – Tutorial Steps

Completing the online tutorial will give you an idea of the base functionality and capabilities of TestCafe. Now it’s time to take that knowledge to the next step.

AngularJS PhoneCat Tutorial App

Now that I have taken some time to familiarize myself with TestCafe using web pages created by DevExpress, I want to try creating a few simple test fixtures with a page that uses one of today’s most popular JavaScript frameworks, AngularJS. I have been working mainly in the world of Windows desktop applications and WPF for the last 18 months, so I turn to Bing to find out how to set up and run a simple AngularJS site.

Not surprisingly, Bing takes me to the AngularJS site where I see that there is a Tutorial under the Develop menu. The tutorial walks you through downloading their sample site, setting it up with Node.js and npm, and running it. The sample site is a simple catalog of Android devices with a list view and detail view.

TestCafe 05 - PhoneCat 1

Figure 5 – AnguluarJS tutorial application – list view

TestCafe 06 - PhoneCat 2

Figure 6 – AngularJS tutorial application – detail view

I create a new Test Project and Test Fixture and record a few tests for the List View page.

TestCafe 07 - Phone List Tests

Figure 7 – Phone List tests

Up to this point everything has been produced for us by TestCafe. Let’s take a moment analyze what has been generated for the tests I created for the Phone List Page. Here’s the JavaScript that is presented when clicking the Edit button on one of the tests or on the Test Fixture itself.

"@fixture Phone List Page";
"@page http://localhost:8000/app/index.html";

"@test"["Sort Phone List"] = {
    "1.Click select": function() {
        var actionTarget = function() {
            return $(".ng-pristine.ng-untouched.ng-valid").eq(1);
        };
        act.click(actionTarget);
    },
    '2.Click option "Alphabetical"': function() {
        act.click(":containsExcludeChildren(Alphabetical)");
    },
    "3.Click select": function() {
        act.click(".ng-untouched.ng-valid.ng-dirty.ng-valid-parse");
    },
    '4.Click option "Newest"': function() {
        act.click(":containsExcludeChildren(Newest)");
    }
};

"@test"["Search Dell"] = {
    "1.Click input": function() {
        var actionTarget = function() {
            return $(".ng-pristine.ng-untouched.ng-valid").eq(0);
        };
        act.click(actionTarget);
    },
    "2.Type in input": function() {
        var actionTarget = function() {
            return $(".ng-pristine.ng-untouched.ng-valid").eq(0);
        };
        act.type(actionTarget, "Dell");
    }
};

"@test"["Search Samsung"] = {
    "1.Click input": function() {
        var actionTarget = function() {
            return $(".ng-pristine.ng-untouched.ng-valid").eq(0);
        };
        act.click(actionTarget);
    },
    "2.Type in input": function() {
        var actionTarget = function() {
            return $(".ng-pristine.ng-untouched.ng-valid").eq(0);
        };
        act.type(actionTarget, "Samsung");
    }
};

"@test"["Search Motorola sort by name and assert first device name"] = {
    "1.Type in input": function() {
        var actionTarget = function() {
            return $(".ng-pristine.ng-untouched.ng-valid").eq(0);
        };
        act.type(actionTarget, "Motorola");
    },
    "2.Click select": function() {
        act.click(".ng-pristine.ng-untouched.ng-valid");
    },
    '3.Click option "Alphabetical"': function() {
        act.click(":containsExcludeChildren(Alphabetical)");
    },
    "4.Assert": function() {
        eq($(":containsExcludeChildren(DROID 2 Global by Motorola)").text(), "DROID™ 2 Global by Motorola", "First Moto Device");
    }
};

Notice that the test fixture is designated with @fixture, and each test definition with @test… pretty straightforward. Each test step is a function that returns the UI element to act upon followed by the action to perform on that element. My final test includes an assert as a final step. This checks that the first list element’s text is equal to what is expected after searching for Motorola and sorting the results alphabetically.

I can access the test fixture on my Windows Phone’s browser.

wp_ss_20150307_0002

Figure 8 – TestCafe on Windows Phone 8.1

And I can kick off a test run.

wp_ss_20150307_0001

Figure 9 – Tests executing on Windows Phone 8.1

So, that is a quick look at some of the basics of getting TestCafe up and running. There is a lot you can accomplish without knowing much more than what the Online Tutorial provides. To learn some more advanced features of the test fixtures in TestCafe, refer to the Test Fixture API Reference guide in the online documentation.

Feature Focus – Continuous Integration

I am a big fan of automated builds and continuous integration (CI) in development. I think it is one of the essentials of creating quality software. CI can do things for your team like running unit tests, code analysis and enabling continuous deployments to dev and test environments. Even at a bare minimum, CI can provide immediate feedback if a change one developer makes in a software component breaks something else in the system.

Automating and integrating functional web tests with CI builds is an extremely valuable proposition. It means that the number of regression bugs discovered by QA will be greatly reduced as builds that fail test steps in TestCafe will never be deployed to QA environments.

TestCafe has an entire section of their online documentation dedicated to continuous integration as well as a CI API reference.

At a glance, these are the steps that will need to be completed to set up CI for most types of servers (taken from the online docs).

  • Copy your tests to the server.
  • Setup TestCafe on the server.
  • Write a Node.js application that runs the tests and logs the results.
  • Call the application from your Continuous Integration system.

The sample node.js app in the online docs logs results to the console. Depending on your CI system, you will either use this method and pick up the results through the console output, or you may want to log the output to a file. There is an example of file output on another online documentation example. Here’s a snippet of that example:

var fs = require('fs');
 
testCafe.runTests(runOptions, function () {
      testCafe.on('taskComplete', function (report) {
           log('\n' + new Date().toString() + ':\n' + JSON.stringify(report));
       });
});
 
function log(mssg) {
       fs.appendFile(LOG_FILE_NAME, mssg, function (err) {
             if (err) 
                throw err;
       });
}

My current build server of choice at home and at work is JetBrains TeamCity. I have also used CruiseControl .NET in the past. While the basic steps were a good starting point, I wanted to find more detailed instructions specific to my TeamCity implementation.

Getting Support

I want to find out more information about TestCafe continuous integration with TeamCity. Where do I turn? To me, the logical first step is to try the Support page on the TestCafe site.

I kept it simple and searched for “TeamCity” and got just a single search result. Luckily, this one result was exactly what I needed. There is no direct support between TestCafe and TeamCity, but they can be integrated using the node.js app approach detailed in the online CI documentation. The method for doing this is similar for Jenkins CI servers, and Marion the DevExpress support representative who answered the question provided a link to the Jenkins information.

The TeamCity equivalent of these Jenkins steps include:

· Create a node.js app and place it on the build server. (The app in the Jenkins example will serve.)

· Install node.js on the build server.

· Add a Windows Command Line step to the desired build configuration in TeamCity that executes the node.js app.

· Capture the result.xml as the build report output.

If you want immediate feedback on your continuous builds, you will probably want to limit the number of tests run. I recommend creating either an hourly or daily build that executes all of your unit tests, integration tests and TestCafe functional tests.

Wrapping Up

Automating your functional tests can provide a great deal of value to any product. It takes a huge load off of the QA department once a full test suite has been created for your web applications. The licensing cost of TestCafe makes it something that every development shop should consider, regardless of the company or team size.

Go give it a try. There’s a 30-day free trial plus a 60-day money back guarantee if you’re not satisfied with the product.

 

Happy coding!

 

 

Disclosure of Material Connection: I received one or more of the products or services mentioned above for free in the hope that I would mention it on my blog. Regardless, I only recommend products or services I use personally and believe my readers will enjoy. I am disclosing this in accordance with the Federal Trade Commission’s 16 CFR, Part 255: “Guides Concerning the Use of Endorsements and Testimonials in Advertising.

The Dew Review – Taking a Look at the Latest Release of Aspose.Email

Introduction

I have been spending some time working with the latest Apose.Email for .NET. It has been twelve or thirteen years since I have written any email related code. Back in the early 2000s, I did a little bit of work with Visual Basic and the Outlook Collaboration Data Objects (CDO). We have come a long way since that time. Email in the cloud is now becoming the norm with Gmail and Office 365.

There’s still plenty of need for local and Exchange based email processing in business applications as well. I will get to one possible scenario in my own application below.

Latest Aspose.Email Release

The Aspose.Email library supports just about any email related activity imaginable. Here is just a handful of scenarios developers can code into their own applications with Aspose.Email:

Generate emails and send via SMTP
  • Embed objects in message body – Send emails with embedded images or documents.
  • Attach files – Attach files to an email as a user would from their email client.
  • Mail merge – Powerful support for mail merge and mass emailing.
  • iCalendar support – Read and manipulate calendar events via the iCalendar standard.
  • Receive POP3 mail
    • Work with IMAP mail sources
    • Authentication
    • Work with messages and folders
    • SSL support (for POP3 and SMTP)
    Message Files
    • EML/MSG/MHT formats – All common mail message formats are supported.
    • Work with files or streams – Open messages from disk or network streams.
    • Manipulate PST files – Create, read and manipulate Outlook PST files and their contents.
    MS Exchange Server
    • WebDav and Exchange Web Services support
    • Unified Messaging operations
    • Send emails and Meeting Invites
    Advanced support for recurrence
    • Easily and reliably calculate event recurrence
    • Suppoprt for iCalendar (RFC 2445)

    Detailed developer documentation for all of the Aspose.Email features is available online here.

    So, whether your application needs to work with Exchange, Outlook files, POP3/SMTP, or talk to Gmail via IMAP, Aspose.Email has APIs to help with each situation. There are also sample apps for each feature exposed in Aspose.Email to get developers started off on the right track.

    An IMAP Console Application

    There are dozens of sample applications installed along with Apose.Email. To get started, launch the Aspose Examples Dashboard:

    asposeexamples

    The example apps are grouped by feature set. Developers can view the code in the Sample Browser, launch the solution in C# or VB, or run the example app right from the Browser application.

    I decided to take a closer look at one of the IMAP samples. The one I chose was “Fetch Messages from IMAP Server and Save to Disk”, which does exacly what the name implies. It is a console application that connects to a Gmail account via IMAP, selects the Inbox folder and loops through all of the messages, saving each one to a local folder in the “.eml” format. Here is the complete code implementation for the app after a couple of ReSharper refactorings.

    public static void Main(string[] args)
    {
        // The path to the documents directory.
        string dataDir = Path.GetFullPath("../../../Data/");
        Directory.CreateDirectory(dataDir);
    
        //Create an instance of the ImapClient class
        var client = new ImapClient
                     {
                         Host = "imap.gmail.com",
                         Username = "asposetest123@gmail.com",
                         Password = "F123456f",
                         Port = 993,
                         SecurityMode = ImapSslSecurityMode.Implicit,
                         EnableSsl = true
                     };
    
        try
        {
            client.Connect();
           
            //Log in to the remote server.
            client.Login();
    
            // Select the inbox folder
            client.SelectFolder(ImapFolderInfo.InBox);
    
            // Get the message info collection
            ImapMessageInfoCollection list = client.ListMessages();
    
            // Download each message
            for (int i = 0; i < list.Count; i++)
            {
                //Save the EML file locally
                client.SaveMessage(list[i].UniqueId, dataDir + list[i].UniqueId + ".eml");
            }
    
            //Disconnect to the remote IMAP server
            client.Disconnect();
    
            System.Console.WriteLine("Disconnected from the IMAP server");
        }
        catch (System.Exception ex)
        {
            System.Console.Write(ex.ToString());
        }
    }
    

    It is simple and intuitive to use.

    The PST Archive Utility

    I didn’t have the time to write my own applications that use every aspect of the library, so I decided to take one set of features and focus there. For years, I have been meaning to organize my work-related PST files into yearly archives. In fact, I have one PST that covers nearly seven years of email. It is nearly 6gb in size and contains who knows how many thousands of items.

    I built a small utility that will read a selected PST file, iterate through all of its folders and move all items for the specified year into a new PST with the year prepended to the PST’s file name, mirroring the folder structure of the original PST. I decided to build the utility as a WPF application, but this could function nicely as a command line utility also.

    pst1

    Using the utility is rather straightforward, simply:

    1. Enter the full path to the PST to be read.
    2. Click ‘Open PST’.
    3. The available years will display. Select a year.
    4. Click ‘Process PST’.
    5. When complete, the status message will update to “New PST Created for Year xxxx”.

    The code to display the available years iterates through the folders and items, collecting the unique years into a List<int>. It then sorts them before setting the property in the ViewModel to which the Available Years ListBox is bound.

    /// <summary>
    /// Gets all the years of items in a PST file.
    /// </summary>
    private void GetPstYears()
    {
        if (String.IsNullOrWhiteSpace(PstPath) || !File.Exists(PstPath)) return;
    
        using (PersonalStorage mainPst = PersonalStorage.FromFile(PstPath, true))
        {
            CurrentStatus = "Processing Years...";
    
            List<int> years = GetAvailableYears(mainPst.RootFolder);
            years.Sort();
            Years.Clear();
    
            foreach (int year in years)
            {
                Years.Add(year);
            }
    
            CurrentStatus = "PST Ready";
        }
    }
    
    /// <summary>
    /// Gets the available years in a specified Outlook folder.
    /// </summary>
    /// <param name="folder">The folder.</param>
    /// <returns>A list of years.</returns>
    private List<int> GetAvailableYears(FolderInfo folder)
    {
        var years = new List<int>();
    
        foreach (MapiMessage message in
                    folder.EnumerateMapiMessages().Where(message => !years.Contains(message.DeliveryTime.Year)))
        {
            years.Add(message.DeliveryTime.Year);
        }
    
        foreach (int subYear in from folderInfo in folder.EnumerateFolders()
            where folderInfo.HasSubFolders
            select GetAvailableYears(folderInfo)
            into subYears
            from subYear in subYears
            where !years.Contains(subYear)
            select subYear)
        {
            years.Add(subYear);
        }
    
        return years;
    }
    

    Similarly, the code to move the messages for the selected year to the new PST, iterates the folder structure to find any matching items.

    /// <summary>
    /// Process a PST by moving items from a selected year to a new PST
    /// while creating the same folder structure.
    /// </summary>
    private void ProcessPst()
    {
        if (SelectedYearIndex < 0 || String.IsNullOrWhiteSpace(PstPath) || !File.Exists(PstPath)) return;
    
        int year = Years[SelectedYearIndex];
    
        using (PersonalStorage mainPst = PersonalStorage.FromFile(PstPath, true))
        {
            string newFileName = PstPath.Insert(PstPath.LastIndexOf("\\", StringComparison.Ordinal) + 1, year.ToString(CultureInfo.InvariantCulture));
    
            using (PersonalStorage pstWithYear = PersonalStorage.Create(newFileName, FileFormatVersion.Unicode))
            {
                ProcessSubfolders(mainPst.RootFolder, pstWithYear.RootFolder, year);
            }
        }
    
        CurrentStatus = String.Format("New PST Created for Year {0}", Years[SelectedYearIndex]);
    }
    
    /// <summary>
    /// Processes the subfolders of a provided PST folder and adds items
    /// from the specified year to the new folder provided.
    /// </summary>
    /// <param name="folder">The source folder.</param>
    /// <param name="newParentFolder">The new folder.</param>
    /// <param name="year">The year of items to move.</param>
    private void ProcessSubfolders(FolderInfo folder, FolderInfo newParentFolder, int year)
    {
        foreach (FolderInfo folderInfo in folder.EnumerateFolders())
        {
            FolderInfo newFolder = newParentFolder.GetSubFolder(folderInfo.DisplayName) ??
                                   newParentFolder.AddSubFolder(folderInfo.DisplayName);
    
            if (folderInfo.HasSubFolders)
            {
                ProcessSubfolders(folderInfo, newFolder, year);
            }
    
            newFolder.AddMessages(folderInfo.EnumerateMapiMessages().Where(m => m.DeliveryTime.Year == year));
    
            if (newFolder.ContentCount == 0 && !newFolder.HasSubFolders && newFolder.DisplayName != "Deleted Items")
                newParentFolder.DeleteChildItem(newFolder.EntryId);
        }
    }
    

    You can see that all of the PST manipulation is very intuitive. Everything I needed to know, I was able to quickly learn from the documentation and the sample applications. It feels as if the classes are a part of the .NET Framework. It is a very well written API.

    You can download the complete source code for the project here.

    Summary

    If you are working on any projects involving email processing or access, Aspose.Email can definitely simplify the code required to get the job done. I will definitely keep these libraries in mind for future projects and you should too.

    Happy coding!

    del.icio.us Tags: ,,

     

    Disclosure of Material Connection: I received one or more of the products or services mentioned above for free in the hope that I would mention it on my blog. Regardless, I only recommend products or services I use personally and believe my readers will enjoy. I am disclosing this in accordance with the Federal Trade Commission’s 16 CFR, Part 255: “Guides Concerning the Use of Endorsements and Testimonials in Advertising.”