Monday, April 29, 2013

Custom jQuery Validators for .NET FileUpload control

The jQuery Validation plugin provides nice functionality for client-side validation and is easy to customize.  Here are some custom validators I wrote for the .NET FileUpload control (Framework 4.5).  These functions validate that a file has been selected and has the desired extension and size.

  1. (function (UTIL, $) {
  2.     "use strict";
  3.  
  4.     UTIL.image_file_extensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp'];
  5.  
  6.     UTIL.getFileExtension = function (filename) {
  7.         var idx = filename.lastIndexOf('.');
  8.         return filename.substring(idx, filename.length);
  9.     };
  10.  
  11.     UTIL.validateFileExtensions = function (element, valid_extensions) {
  12.         var files = element.files,
  13.             i = 0,
  14.             max = files.length,
  15.             ext = '',
  16.             is_valid = true;
  17.  
  18.         while (i < max && is_valid === true) {
  19.             ext = UTIL.getFileExtension(files[i].name);
  20.             is_valid = ($.inArray(ext, valid_extensions) !== -1);
  21.             i += 1;
  22.         }
  23.  
  24.         return is_valid;
  25.     };
  26.  
  27.     if ($.validator) {
  28.  
  29.         $.validator.addMethod("fileupload_required", function (value, element) {
  30.             var is_valid = element.files.length > 0;
  31.             return is_valid;
  32.         }, "You must select a file for upload");
  33.  
  34.         $.validator.addMethod("fileupload_image_extensions", function (value, element) {
  35.             var is_valid = UTIL.validateFileExtensions(element, UTIL.image_file_extensions);
  36.             return is_valid;
  37.         }, "You must select a valid image (.png, .jpg, .gif, .bmp) file for upload");
  38.  
  39.         $.validator.addMethod("fileupload_size", function (value, element) {
  40.             var files = element.files,
  41.                 i = 0,
  42.                 max = files.length,
  43.                 ext = '',
  44.                 is_valid = true;
  45.  
  46.             while (i < max && is_valid === true) {
  47.                 is_valid = (files[i].size <= 4194304);
  48.                 i += 1;
  49.             }
  50.  
  51.             return is_valid;
  52.         }, "You must select a file that is smaller than 4 MB");
  53.     }
  54.  
  55. }(Utilities, jQuery));

Once the custom validators are defined, they can be applied to any FileUpload control by defining the rules for that control:

  1. $('Form').validate({
  2.     rules.fileUploadControlUniqueId = {
  3.         fileupload_required: true,
  4.         fileupload_image_extensions: true,
  5.         fileupload_size: true
  6.     }
  7. });

Note that the validations are applied in the order that they are added to the rules object.  In the example above, the FileUpload control would be validated first for a required file, then for the file extension, then for file size.

Sunday, April 21, 2013

Preparation for Android Testing Presentation

I will be giving a presentation tomorrow on Android Testing strategies for Mobile Monday (MoMo) Lansing.  The target audience is a blend of developers, managers, and anyone else interested in mobile.  My presentation is publicly available on Google Drive as is my demo source code hosted on bitbucket.

In preparation I had to come up to speed on the basics of the Google Android Testing Framework and the Robolectric Testing Framework.  I first learned about Robolectric over lunch at CodeMash 2013 and then got a great demonstration and some setup help from Nathan Dotz at Detroit DevFest 2013.

I am still pretty new to Android and am most comfortable working in the Eclipse IDE so that is how I configured my demo application and tests for my talk.  Here are a few notes of my experiences trying to get everything configured properly:

Android Testing Framework

  • I purchased a copy of the Android Application Testing Guide by Diego Torres Milano
  • I had no trouble setting up an Android Test Project following his instructions, except I had to manually add a Library reference to JUnit 3 which I didn't see mentioned
  • I shamelessly borrowed his concept of a TemperatureConverter app to use as my demo

