Category Archives: Development

Unit Testing KnockoutJS and Web API

After a couple of years of looking on from the side-lines at the advancements being made world of web development, I decided recently it was time to dive in head-first and bring my web knowledge up to speed. A lot of my initial work with C# and .NET was with ASP.NET WebForms, but in the past few years the majority of my work has been either mobile, desktop, or server-based.

So, for the past couple of months I’ve been investigating a variety of topics from top-to-bottom, including HTML5 and CSS3, Bootstrap, ASP.NET MVC, Entity Framework (including Repository, Unit of Work, and Service patterns), Dependency Injection, JavaScript (including the Module and Revealing Module patterns) and jQuery, KnockoutJS, and finally QUnit.

One topic I thought I’d blog about is how to unit test client-side JavaScript code, specifically ViewModels used by KnockoutJS that communicate with a Web API endpoint. To help illustrate these techniques I’ve created an ultra-simple ASP.NET MVC application that uses both Web API and KnockoutJS. Shockingly, it’s a to-do application. You can check out the source code for the application here. You can also download a snapshot of the project, before adding unit testing, here.

Here is the main view for the MVC application:

<input data-bind="value: addingItemText, valueUpdate: 'afterkeydown'" type="text" />
<button data-bind="enable: canAddItem, click: addNewItem">Add</button>

<ol data-bind="foreach: items">
    <li>
        <strong data-bind="visible: Completed">Completed </strong>
        <span data-bind="text: Text"></span>
        <button data-bind="click: $root.deleteSelectedItem">Delete</button>
        <button data-bind="click: $root.completeSelectedItem, visible: !Completed()">Complete</button>
        <button data-bind="click: $root.undoSelectedItem, visible: Completed()">Undo</button>
    </li>
</ol>

@section scripts {
    @Scripts.Render("~/bundles/app")
}

I define a text input and button for adding a new to-do item. The Add button should only be enabled when there is text entered. Following that there is a list of to-do items. If an item has been completed it shows appropriate text in bold. There’s a button to delete an item. Finally, if the item is uncompleted there is a button to complete it, and if the item is completed there is a button to undo it.

Here is a look at the ViewModel:

$((function (ns, webApiClient) {
    "use strict";

    ns.todoViewModel = (function () {

        //utilities
        function cloneJSModel(sourceModel, destinationModel) {
            destinationModel.Id(sourceModel.Id)
                .Text(sourceModel.Text)
                .Completed(sourceModel.Completed);
        }

        function cloneKOModel(sourceModel, destinationModel) {
            var jsModel = ko.toJS(sourceModel);
            cloneJSModel(jsModel, destinationModel);
        }

        //UI binding
        var items = ko.observableArray();
        
        //web api calls
        function populate() {

            webApiClient.ajaxGet("TodoItem", "", function (json) {
                items.removeAll();

                $.each(json, function (index, value) { //ignore jslint
                    var item = new ns.todoItemModel();
                    cloneJSModel(value, item);
                    items.push(item);
                });
            });
        }

        function addItem(todoItem) {

            webApiClient.ajaxPost("TodoItem", ko.toJS(todoItem), function (result) {
                var newItem = new ns.todoItemModel();
                cloneJSModel(result, newItem);
                items.push(newItem);
            });
        }

        function deleteItem(id) {

            webApiClient.ajaxDelete("TodoItem", id, function (result) {
                items.remove(function (item) {
                    return item.Id() === result.Id;
                });
            });
        }

        function updateItem(todoItem) {

            webApiClient.ajaxPut("TodoItem", todoItem.Id(), ko.toJS(todoItem), function () {
                var existingItem = ko.utils.arrayFirst(items(), function (item) {
                    return item.Id() === todoItem.Id();
                });
                cloneKOModel(todoItem, existingItem);
            });
        }

        //UI actions
        var addingItemText = ko.observable('');

        var canAddItem = ko.computed(function () {
            return addingItemText() !== "";
        });

        var addNewItem = function () {
            var newItem = new ns.todoItemModel();
            newItem.Text(addingItemText());
            addItem(newItem);
            addingItemText("");
        };

        var deleteSelectedItem = function () {
            deleteItem(this.Id());
        };

        var completeSelectedItem = function () {
            this.Completed(true);
            updateItem(this);
        };

        var undoSelectedItem = function () {
            this.Completed(false);
            updateItem(this);
        };

        //return a new object with the above items
        //bound as defaults for its properties
        return {
            items: items,
            populate: populate,
            addingItemText: addingItemText,
            canAddItem: canAddItem,
            addNewItem: addNewItem,
            deleteSelectedItem: deleteSelectedItem,
            completeSelectedItem: completeSelectedItem,
            undoSelectedItem: undoSelectedItem
        };

    }());

    ns.todoViewModel.populate();

    ko.applyBindings(ns.todoViewModel);

    //pass in namespace prefix (from namespace.js)
}(todo, todo.webApiClient)));

Again this is all pretty standard. It follows the Revealing Module pattern for the ViewModel. One thing to note is that a webApiClient is passed in and used for the AJAX calls. John Papa shows something very similar in his Pluralsight training courses. This nicely abstracts out the Web API specifics plus, as you’ll see, it makes it easier to unit tests our ViewModel.

Here’s the Web API client source:

(function (ns) {
    "use strict";

    ns.webApiClient = (function () {

        var ajaxGet = function (method, input, callback, query) {

            var url = "/api/" + method;
            if (query) {
                url = url + "?" + query;
            }

            $.ajax({
                url: url,
                type: "GET",
                data: input,

                success: function (result) {
                    callback(result);
                }
            });
        };

        var ajaxPost = function (method, input, callback) {

            $.ajax({
                url: "/api/" + method + "/",
                type: "POST",
                data: input,

                success: function (result) {
                    callback(result);
                }
            });
        };

        var ajaxPut = function (method, id, input, callback) {

            $.ajax({
                url: "/api/" + method + "/" + id,
                type: "PUT",
                data: input,

                success: function (result) {
                    callback(result);
                }
            });
        };

        var ajaxDelete = function (method, id, callback) {

            $.ajax({
                url: "/api/" + method + "/" + id,
                type: "DELETE",

                success: function (result) {
                    callback(result);
                }
            });
        };

        return {
            ajaxGet: ajaxGet,
            ajaxPut: ajaxPut,
            ajaxPost: ajaxPost,
            ajaxDelete: ajaxDelete
        };
    }());

    //pass in namespace prefix (from namespace.js)
}(todo));

As you can see we’re simply wrapping access to the jQuery ajax function and calling our callback function. Again you can download the source code above or from Bitbucket and run the app to try all this out. It all works as expected: you can add, delete, complete, and undo items.

In this example I’ll be using QUnit to unit test the JavaScript. JavaScript unit tests, unlike standard unit tests, generally exist in the same project as your site. You’ll see that sites like KnockoutJS and Sugarjs have a webpages where you can run their tests. The tests need access to your JavaScript source files, and there is no easy way to make these available to other projects like you can .NET assemblies.

So we’ll start by creating a new Tests folder in the project.

Add New Folder

Inside that folder create both a test.html and a tests.js file. The next step is to put the QUnit specific markup in the tests.html file:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset="utf-8">
        <!-- QUnit stylesheet from the jQuery CDN -->
        <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.11.0.css">
    </head>
    <body>
        <!-- elements that QUnit will inject test results into -->
        <div id="qunit"></div>
        <div id="qunit-fixture"></div>

        <!-- required JS libraries, such as jQuery and KnockoutJS -->
        <script src="/Scripts/jquery-1.9.1.js"></script>
        <script src="/Scripts/knockout-2.2.1.debug.js"></script>

        <!-- QUnit itself from the jQuery CDN -->
        <script src="http://code.jquery.com/qunit/qunit-1.11.0.js"></script>

        <!-- include tests themselves -->
        <script src="tests.js"></script>
    </body>
</html>

Note that I’ve also included references to the jQuery and Knockout js files. Next, add the following code to the tests.js file in order to ensure things are working:

test("hello test", function () {
    ok(1 == "1", "Passed!");
});

Now, if you right-click on the tests.html file and click View in Browser, you should see passing results for the single test.

Hello Test Run

Now lets implement actual unit tests for our ViewModel. One of the cardinal rules of unit tests is that they should not interact with the “outside world”, specifically things like databases, file systems, and web services. Tests that do this, even using a unit testing framework, are known as integration tests. So, one thing we need resolve is how to test our ViewModel without making the Web API calls.