Robolectric

  • This was definitely harder to configure, I found the configuration instructions at the Robolectric site to be somewhat confusing
  • One thing that threw me off was Maven.  Turns out you don't need Maven to use Robolectric, but I spent several hours trying to get is configured on my own following the Maven quick-start instructions and then with help from Nathan Dotz.
  • Generally speaking I found the Eclipse quick start instructions to be clear with these notes:
    • I could not get the M2Eclipse plugin installed and didn't have time to figure out the problem
    • I created my lib folder within Eclipse, not at the command line
    • I used robolectric-1.3-20130226.015747-4-jar-with-dependencies.jar
    • Will be demonstrating EasyMock as well so also copied in easymock-3.1.jar
    • When I tried to run my first Robolectric test (as shown in their documentation), I encountered this error:  "WARNING: Unable to find path to Android SDK"
    • I resolved this problem from the command line:
      $ CD to the folder containing my project root
      $ android update project -p {my project name}

Robolectric provides a sample app with tests covering some of the basics. I couldn't open the sample in Eclipse because of my M2Eclipse plugin woes so instead I opened it into IntelliJ IDEA which was a painless process.

Thursday, April 11, 2013

Improved DotNetNuke Role Checking

I try to avoid magic strings at all costs and I try to always write unit-testable code.  The DNN 7 framework UserInfo class relies on magic strings (role names) and is not easily unit tested because its methods are not virtual nor does UserInfo implement an interface.  I developed a small reusable framework to overcome both of these DNN UserInfo shortcomings.

The UserInfoAdapter class adapts UserInfo so that unit testing is simplified for methods relying on a UserInfo instance.

  1. /// <summary>
  2. /// Adapts the DNN UserInfo class so that methods relying on UserInfo
  3. /// can be unit-tested.
  4. /// </summary>
  5. public class UserInfoAdapter
  6. {
  7.     private readonly UserInfo _userInfo;
  8.  
  9.     /// <summary>
  10.     /// Construct a new UserInfoAdapter and provide the UserInfo
  11.     /// instance to be adapted.
  12.     /// </summary>
  13.     /// <param name="userInfo">UserInfo instance.</param>
  14.     public UserInfoAdapter(UserInfo userInfo)
  15.     {
  16.         _userInfo = userInfo;
  17.     }
  18.  
  19.     internal UserInfoAdapter()
  20.         : this(null)
  21.     {
  22.     }
  23.  
  24.     /// <summary>
  25.     /// Gets a boolean indicating if the adapted UserInfo instance is null.
  26.     /// </summary>
  27.     public virtual bool IsUserInfoNull { get { return _userInfo == null; } }
  28.  
  29.     /// <summary>
  30.     /// Gets the Roles string array from the adapted UserInfo instance.
  31.     /// </summary>
  32.     public virtual string[] Roles { get { return _userInfo.Roles; } }
  33.  
  34.     /// <summary>
  35.     /// Gets the IsSuperUser boolean value from the adapted UserInfo instance.
  36.     /// </summary>
  37.     public virtual bool IsSuperUser { get { return _userInfo.IsSuperUser; } }
  38. }

The RoleName base class provides a mechanism for overcoming role name magic strings by encapsulating each DNN role name in one place.  Each implementation of RoleName provides a Role value that is identical to a DNN Roles table RoleName value.

  1. /// <summary>
  2. /// Provides testable mechanism for retrieving user role information
  3. /// along with tight database role integration.
  4. /// </summary>
  5. public abstract class RoleName
  6. {
  7.     /// <summary>
  8.     /// Gets the role associated with the RoleName instance.
  9.     /// Returned value should match exactly to a DNN database Roles.RoleName value.
  10.     /// </summary>
  11.     public abstract string Role { get; }
  12.  
  13.     /// <summary>
  14.     /// Gets false to indicate that this is not a SuperUser role.
  15.     /// </summary>
  16.     public virtual bool IsSuperUserRole { get { return false; } }
  17. }