We’ll do this by creating a stub for our Web API client object. With typical C# applications you can do stubbing, mocking, and faking in a variety of ways. I personally like using FakeItEasy. However, the dynamic nature of JavaScript means that, at least for something simple like stubbing our Web API client, there’s really no additional framework needed.

Let’s start the stubbing by creating a new JavaScript file called webapiclient.stub.js in the Tests folder. Now add the following code to define the stub:

(function (ns) {
    //better exceptions, less tomfoolery allowed
    "use strict";

    ns.webApiClient = (function () {

        var testResult = [];

        var ajaxGet = function (method, input, callback, query) { //ignore jslint
            callback(this.testResult);
        };

        var ajaxPost = function (method, input, callback) { //ignore jslint
            callback(this.testResult);
        };

        var ajaxPut = function (method, id, input, callback) { //ignore jslint
            callback(this.testResult);
        };

        var ajaxDelete = function (method, id, callback) { //ignore jslint
            callback(this.testResult);
        };

        //return a new object with the above items
        //bound as defaults for its properties
        return {
            ajaxGet: ajaxGet,
            ajaxPut: ajaxPut,
            ajaxPost: ajaxPost,
            ajaxDelete: ajaxDelete,
            testResult: testResult
        };
    }());

    //pass in namespace prefix (from namespace.js)
}(todo));

This code is pretty straight forward. It uses the same signatures as the real Web API client object. However, instead of making real AJAX calls, it calls the callback immediately, passing back the value stored in testResult.

One important thing to note is that the function calls to the callbacks pass this.testResult rather than just testResult. This is because the return block is returning a new object with the specified properties and default values for those properties. Those properties are not getters or setters for the privately scoped variables above, although it may look that way if you are used to OOP languages like C# or Delphi.

Next lets look at how to make use of this stub to write tests against the ViewModel. We’ll add the following script references to the tests.html file:

<!-- include the application's namespace.js -->
<script src="/Scripts/app/namespace.js"></script>

<!-- include our sub Web API client -->
<script src="webapiclient.stub.js"></script>

<!-- include the items to test -->
<script src="/Scripts/app/model.js"></script>
<script src="/Scripts/app/viewmodel.js"></script>

The first reference is to our Web API client stub and the second two are to our application’s Model and ViewModel respectively. Now lets add our first real test to the tests.js file:

module("todo.viewmodel.populate");

test("todo.viewmodel.populate (0 length)", function () {
    "use strict";

    //arrange
    todo.webApiClient.testResult = [];

    //act
    todo.todoViewModel.populate();

    //assert
    equal(todo.todoViewModel.items().length, 0, "Passed!");
});

The first line defines a module. This is not required at all and is merely a way to group tests visually into sections when results are displayed. Next we setup the todo.webApiClient (our stub) to return an empty array for any calls to it. Then, we call the populate() function on our ViewModel. Finally, we assert that the items() in our ViewModel is zero-length. You can save the tests.js and tests.html file and refresh your browser to view the results:

First Real Test Run

Here are some more example tests for the to-do ViewModel:


test("todo.viewmodel.populate (1 length)", function () {
    "use strict";

    //arrange
    todo.webApiClient.testResult = [
        {
            Id: 1,
            Text: "To-do",
            Completed: false
        }
    ];

    //act
    todo.todoViewModel.populate();

    //assert
    equal(todo.todoViewModel.items().length, 1, "Passed!");
});

module("todo.viewmodel.canAddNewItem");

test("todo.viewmodel.canAddNewItem (without text)", function () {
    "use strict";

    //arrange

    //act
    todo.todoViewModel.addingItemText('');

    //assert
    equal(todo.todoViewModel.canAddItem(), false, "Passed!");
});

test("todo.viewmodel.canAddNewItem (with text)", function () {
    "use strict";

    //arrange

    //act
    todo.todoViewModel.addingItemText('To-do');

    //assert
    equal(todo.todoViewModel.canAddItem(), true, "Passed!");
});

module("todo.viewmodel.addNewItem");

test("todo.viewmodel.addNewItem", function () {
    "use strict";

    //arrange
    todo.webApiClient.testResult = [];
    todo.todoViewModel.populate();

    var expectedItem = {
        Id: 1,
        Text: "To-do",
        Completed: false
    }

    todo.webApiClient.testResult = expectedItem;

    //act
    todo.todoViewModel.addingItemText(expectedItem.Text);
    todo.todoViewModel.addNewItem();

    //assert
    var firstItem = todo.todoViewModel.items()[0];
    equal(firstItem.Id(), expectedItem.Id, "Passed!");
});

Along with results for the full suite of tests:

All Tests Run

Hopefully this proves helpful to other developers taking a look at writing more client-side JavaScript and looking for ways to test it. Look forward to more posts in the coming months on other topics and techniques I’ve discovered relating to these evolving web technologies.

Advertisements

Sharing a Keyboard and Mouse with Windows RT

Anyone who uses computers to create knows that one of the best ways to boost productivity is to add additional screens to your setup. This can be done in several ways. The most direct way is to buy and connect a new display to your system. In addition, applications such as Synergy and Mouse without Borders let you control additional computing devices, such as laptops, using your desktop computer’s mouse and keyboard.

For instance, my current development system uses a primary display and an left-hand display. In addition, I use my laptop as a “third display” with things like Twitter, Skype, and IRC running on it.

Multimonitor

By running Synergy on my desktop computer and my laptop I am abile to freely move my mouse between all three screens, and use my desktop mouse and keyboard with my laptop. Things like copy-and-paste work well too.

One of the cooler ports to come out of the Windows RT jailbreak scene (obviously aside from Quake 2) is the ARM port of Synergy, which you can download here. Setup is simple:

  1. Jailbreak your Windows RT device using this tool
  2. Download the ARM port of Synergy here
  3. Extract the archive and run Launcher.exe
  4. Enter the IP address of your desktop computer (where Synergy should be running in server-mode)
  5. Click Start

Synergy Client Settings

Note that you’ll need to download and run Synergy on your desktop computer as well, and configure Synergy accordingly. If you are using OS X on the desktop, SynergyKM is a friendly Synergy distribution that installs into System Preferences.

SynergyKM

This is great way to use a Windows RT device as an additional small-form display. It’s also very helpful when developing Windows Store apps or WinForms apps for Windows RT. Once you start remote debugging your Windows Store or WinForms app on your Windows RT device, you can simply move your mouse from your development system to your Windows RT device to debug, easily moving the mouse from the app on Windows RT to the IDE for debugging on your desktop.

Visualizing Code Coverage with Xcode

After having a particularly good experience exploring and implementing unit tests in Visual Studio 2012 using the code coverage analysis found in the Premium SKU I decided it was time (past time!) to get a similar handle on testing my iOS applications.

Now I love Xcode but the tooling is, in my opinion, not as mature as Visual Studio in many respects: debugging experience, testing experience, analysis, and integration for 3rd-parties to name a few.

When it comes to unit testing and code coverage with Xcode, the story isn’t nearly as nice as that found in Visual Studio. For instance, your only option for running unit tests is to run all test cases at once. There is no test runner or dedicated UI and there is no way to run individual tests or easily toggle tests (other than individually toggling the membership of .m files in the test target). To put it simply, it’s a far cry from Visual Studio’s Test Explorer, much less something like CodeRush’s Test Runner. See the update at the bottom of the post for clarification on this.

When it comes to visualizing code coverage with Xcode – specifically iOS apps – there are several steps to take and a 3rd-party application is required.

The first step is to configure your iOS app target for code coverage. Select your app target in Xcode (not the unit test target) and, under Build Settings, enable the following settings for the Debug configuration:

  1. Generate Test Coverage Files
  2. Generate Profiling Code
  3. Instrument Program Flow

One Setting

Two Settings

Now, as long as your project is setup with a unit test target, you can click Product and then Test to run your unit tests.

This is where things start to get not-as-pretty-as-Visual-Studio. What this did was generate a bunch of files in a deeply nested location on the file system, namely:

~/Library/Developer/Xcode/DerivedData/[Project Name]-[Random Characters]/Build/Intermediates/[Target Name].build/[Configuration Name]-iphonesimulator/[Target Name].build/Objects-normal/i386

Yes. Really. For example, this is the path for a sample I created:

~/Library/Developer/Xcode/DerivedData/CodeCoverageSample-gtzzspgbzxcdhifewyfazdxkkwum/Build/Intermediates/CodeCoverageSample.build/Test-iphonesimulator/CodeCoverageSample.build/Objects-normal/i386