Here are sample RoleName implementations for the Admin role and SuperUser status.

  1. /// <summary>
  2. /// SuperUser role.
  3. /// </summary>
  4. public class SuperUserRole : RoleName
  5. {
  6.     /// <summary>
  7.     /// Gets a value indicating a SuperUser role.  This is not a
  8.     /// DNN Roles.RoleName value.
  9.     /// </summary>
  10.     public override string Role { get { return "SuperUser"; } }
  11.  
  12.     /// <summary>
  13.     /// Gets true to indicate that this is a SuperUser role.
  14.     /// </summary>
  15.     public override bool IsSuperUserRole { get { return true; } }
  16. }
  17.  
  18. /// <summary>
  19. /// Administrators role.
  20. /// </summary>
  21. public class AdminRole : RoleName
  22. {
  23.     /// <summary>
  24.     /// Gets the DNN database Roles.RoleName value for an Administrator user.
  25.     /// </summary>
  26.     public override string Role { get { return "Administrators"; } }
  27. }

RoleNames can be configured to work like enumerators by declaring them as static instances:

  1. public static class Role
  2. {
  3.     private static RoleName _superUser = new SuperUserRole();
  4.     public static RoleName SuperUser { get { return _superUser; } }
  5.  
  6.     private static RoleName _admin = new AdminRole();
  7.     public static RoleName Admin { get { return _admin; } }
  8.  
  9.     private static RoleName _regUser = new RegisteredUserRole();
  10.     public static RoleName RegisteredUser { get { return _regUser; } }
  11.  
  12.     private static RoleName _subscriber = new SubscriberRole();
  13.     public static RoleName Subscriber { get { return _subscriber; } }
  14. }

With these classes now in place, it is easy to write an IsInRole extension method override for the DNN UserInfo class.  I also created a RoleComparer implementation of IEqualityComparer<string> to ensure role equality is case-insensitive.

  1. public static class UserInfoUtil
  2. {
  3.     /// <summary>
  4.     /// Returns a boolean value indicating whether or not the User
  5.     /// is in one of the supplied roles.
  6.     /// </summary>
  7.     /// <param name="user">A UserInfo instance.</param>
  8.     /// <param name="roles">One or more roles.</param>
  9.     /// <returns>true if the User is in one or more of the supplied roles;
  10.     /// otherwise returns false.</returns>
  11.     public static bool IsInRole(this UserInfo user, params RoleName[] roles)
  12.     {
  13.         return IsInRole(new UserInfoAdapter(user), roles,
  14.             IsSuperUserRole, IsNonSuperUserRole);
  15.     }
  16.  
  17.     internal static bool IsInRole(UserInfoAdapter aUser, RoleName[] roles,
  18.         Func<UserInfoAdapter, RoleName[], bool> isSuperUserRole,
  19.         Func<UserInfoAdapter, RoleName[], bool> isNonSuperUserRole)
  20.     {
  21.         if (aUser.IsUserInfoNull) return false;
  22.         bool isInRole = isSuperUserRole(aUser, roles);
  23.         if (!isInRole)
  24.             isInRole = isNonSuperUserRole(aUser, roles);
  25.         return isInRole;
  26.     }
  27.  
  28.     internal static bool IsSuperUserRole(UserInfoAdapter aUser, RoleName[] roles)
  29.     {
  30.         RoleName supervisor = roles.FirstOrDefault(r => r.IsSuperUserRole == true);
  31.         if (supervisor != null)
  32.         {
  33.             return aUser.IsSuperUser;
  34.         }
  35.         else return false;
  36.     }
  37.  
  38.     internal static bool IsNonSuperUserRole(UserInfoAdapter aUser, RoleName[] roles)
  39.     {
  40.         bool isInRole = false;
  41.         string[] userRoles = aUser.Roles;
  42.         int i = 0;
  43.         while (i < roles.Length && !isInRole)
  44.         {
  45.             isInRole = userRoles.Contains(roles[i].Role, new RoleComparer());
  46.             i++;
  47.         }
  48.         return isInRole;
  49.     }
  50. }
  51.  
  52. /// <summary>
  53. /// Compares two roles for equality.
  54. /// </summary>
  55. internal class RoleComparer : IEqualityComparer<string>
  56. {
  57.     /// <summary>
  58.     /// Returns a boolean value indicating equality between
  59.     /// two roles.
  60.     /// </summary>
  61.     /// <param name="roleOne">First role.</param>
  62.     /// <param name="roleTwo">Second role.</param>
  63.     /// <returns>true if the two roles are equal using a case-insensitive
  64.     /// test; otherwise returns false.</returns>
  65.     public bool Equals(string roleOne, string roleTwo)
  66.     {
  67.         return (string.Compare(roleOne, roleTwo, true) == 0);
  68.     }
  69.  
  70.     /// <summary>
  71.     /// Returns a hash code for the role.
  72.     /// </summary>
  73.     /// <param name="role">A user role.</param>
  74.     /// <returns>The hash code of the role string.</returns>
  75.     public int GetHashCode(string role)
  76.     {
  77.         return role.GetHashCode();
  78.     }
  79. }