If you check the contents of this folder after taking the above steps, you should now see a collection of DIA, GCDA, GCNO, and related files.

The next step is to find a friendly way to view these results. There is nothing for this built into Xcode or provided by Apple. This is where a nice open source application called CoverStory comes in handy. This application loads the files generated by Xcode and displays an output similar to other code coverage tools.

You can download CoverStory here. After running the app, click the File menu, then Open, and navigate to the same ridiculous path mentioned previously (press ⌘⇧. to show hidden files & folders in Finder). This should display the breakdown of code coverage for your application.

CoverStory

Selecting .m files will show the individual lines of code covered by the unit tests.

Finally, depending on the linker flags your project uses there may be additional steps required to generate GCDA files with iOS 6. I’ve covered steps for working around that issue in a separate blog post here.

My next step will be to look into GHUnit, an alternative to the unit testing bundled into the Xcode IDE (OCUnit). While not integrated into Xcode, GHUnit does offer several improvements such as the ability to run individual tests or failed tests.

UPDATE: Martin Pilkington was kind enough to call to my attention that there is another way to enable and disable tests and test cases using Xcode schemes. Click on Product, then Scheme, then Edit Scheme (or press ⌘<). On the left of the scheme sheet select the Test action. On the right, in the list under Tests, you can expand the individual test targets, test cases, and tests, and enable and disable them via the checkbox in the last column.

Scheme Test Action Tests

You can also multi-select multiple tests and test cases and right-click to disable or enable a group.

Disable Tests

Thanks Martin! And while this is absolutely better than toggling the target membership of the .m test case files, I’d still like the ability to easily run a single test or test case. Right now, to target coverage of a single test or test case, I have to disable hundreds (on a larger project) of tests, then work on my one test, only to re-enable hundreds of tests afterwards.

What I’d personally like is the sort of simple features found in other unit test runners: easily select and run a single test, a single test case, failing tests, or all tests (without having to manually and individually select which tests to run).

UPDATE: Xcode 5 has since been released by Apple and now includes a full-blown Test Navigator for running unit tests.

Fix for Missing Code Coverage Files with Xcode

One of the things I’ve been working with lately is code coverage analysis using Xcode and CoverStory.

CoverStory Demo

While the experience is not as polished and complete as Visual Studio 2012, I’ve gotten most things working well enough.

One of the bigger hurdles I ran into was missing code coverage files when using iOS 6 to run unit tests. If I used iOS 5 or 5.1, the code coverage files were generated just fine. However, if I used iOS 6.0 or 6.1 the GCDA files specifically were not created.

I moved past this for a while but, as my code coverage increased, it became more important for me to test the iOS 6 specific code paths. I tried the Apple developer forums but had no luck, so I continued digging myself.

The first thing I noticed was that a brand new project created with Xcode did create GCDA files for iOS 6 and 6.1. This was promising as it meant that the problem was somewhere in my project.

After spending some time comparing project.pbxproj files (thanks to Marc Hoffman for pointing me in that direction), I was finally able to narrow the problem down to its cause. If you have an old enough iOS project, there’s a decent chance you’ll have one of the following flags specified in your Build Settings for Other Linker Flags:

  • -weak_library /usr/lib/libSystem.B.dylib
  • -weak-lSystem

Bad Setting

Among other things these flags allow apps that use features such as blocks and backgrounding to build for iOS 3. As it turns out, this setting also causes GCDA files not to be created when generating code coverage under iOS 6.

There are two options here:

  1. If the flag is not needed anymore you can remove it
  2. If the flag is necessary, you can create a new configuration for unit testing (this is also useful for setting up preprocessor macros)

In Xcode, select your project and select the Info tab. Click the plus button under Configurations and create a new configuration based on Debug.

Add Config
Dupe Debug

Name the new configuration Test.

Name Test

On the Build Settings tab, locate Other Linker Flags. Expand the setting so that each configuration is shown underneath it. For the Test configuration, remove the offending flag.

Clear Flag for Test

Now that the Test configuration is setup not to use the offending flag, you need to tell Xcode to use this configuration when testing. You can do this using schemes. Click on Product, then Scheme, then Edit Scheme (or alternatively press ⌘<). On the left of the scheme sheet select the Test action.

Scheme Test Action

On the Info tab, change Build Configuration from Debug to Test.

Select Test Config