Here are the unit tests for the UserInfoUtil methods.  These tests use the NUnit and Rhino.Mocks frameworks.

  1. [TestFixture]
  2. public class UserInfoUtilTests
  3. {
  4.     #region Variables and Constants
  5.     private UserInfoAdapter _stubUserInfo;
  6.     private RoleName _suRole;
  7.     private RoleName _adminRole;
  8.     private RoleName _otherRole;
  9.     private Func<UserInfoAdapter, RoleName[], bool> _stubIsSuperUserRole;
  10.     private Func<UserInfoAdapter, RoleName[], bool> _stubIsNonSuperUserRole;
  11.     #endregion
  12.  
  13.  
  14.     #region Setup
  15.  
  16.     [TestFixtureSetUp]
  17.     public void InitFixture()
  18.     {
  19.         _suRole = new TestSuperUserRole();
  20.         _adminRole = new TestAdminRole();
  21.         _otherRole = new TestOtherRole();
  22.     }
  23.  
  24.     [SetUp]
  25.     public void InitTest()
  26.     {
  27.         _stubUserInfo = MockRepository.GenerateStub<UserInfoAdapter>();
  28.         _stubUserInfo.Stub(ui => ui.IsUserInfoNull).Return(false);
  29.  
  30.         _stubIsSuperUserRole = MockRepository.GenerateStub<Func<UserInfoAdapter, RoleName[], bool>>();
  31.         _stubIsNonSuperUserRole = MockRepository.GenerateStub<Func<UserInfoAdapter, RoleName[], bool>>();
  32.     }
  33.  
  34.     #endregion
  35.  
  36.  
  37.     #region Unit Tests
  38.  
  39.     [Test]
  40.     public void IsInRole_IsUserInfoNull_True_Returns_False()
  41.     {
  42.         var stubUserInfo = MockRepository.GenerateStub<UserInfoAdapter>();
  43.         stubUserInfo.Stub(ui => ui.IsUserInfoNull).Return(true);
  44.         Assert.That(UserInfoUtil.IsInRole(stubUserInfo, null, _stubIsSuperUserRole, _stubIsNonSuperUserRole),
  45.             Is.EqualTo(false));
  46.     }
  47.  
  48.     [Test]
  49.     public void IsInRole_IsUserInfoNull_False_IsInSuperUserRole_True_Returns_True()
  50.     {
  51.         _stubIsSuperUserRole.Stub(f => f(Arg<UserInfoAdapter>.Is.Equal(_stubUserInfo), Arg<RoleName[]>.Is.Anything)).Return(true);
  52.         Assert.That(UserInfoUtil.IsInRole(_stubUserInfo, null, _stubIsSuperUserRole, _stubIsNonSuperUserRole),
  53.             Is.EqualTo(true));
  54.     }
  55.  
  56.     [TestCase(true)]
  57.     [TestCase(false)]
  58.     public void IsInRole_IsUserInfoNull_False_IsInSuperUserRole_False_Returns_IsNonSuperUserRole_Value(bool isNonSuperUserRole)
  59.     {
  60.         _stubIsSuperUserRole.Stub(f => f(Arg<UserInfoAdapter>.Is.Equal(_stubUserInfo), Arg<RoleName[]>.Is.Anything)).Return(false);
  61.         _stubIsNonSuperUserRole.Stub(f => f(Arg<UserInfoAdapter>.Is.Equal(_stubUserInfo), Arg<RoleName[]>.Is.Anything)).Return(isNonSuperUserRole);
  62.         Assert.That(UserInfoUtil.IsInRole(_stubUserInfo, null, _stubIsSuperUserRole, _stubIsNonSuperUserRole),
  63.             Is.EqualTo(isNonSuperUserRole));
  64.     }
  65.                 
  66.     [Test]
  67.     public void IsSuperUserRole_SuperUserRole_Not_Provided_Returns_False()
  68.     {
  69.         Assert.That(UserInfoUtil.IsSuperUserRole(_stubUserInfo, new RoleName[] { _adminRole, _otherRole }),
  70.             Is.EqualTo(false));
  71.     }
  72.  
  73.     [TestCase(true)]
  74.     [TestCase(false)]
  75.     public void IsSuperUserRole_SuperUserRole_Provided_Returns_IsSuperUser_Value_From_User(bool isSuperUser)
  76.     {
  77.         _stubUserInfo.Stub(ui => ui.IsSuperUser).Return(isSuperUser);
  78.         Assert.That(UserInfoUtil.IsSuperUserRole(_stubUserInfo, new RoleName[] { _suRole }),
  79.             Is.EqualTo(isSuperUser));
  80.         Assert.That(UserInfoUtil.IsSuperUserRole(_stubUserInfo, new RoleName[] { _suRole, _adminRole, _otherRole }),
  81.             Is.EqualTo(isSuperUser));
  82.     }
  83.         
  84.     [Test]
  85.     public void IsNonSuperUserRole_User_Not_In_Provided_Roles_Returns_False()
  86.     {
  87.         _stubUserInfo.Stub(ui => ui.Roles).Return(new string[] { "X", "Y" });
  88.         Assert.That(UserInfoUtil.IsNonSuperUserRole(_stubUserInfo, new RoleName[] { _adminRole, _otherRole }),
  89.             Is.EqualTo(false));
  90.     }
  91.  
  92.     [Test]
  93.     public void IsNonSuperUserRole_User_In_Provided_Roles_Regardless_Of_Case_Returns_True()
  94.     {
  95.         _stubUserInfo.Stub(ui => ui.Roles).Return(new string[] { "A", "o" });
  96.         Assert.That(UserInfoUtil.IsNonSuperUserRole(_stubUserInfo, new RoleName[] { _adminRole, _otherRole }),
  97.             Is.EqualTo(true));
  98.         Assert.That(UserInfoUtil.IsNonSuperUserRole(_stubUserInfo, new RoleName[] { _adminRole }),
  99.             Is.EqualTo(true));
  100.         Assert.That(UserInfoUtil.IsNonSuperUserRole(_stubUserInfo, new RoleName[] { _otherRole }),
  101.             Is.EqualTo(true));
  102.     }
  103.  
  104.     #endregion
  105.  
  106.     public class TestSuperUserRole : RoleName
  107.     {
  108.         public override string Role { get { return "SU"; } }
  109.         public override bool IsSuperUserRole { get { return true; } }
  110.     }
  111.  
  112.     public class TestAdminRole : RoleName
  113.     {
  114.         public override string Role { get { return "A"; } }
  115.     }
  116.  
  117.     public class TestOtherRole : RoleName
  118.     {
  119.         public override string Role { get { return "O"; } }
  120.     }
  121. }

Here is an example of how to implement.  This method returns true if the current DNN user is either a SuperUser or an Administrator.

  1. public bool HasAdminRole()
  2. {
  3.     return DotNetNuke.Entities.Users.UserController.GetCurrentUserInfo().IsInRole(Role.SuperUser, Role.Admin);
  4. }