With these settings in place the proper code coverage files should now be generated when running unit tests with iOS 6. Note that the path to the code coverage files will change, as the path is based on the configuration name which is now Test.

CoverStory

This took a while to figure out and hopefully Apple will fix the issue (I’ve submitted a bug report). In the meantime hopefully this post helps others who run into this issue!

Hip Shot Version 1.5 Available

Version 1.5 of Hip Shot is now available for download on the Apple App Store. This update to Hip Shot introduces the following improvements:

  • Added the ability to disable individual positions within each location
  • Added support for displaying information in Spanish and German

The #1 request from users since the release of Hip Shot has been the ability to disable individual positions in the application, for example subregions of the left thigh:
Disable Position

With this update it is now possible to customize these individual disabled positions in the Settings tab.

This update also brings localized translations for both Spanish and German. Now, if you have your language in iOS set to either of these languages the text in Hip Shot will be shown in your native language.

If you are able to help translate the phrases in Hip Shot, visit hipshotapp.com and click the link at the bottom of the page.

Remote Debugging WinForms Desktop Apps on Windows RT

Once you have your Windows RT device jailbroken and running your WinForms applications it would be very handy to be able to remotely debug those apps running on Windows RT, similar to what can be done when developing Windows Store apps.

The first step is to share the project folder. Right-click the project in the Solution Explorer in Visual Studio and click “Open Folder in File Explorer”. Then use the context menu in Explorer to share the folder with a common Windows user. The basic sharing wizard worked fine in my testing.

Sharing Wizard

Next we need to setup debugging for the project. Unlike a Windows Store application the remote debugger options aren’t available from the Start debugging button in VS2012. Each project type in Visual Studio seems to surface its remote debugging options in a slightly different way.

Right-click the project in Solution Explorer and click Properties. On the Debug tab, select “Start external program” and enter the path to your exe using the UNC (network) path. Then, check “Use remote machine” and enter either the computer name or IP of your Windows RT device.

Debug Settings

Switch to the Windows RT device and start the Remote Debugger from the Start screen. If you do not have the Remote Debugger for Windows RT installed you can download the Remote Tools for Visual Studio 2012 on your Windows RT device here, under Additional software. Make sure you select the ARM download.

With the Remote Debugger running on your Windows RT device, you should now be able to click Play in VS2012. The application will launch on your Windows RT device, and you can set breakpoints and debug as you normally would.

Windows RT Debugging

Throw in the Remote Desktop app in Windows RT along with the split screen feature and you’ll be debugging from your couch in no time!

RDP Debugging

Creating and Running WinForms Desktop Apps on Windows RT

I’ve been following the developments on the home-brew front for Windows RT for the past few months. As you’ve probably heard, a “jailbreak” for Windows RT was documented several days ago. This “jailbreak” allows the user to run desktop applications that are not signed by Microsoft. You can ready more about how the exploit works here.

The original exploit was pretty difficult to do and involved using a combination of a Windows Store app to open a command prompt that is then able to run some unsigned applications, and then using that command prompt to exploit a bug in Windows RT that stores a new value in memory used when checking if an app is signed. The actual exploit also required the user to use remote debugging from VS2012 in order to get the exploit payload into memory on the Windows RT device and then setting several breakpoints, redirecting the RT device to that payload in memory. Yikes. This mostly just led me to BSOD my device repeatedly.

However, a couple of days ago a much simpler jailbreak tool was released that just involves a double-click and following a few prompts. You can read about the tool and download it here.

Once you have Windows RT able to run unsigned desktop apps you can download a whole set of FOSS apps that have been recompiled for ARM such as Notepad++, PuTTY, bochs, and 7-Zip. The list is growing daily, and there’s even an app you can install on Windows RT that makes for a non-official “RT Desktop Store”.

RT Desktop Store

One of the very cool and often overlooked aspect of all this, though, is that any pure .NET 4.5 application will also run on Windows RT once jailbroken. I tested this myself and it worked without any extra steps. I created a new C# .NET 4.5 application and was able to run it, unmodified, on my Surface RT.

WinForms on Windows RT

Microsoft has stated that they do not see the current jailbreak as a security vulnerability, though they could also (obviously) not promise that the exploit will remain unpatched. If you want to ensure your device remains “vulnerable” there are steps to disable automatic updated for Windows RT here.