[NIFIREG-30] update unit tests and harden client code

Project: http://git-wip-us.apache.org/repos/asf/nifi-registry/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi-registry/commit/2f343e1b
Tree: http://git-wip-us.apache.org/repos/asf/nifi-registry/tree/2f343e1b
Diff: http://git-wip-us.apache.org/repos/asf/nifi-registry/diff/2f343e1b

Branch: refs/heads/master
Commit: 2f343e1bf1eed03e27d6441c4ab3bd36f65f5dbb
Parents: be2b013
Author: Scott Aslan <[email protected]>
Authored: Tue Dec 5 23:28:23 2017 -0500
Committer: Scott Aslan <[email protected]>
Committed: Fri Dec 8 01:10:45 2017 -0500

----------------------------------------------------------------------
 .travis.yml                                     |    2 -
 .../src/main/frontend/karma.conf.js             |    6 +-
 .../platform/core/common/fds-common.module.js   |   48 +
 .../core/common/services/fds-storage.service.js |  219 ++++
 .../core/common/styles/_helperClasses.scss      |    4 +
 .../platform/core/common/styles/_links.scss     |    1 +
 ...nf-registry-add-selected-users-to-group.html |    6 +-
 .../nf-registry-add-selected-users-to-group.js  |   28 +-
 ...registry-add-selected-users-to-group.spec.js |  169 +++
 .../add-user/nf-registry-add-user.spec.js       |   80 ++
 .../nf-registry-create-new-group.js             |   28 +-
 .../nf-registry-create-new-group.spec.js        |   80 ++
 .../users/nf-registry-users-administration.html |   13 +-
 .../users/nf-registry-users-administration.js   |   18 +-
 .../nf-registry-users-adminstration.spec.js     |  206 +++
 .../dialogs/nf-registry-create-bucket.spec.js   |   50 +-
 .../nf-registry-workflow-administration.spec.js |   25 +-
 .../nf-registry-bucket-grid-list-viewer.js      |    3 +
 .../nf-registry-bucket-grid-list-viewer.spec.js |    1 +
 .../registry/nf-registry-grid-list-viewer.js    |    4 +
 .../nf-registry-grid-list-viewer.spec.js        |    2 +
 .../src/main/webapp/nf-registry.animations.js   |    8 +-
 .../src/main/webapp/nf-registry.html            |   12 +-
 .../src/main/webapp/nf-registry.js              |   27 +-
 .../src/main/webapp/nf-registry.spec.js         |    9 +-
 .../src/main/webapp/services/nf-registry.api.js |   66 +-
 .../webapp/services/nf-registry.api.spec.js     | 1178 ++++++++++++++----
 .../main/webapp/services/nf-registry.service.js |  111 +-
 .../webapp/services/nf-registry.service.spec.js |  544 +++++++-
 29 files changed, 2482 insertions(+), 466 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 39650d6..8b2bc35 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,8 +19,6 @@ env:
     - USER_LANGUAGE=en USER_REGION=US'
     - USER_LANGUAGE=fr USER_REGION=FR'
     - USER_LANGUAGE=ja USER_REGION=JP'
-    - USER_LANGUAGE=pt USER_REGION=BR'
-    - USER_LANGUAGE=default USER_REGION=default
 
 os: linux
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/frontend/karma.conf.js
----------------------------------------------------------------------
diff --git a/nifi-registry-web-ui/src/main/frontend/karma.conf.js 
b/nifi-registry-web-ui/src/main/frontend/karma.conf.js
index bffdba2..6be2e75 100644
--- a/nifi-registry-web-ui/src/main/frontend/karma.conf.js
+++ b/nifi-registry-web-ui/src/main/frontend/karma.conf.js
@@ -122,7 +122,7 @@ module.exports = function (config) {
             // {pattern: appBase + '**/*.html', included: false, watched: 
true},
 
             // Images
-            {pattern: '**/*.svg', watched: false, included: false, served: 
true},
+            {pattern: '**/*.svg', watched: false, included: true, served: 
true},
 
             // Paths for debugging with source maps in dev tools
             {pattern: 
'node_modules/@fluid-design-system/dist/platform/**/*.css.map', included: 
false, watched: false},
@@ -137,7 +137,9 @@ module.exports = function (config) {
             '/base/nifi-registry/node_modules/': '/base/node_modules/',
             '/base/systemjs-angular-loader.js': 
'/base/webapp/systemjs-angular-loader.js',
             '/base/nifi-registry/': '/base/webapp/',
-            '/nifi-registry/images/': '/base/webapp/images/'
+            '/nifi-registry/images/': '/base/webapp/images/',
+            '/nifi-registry/explorer/nifi-registry/images/': 
'/base/webapp/images/',
+            '/nifi-registry/explorer/grid-list/buckets/nifi-registry/images/': 
'/base/webapp/images/'
         },
 
         exclude: [],

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/platform/core/common/fds-common.module.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/platform/core/common/fds-common.module.js 
b/nifi-registry-web-ui/src/main/platform/core/common/fds-common.module.js
new file mode 100644
index 0000000..32b5512
--- /dev/null
+++ b/nifi-registry-web-ui/src/main/platform/core/common/fds-common.module.js
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ngCore = require('@angular/core');
+var fdsStorageServiceModule = require('@fluid-design-system/storage-service');
+
+/**
+ * FdsCommonsModule constructor.
+ *
+ * @constructor
+ */
+function FdsCommonsModule() {
+
+};
+
+FdsCommonsModule.prototype = {
+    constructor: FdsCommonsModule
+};
+
+FdsCommonsModule.annotations = [
+    new ngCore.NgModule({
+        imports: [],
+        declarations: [],
+        exports: [],
+        providers: [
+            fdsStorageServiceModule.FdsStorageService
+        ]
+    })
+];
+
+module.exports = {
+    FdsCommonsModule: FdsCommonsModule,
+    FdsStorageService: fdsStorageServiceModule.FdsStorageService
+};

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/platform/core/common/services/fds-storage.service.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/platform/core/common/services/fds-storage.service.js
 
b/nifi-registry-web-ui/src/main/platform/core/common/services/fds-storage.service.js
new file mode 100644
index 0000000..8dff679
--- /dev/null
+++ 
b/nifi-registry-web-ui/src/main/platform/core/common/services/fds-storage.service.js
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Store items for two days before being eligible for removal.
+var MILLIS_PER_DAY = 86400000;
+var TWO_DAYS = MILLIS_PER_DAY * 2;
+
+var isUndefined = function (obj) {
+    return typeof obj === 'undefined';
+};
+
+var isNull = function (obj) {
+    return obj === null;
+};
+
+var isDefinedAndNotNull = function (obj) {
+    return !isUndefined(obj) && !isNull(obj);
+};
+
+/**
+ * Checks the expiration for the specified entry.
+ *
+ * @param {object} entry
+ * @returns {boolean}
+ */
+var checkExpiration = function (entry) {
+    if (isDefinedAndNotNull(entry.expires)) {
+        // get the expiration
+        var expires = new Date(entry.expires);
+        var now = new Date();
+
+        // return whether the expiration date has passed
+        return expires.valueOf() < now.valueOf();
+    } else {
+        return false;
+    }
+};
+
+/**
+ * Gets an enty for the key. The entry expiration is not checked.
+ *
+ * @param {string} key
+ */
+var getEntry = function (key) {
+    try {
+        // parse the entry
+        var entry = JSON.parse(localStorage.getItem(key));
+
+        // ensure the entry and item are present
+        if (isDefinedAndNotNull(entry)) {
+            return entry;
+        } else {
+            return null;
+        }
+    } catch (e) {
+        return null;
+    }
+};
+
+/**
+ * FdsStorageService constructor.
+ * @constructor
+ */
+function FdsStorageService() {
+};
+
+FdsStorageService.prototype = {
+    constructor: FdsStorageService,
+    /**
+     * Initializes the storage. Items will be persisted for two days. Once the 
scripts runs
+     * thereafter, all eligible items will be removed. This strategy does not 
support persistence.
+     */
+    init: function () {
+        for (var i = 0; i < localStorage.length; i++) {
+            try {
+                // get the next item
+                var key = localStorage.key(i);
+
+                // attempt to get the item which will expire if necessary
+                this.getItem(key);
+            } catch (e) {
+            }
+        }
+    },
+
+    /**
+     * Stores the specified item.
+     *
+     * @param {string} key
+     * @param {object} item
+     * @param {integer} expires
+     */
+    setItem: function (key, item, expires) {
+        // calculate the expiration
+        expires = isDefinedAndNotNull(expires) ? expires : new 
Date().valueOf() + TWO_DAYS;
+
+        // create the entry
+        var entry = {
+            expires: expires,
+            item: item
+        };
+
+        // store the item
+        localStorage.setItem(key, JSON.stringify(entry));
+    },
+
+    /**
+     * Returns whether there is an entry for this key. This will not check the 
expiration. If
+     * the entry is expired, it will return null on a subsequent getItem 
invocation.
+     *
+     * @param {string} key
+     * @returns {boolean}
+     */
+    hasItem: function (key) {
+        return getEntry(key) !== null;
+    },
+
+    /**
+     * Gets the item with the specified key. If an item with this key does
+     * not exist, null is returned. If an item exists but cannot be parsed
+     * or is malformed/unrecognized, null is returned.
+     *
+     * @param {type} key
+     */
+    getItem: function (key) {
+        var entry = getEntry(key);
+        if (entry === null) {
+            return null;
+        }
+
+        // if the entry is expired, drop it and return null
+        if (checkExpiration(entry)) {
+            this.removeItem(key);
+            return null;
+        }
+
+        // if the entry has the specified field return its value
+        if (isDefinedAndNotNull(entry['item'])) {
+            return entry['item'];
+        } else {
+            return null;
+        }
+    },
+
+    /**
+     * Gets the expiration for the specified item. This will not check the 
expiration. If
+     * the entry is expired, it will return null on a subsequent getItem 
invocation.
+     *
+     * @param {string} key
+     * @returns {integer}
+     */
+    getItemExpiration: function (key) {
+        var entry = getEntry(key);
+        if (entry === null) {
+            return null;
+        }
+
+        // if the entry has the specified field return its value
+        if (isDefinedAndNotNull(entry['expires'])) {
+            return entry['expires'];
+        } else {
+            return null;
+        }
+    },
+
+    /**
+     * Extracts the subject from the specified jwt. If the jwt is not as 
expected
+     * an empty string is returned.
+     *
+     * @param {string} jwt
+     * @returns {string}
+     */
+    getJwtPayload: function (jwt) {
+        if (isDefinedAndNotNull(jwt)) {
+            var segments = jwt.split(/\./);
+            if (segments.length !== 3) {
+                return '';
+            }
+
+            var rawPayload = window.atob(segments[1]);
+            var payload = JSON.parse(rawPayload);
+
+            if (isDefinedAndNotNull(payload)) {
+                return payload;
+            } else {
+                return null;
+            }
+        }
+
+        return null;
+    },
+
+    /**
+     * Removes the item with the specified key.
+     *
+     * @param {type} key
+     */
+    removeItem: function (key) {
+        localStorage.removeItem(key);
+    }
+};
+
+FdsStorageService.parameters = [];
+
+module.exports = FdsStorageService;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/platform/core/common/styles/_helperClasses.scss
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/platform/core/common/styles/_helperClasses.scss 
b/nifi-registry-web-ui/src/main/platform/core/common/styles/_helperClasses.scss
index c878c15..6b00b46 100644
--- 
a/nifi-registry-web-ui/src/main/platform/core/common/styles/_helperClasses.scss
+++ 
b/nifi-registry-web-ui/src/main/platform/core/common/styles/_helperClasses.scss
@@ -74,3 +74,7 @@
 .fill-available-width {
   width: 100%;
 }
+
+.pointer {
+  cursor: pointer;
+}

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/platform/core/common/styles/_links.scss
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/platform/core/common/styles/_links.scss 
b/nifi-registry-web-ui/src/main/platform/core/common/styles/_links.scss
index 2ce0fa9..1a5896d 100644
--- a/nifi-registry-web-ui/src/main/platform/core/common/styles/_links.scss
+++ b/nifi-registry-web-ui/src/main/platform/core/common/styles/_links.scss
@@ -22,6 +22,7 @@ body[fds] .link {
   font-size: 14px;
   text-decoration: none;
   line-height: 24px;
+  cursor: pointer;
 }
 
 body[fds] .link:hover {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.html
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.html
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.html
index bea66a5..e76e898 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.html
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.html
@@ -24,7 +24,7 @@ limitations under the License.
     </div>
     <div class="pad-bottom-md">
         <div 
id="nifi-registry-users-administration-list-container-column-header" 
class="td-data-table">
-            <div class="td-data-table-column" (click)="sortUserGroups($event, 
column)"
+            <div class="td-data-table-column" (click)="sortUserGroups(column)"
                  *ngFor="let column of userGroupsColumns"
                  fxFlex="{{column.width}}">
                 {{column.label}}
@@ -34,8 +34,8 @@ limitations under the License.
                    aria-hidden="true"></i>
             </div>
             <div class="td-data-table-column">
-                <mat-checkbox [(ngModel)]="allUsersAndGroupsSelected"
-                              (checked)="allUsersAndGroupsSelected"
+                <mat-checkbox [(ngModel)]="allGroupsSelected"
+                              (checked)="allGroupsSelected"
                               
(change)="toggleUserGroupsSelectAll()"></mat-checkbox>
             </div>
         </div>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.js
index 39dbb90..e66630a 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.js
@@ -27,15 +27,15 @@ var $ = require('jquery');
  * NfRegistryAddSelectedToGroup constructor.
  *
  * @param nfRegistryApi         The api service.
- * @param TdDataTableService    The covalent data table service module.
+ * @param tdDataTableService    The covalent data table service module.
  * @param nfRegistryService     The nf-registry.service module.
  * @param matDialogRef          The angular material dialog ref.
- * @param FdsSnackBarService    The FDS snack bar service module.
+ * @param fdsSnackBarService    The FDS snack bar service module.
  * @constructor
  */
-function NfRegistryAddSelectedToGroup(nfRegistryApi, TdDataTableService, 
nfRegistryService, matDialogRef, FdsSnackBarService) {
-    this.dataTableService = TdDataTableService;
-    this.snackBarService = FdsSnackBarService;
+function NfRegistryAddSelectedToGroup(nfRegistryApi, tdDataTableService, 
nfRegistryService, matDialogRef, fdsSnackBarService) {
+    this.dataTableService = tdDataTableService;
+    this.snackBarService = fdsSnackBarService;
     this.nfRegistryService = nfRegistryService;
     this.nfRegistryApi = nfRegistryApi;
     this.dialogRef = matDialogRef;
@@ -43,7 +43,7 @@ function NfRegistryAddSelectedToGroup(nfRegistryApi, 
TdDataTableService, nfRegis
     //make an independent copy of the groups for sorting and selecting within 
the scope of this component
     this.groups = $.extend(true, [], this.nfRegistryService.groups);
     this.selectedGroups = [];
-    this.allUsersAndGroupsSelected = false;
+    this.allGroupsSelected = false;
     this.isAddSelectedUsersToSelectedGroupsDisabled = true;
     this.userGroupsSearchTerms = [];
     this.userGroupsColumns = [
@@ -111,7 +111,7 @@ NfRegistryAddSelectedToGroup.prototype = {
      *
      * @param column    The column to sort by.
      */
-    sortUserGroups: function (sortEvent, column) {
+    sortUserGroups: function (column) {
         if (column.sortable) {
             var sortBy = column.name;
             var sortOrder = column.sortOrder = (column.sortOrder === 'ASC') ? 
'DESC' : 'ASC';
@@ -120,11 +120,11 @@ NfRegistryAddSelectedToGroup.prototype = {
     },
 
     /**
-     * Checks the `allUsersAndGroupsSelected` property state and either selects
+     * Checks the `allGroupsSelected` property state and either selects
      * or deselects each of the `filteredUserGroups`.
      */
     toggleUserGroupsSelectAll: function () {
-        if (this.allUsersAndGroupsSelected) {
+        if (this.allGroupsSelected) {
             this.selectAllUserGroups();
         } else {
             this.deselectAllUserGroups();
@@ -133,7 +133,7 @@ NfRegistryAddSelectedToGroup.prototype = {
 
     /**
      * Sets the `checked` property of each of the `filteredUserGroups` to true
-     * and sets the `isAddSelectedUsersToSelectedGroupsDisabled` and the 
`allUsersAndGroupsSelected`
+     * and sets the `isAddSelectedUsersToSelectedGroupsDisabled` and the 
`allGroupsSelected`
      * properties accordingly.
      */
     selectAllUserGroups: function () {
@@ -141,12 +141,12 @@ NfRegistryAddSelectedToGroup.prototype = {
             c.checked = true;
         });
         this.isAddSelectedUsersToSelectedGroupsDisabled = false;
-        this.allUsersAndGroupsSelected = true;
+        this.allGroupsSelected = true;
     },
 
     /**
      * Sets the `checked` property of each group to false
-     * and sets the `isAddSelectedUsersToSelectedGroupsDisabled` and the 
`allUsersAndGroupsSelected`
+     * and sets the `isAddSelectedUsersToSelectedGroupsDisabled` and the 
`allGroupsSelected`
      * properties accordingly.
      */
     deselectAllUserGroups: function () {
@@ -154,7 +154,7 @@ NfRegistryAddSelectedToGroup.prototype = {
             c.checked = false;
         });
         this.isAddSelectedUsersToSelectedGroupsDisabled = true;
-        this.allUsersAndGroupsSelected = false;
+        this.allGroupsSelected = false;
     },
 
     /**
@@ -180,7 +180,7 @@ NfRegistryAddSelectedToGroup.prototype = {
             this.isAddSelectedUsersToSelectedGroupsDisabled = true;
         }
 
-        this.allUsersAndGroupsSelected = allSelected;
+        this.allGroupsSelected = allSelected;
     },
 
     /**

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.spec.js
new file mode 100644
index 0000000..c47d255
--- /dev/null
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.spec.js
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the 'License'); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var NfRegistryApi = require('nifi-registry/services/nf-registry.api.js');
+var NfRegistryService = 
require('nifi-registry/services/nf-registry.service.js');
+var NfRegistryAddSelectedToGroup = 
require('nifi-registry/components/administration/users/dialogs/add-selected-users-to-group/nf-registry-add-selected-users-to-group.js');
+var rxjs = require('rxjs/Rx');
+var covalentCore = require('@covalent/core');
+var fdsSnackBarsModule = require('@fluid-design-system/snackbars');
+
+describe('NfRegistryAddSelectedToGroup Component isolated unit tests', 
function () {
+    var comp;
+    var nfRegistryService;
+    var nfRegistryApi;
+    var snackBarService;
+    var dataTableService;
+
+    beforeEach(function () {
+        nfRegistryService = new NfRegistryService();
+        // setup the nfRegistryService
+        nfRegistryService.groups = [{identifier: 1, identity: 'Group 1'}];
+        nfRegistryService.filteredUsers = [{identifier: 2, identity: 'User 
1'}];
+
+        nfRegistryApi = new NfRegistryApi();
+        snackBarService = new fdsSnackBarsModule.FdsSnackBarService();
+        dataTableService = new covalentCore.TdDataTableService();
+        comp = new NfRegistryAddSelectedToGroup(nfRegistryApi, 
dataTableService, nfRegistryService, {
+            close: function () {
+            }
+        }, snackBarService);
+
+        // Spy
+        spyOn(nfRegistryApi, 'getUserGroup').and.callFake(function () {
+        }).and.returnValue(rxjs.Observable.of({identifier: 1, identity: 'Group 
1'}));
+        spyOn(nfRegistryApi, 'updateUserGroup').and.callFake(function () {
+        }).and.returnValue(rxjs.Observable.of({identifier: 1, identity: 'Group 
1'}));
+        spyOn(comp.dialogRef, 'close');
+        spyOn(comp.snackBarService, 'openCoaster');
+        spyOn(comp, 'filterGroups').and.callThrough();
+
+        // initialize the component
+        comp.ngOnInit();
+
+        //assertions
+        expect(comp.filterGroups).toHaveBeenCalled();
+        expect(comp.filteredUserGroups[0].identity).toEqual('Group 1');
+        expect(comp.filteredUserGroups.length).toBe(1);
+        expect(comp).toBeDefined();
+    });
+
+    it('should make a call to the api to add selected users to selected 
groups', function () {
+        // select a group
+        comp.filteredUserGroups[0].checked = true;
+
+        // the function to test
+        comp.addSelectedUsersToSelectedGroups();
+
+        //assertions
+        expect(comp.dialogRef.close).toHaveBeenCalled();
+        expect(comp.snackBarService.openCoaster).toHaveBeenCalled();
+    });
+
+    it('should determine all user groups are selected', function () {
+        // select a group
+        comp.filteredUserGroups[0].checked = true;
+
+        // the function to test
+        comp.determineAllUserGroupsSelectedState();
+
+        //assertions
+        expect(comp.allGroupsSelected).toBe(true);
+        expect(comp.isAddSelectedUsersToSelectedGroupsDisabled).toBe(false);
+    });
+
+    it('should determine all user groups are not selected', function () {
+        // the function to test
+        comp.determineAllUserGroupsSelectedState();
+
+        //assertions
+        expect(comp.allGroupsSelected).toBe(false);
+        expect(comp.isAddSelectedUsersToSelectedGroupsDisabled).toBe(true);
+    });
+
+    it('should select all groups.', function () {
+        // The function to test
+        comp.selectAllUserGroups();
+
+        //assertions
+        expect(comp.filteredUserGroups[0].checked).toBe(true);
+        expect(comp.isAddSelectedUsersToSelectedGroupsDisabled).toBe(false);
+        expect(comp.allGroupsSelected).toBe(true);
+    });
+
+    it('should deselect all groups.', function () {
+        // select a group
+        comp.filteredUserGroups[0].checked = true;
+
+        // The function to test
+        comp.deselectAllUserGroups();
+
+        //assertions
+        expect(comp.filteredUserGroups[0].checked).toBe(false);
+        expect(comp.isAddSelectedUsersToSelectedGroupsDisabled).toBe(true);
+        expect(comp.allGroupsSelected).toBe(false);
+    });
+
+    it('should toggle all groups `checked` properties to true.', function () {
+        //Spy
+        spyOn(comp, 'selectAllUserGroups').and.callFake(function () {
+        });
+
+        comp.allGroupsSelected = true;
+
+        // The function to test
+        comp.toggleUserGroupsSelectAll();
+
+        //assertions
+        expect(comp.selectAllUserGroups).toHaveBeenCalled();
+    });
+
+    it('should toggle all groups `checked` properties to false.', function () {
+        //Spy
+        spyOn(comp, 'deselectAllUserGroups').and.callFake(function () {
+        });
+
+        comp.allGroupsSelected = false;
+
+        // The function to test
+        comp.toggleUserGroupsSelectAll();
+
+        //assertions
+        expect(comp.deselectAllUserGroups).toHaveBeenCalled();
+    });
+
+    it('should sort `groups` by `column`', function () {
+        // object to be updated by the test
+        var column = {name: 'name', label: 'Group Name', sortable: true};
+
+        // The function to test
+        comp.sortUserGroups(column);
+
+        //assertions
+        var filterGroupsCall = comp.filterGroups.calls.mostRecent();
+        expect(filterGroupsCall.args[0]).toBe('name');
+        expect(filterGroupsCall.args[1]).toBe('ASC');
+    });
+
+    it('should cancel the creation of a new user', function () {
+        // the function to test
+        comp.cancel();
+
+        //assertions
+        expect(comp.dialogRef.close).toHaveBeenCalled();
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user/nf-registry-add-user.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user/nf-registry-add-user.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user/nf-registry-add-user.spec.js
new file mode 100644
index 0000000..b6e5b20
--- /dev/null
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user/nf-registry-add-user.spec.js
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the 'License'); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var NfRegistryApi = require('nifi-registry/services/nf-registry.api.js');
+var NfRegistryService = 
require('nifi-registry/services/nf-registry.service.js');
+var NfRegistryAddUser = 
require('nifi-registry/components/administration/users/dialogs/add-user/nf-registry-add-user.js');
+var rxjs = require('rxjs/Rx');
+
+describe('NfRegistryAddUser Component isolated unit tests', function () {
+    var comp;
+    var nfRegistryService;
+    var nfRegistryApi;
+
+    beforeEach(function () {
+        nfRegistryService = new NfRegistryService();
+        nfRegistryApi = new NfRegistryApi();
+        comp = new NfRegistryAddUser(nfRegistryApi, nfRegistryService, {
+            close: function () {
+            }
+        });
+
+        // Spy
+        spyOn(nfRegistryApi, 'addUser').and.callFake(function () {
+        }).and.returnValue(rxjs.Observable.of([{
+            'identifier': '2e04b4fb-9513-47bb-aa74-1ae34616bfdc',
+            'identity': 'New User #1'
+        }]));
+        spyOn(nfRegistryService, 'filterUsersAndGroups');
+        spyOn(comp.dialogRef, 'close');
+    });
+
+    it('should make a call to the api to create a new user and close the 
dialog', function () {
+        // the function to test
+        comp.addUser({value: 'New User #1'});
+
+        //assertions
+        expect(comp).toBeDefined();
+        expect(nfRegistryService.users.length).toBe(1);
+        expect(nfRegistryService.allUsersAndGroupsSelected).toBe(false);
+        expect(nfRegistryService.filterUsersAndGroups).toHaveBeenCalled();
+        expect(comp.dialogRef.close).toHaveBeenCalled();
+    });
+
+    it('should make a call to the api to create a new user and keep the dialog 
open', function () {
+        // setup the component
+        comp.keepDialogOpen = true;
+
+        // the function to test
+        comp.addUser({value: 'New User #1'});
+
+        //assertions
+        expect(comp).toBeDefined();
+        expect(nfRegistryService.users.length).toBe(1);
+        expect(nfRegistryService.allUsersAndGroupsSelected).toBe(false);
+        expect(nfRegistryService.filterUsersAndGroups).toHaveBeenCalled();
+        expect(comp.dialogRef.close.calls.count()).toEqual(0);
+    });
+
+    it('should cancel the creation of a new user', function () {
+        // the function to test
+        comp.cancel();
+
+        //assertions
+        expect(comp.dialogRef.close).toHaveBeenCalled();
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.js
index c1fc2c6..d3a574e 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.js
@@ -44,24 +44,16 @@ NfRegistryCreateNewGroup.prototype = {
      * @param createNewGroupInput     The createNewGroupInput element.
      */
     createNewGroup: function (createNewGroupInput) {
-        if(!this.nfRegistryService.isMultiUserGroupActionsDisabled) {
-            var self = this;
-            var selectedUsers = 
this.nfRegistryService.filteredUsers.filter(function (filteredUser) {
-                return filteredUser.checked;
-            });
-            var selectedUserGroups = 
this.nfRegistryService.filteredUserGroups.filter(function (filteredUserGroup) {
-                return filteredUserGroup.checked;
-            });
-
-            this.nfRegistryApi.createNewGroup(null, createNewGroupInput.value, 
selectedUsers.concat(selectedUserGroups)).subscribe(function (group) {
-                self.nfRegistryService.groups.push(group);
-                self.nfRegistryService.filterUsersAndGroups();
-                self.nfRegistryService.allUsersAndGroupsSelected = false;
-                if (self.keepDialogOpen !== true) {
-                    self.dialogRef.close();
-                }
-            });
-        }
+        var self = this;
+        // create new group with any selected users added to the new group
+        this.nfRegistryApi.createNewGroup(null, createNewGroupInput.value, 
this.nfRegistryService.getSelectedUsers()).subscribe(function (group) {
+            self.nfRegistryService.groups.push(group);
+            self.nfRegistryService.filterUsersAndGroups();
+            self.nfRegistryService.allUsersAndGroupsSelected = false;
+            if (self.keepDialogOpen !== true) {
+                self.dialogRef.close();
+            }
+        });
     },
 
     /**

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.spec.js
new file mode 100644
index 0000000..7c51e45
--- /dev/null
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.spec.js
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the 'License'); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var NfRegistryApi = require('nifi-registry/services/nf-registry.api.js');
+var NfRegistryService = 
require('nifi-registry/services/nf-registry.service.js');
+var NfRegistryCreateNewGroup = 
require('nifi-registry/components/administration/users/dialogs/create-new-group/nf-registry-create-new-group.js');
+var rxjs = require('rxjs/Rx');
+
+describe('NfRegistryCreateNewGroup Component isolated unit tests', function () 
{
+    var comp;
+    var nfRegistryService;
+    var nfRegistryApi;
+
+    beforeEach(function () {
+        nfRegistryService = new NfRegistryService();
+        nfRegistryApi = new NfRegistryApi();
+        comp = new NfRegistryCreateNewGroup(nfRegistryApi, nfRegistryService, {
+            close: function () {
+            }
+        });
+
+        // Spy
+        spyOn(nfRegistryApi, 'createNewGroup').and.callFake(function () {
+        }).and.returnValue(rxjs.Observable.of([{
+            'identifier': '2e04b4fb-9513-47bb-aa74-1ae34616bfdc',
+            'identity': 'New Group #1'
+        }]));
+        spyOn(nfRegistryService, 'filterUsersAndGroups');
+        spyOn(comp.dialogRef, 'close');
+    });
+
+    it('should make a call to the api to create a new group and close the 
dialog.', function () {
+        // the function to test
+        comp.createNewGroup({value: 'New Group #1'});
+
+        //assertions
+        expect(comp).toBeDefined();
+        expect(nfRegistryService.groups.length).toBe(1);
+        expect(nfRegistryService.allUsersAndGroupsSelected).toBe(false);
+        expect(nfRegistryService.filterUsersAndGroups).toHaveBeenCalled();
+        expect(comp.dialogRef.close).toHaveBeenCalled();
+    });
+
+    it('should make a call to the api to create a new group and keep the 
dialog open.', function () {
+        // setup the component
+        comp.keepDialogOpen = true;
+
+        // the function to test
+        comp.createNewGroup({value: 'New Group #1'});
+
+        //assertions
+        expect(comp).toBeDefined();
+        expect(nfRegistryService.groups.length).toBe(1);
+        expect(nfRegistryService.allUsersAndGroupsSelected).toBe(false);
+        expect(nfRegistryService.filterUsersAndGroups).toHaveBeenCalled();
+        expect(comp.dialogRef.close.calls.count()).toEqual(0);
+    });
+
+    it('should cancel the creation of a new group', function () {
+        // the function to test
+        comp.cancel();
+
+        //assertions
+        expect(comp.dialogRef.close).toHaveBeenCalled();
+    });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.html
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.html
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.html
index 60f1270..b7fe21c 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.html
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.html
@@ -25,28 +25,29 @@ limitations under the License.
             <button class="push-top-sm push-right-sm" color="fds-secondary" 
mat-raised-button (click)="addUser()">
                 Add User
             </button>
-            <button class="push-top-sm" 
[disabled]="nfRegistryService.isMultiUserActionsDisabled" color="fds-primary"
+            <button class="push-top-sm" color="fds-primary"
                     mat-raised-button [matMenuTriggerFor]="userActionMenu">
                 Actions<i class="fa fa-caret-down" aria-hidden="true"></i>
             </button>
         </div>
         <mat-menu class="fds-primary-dropdown-button-menu" 
#userActionMenu="matMenu" [overlapTrigger]="false">
             <button mat-menu-item
+                    [disabled]="(nfRegistryService.getSelectedGroups().length 
=== 0) && (nfRegistryService.getSelectedUsers().length === 0)"
                     (click)="nfRegistryService.deleteSelectedUsersAndGroups()">
                 <span>Delete</span>
             </button>
-            <button mat-menu-item 
[disabled]="nfRegistryService.isMultiUserGroupActionsDisabled"
+            <button mat-menu-item 
[disabled]="(nfRegistryService.getSelectedGroups().length > 0)"
                     (click)="createNewGroup()">
                 <span>Create new group</span>
             </button>
-            <button mat-menu-item 
[disabled]="nfRegistryService.isMultiUserGroupActionsDisabled || 
nfRegistryService.groups.length === 0"
+            <button mat-menu-item 
[disabled]="nfRegistryService.getSelectedGroups().length > 0 || 
nfRegistryService.getSelectedUsers().length === 0 || 
nfRegistryService.groups.length === 0"
                     (click)="addSelectedUsersToGroup()">
                 <span>Add selected users to group</span>
             </button>
         </mat-menu>
         <div 
id="nifi-registry-users-administration-list-container-column-header" 
fxLayout="row"
              fxLayoutAlign="space-between center" class="td-data-table">
-            <div class="td-data-table-column" 
(click)="nfRegistryService.sortUsersAndGroups($event, column)"
+            <div class="td-data-table-column" 
(click)="nfRegistryService.sortUsersAndGroups(column)"
                  *ngFor="let column of nfRegistryService.userColumns"
                  fxFlex="{{column.width}}">
                 {{column.label}}
@@ -67,7 +68,7 @@ limitations under the License.
             <div [ngClass]="{'selected' : row.checked}" *ngFor="let row of 
nfRegistryService.filteredUserGroups"
                  (click)="row.checked = 
!row.checked;nfRegistryService.determineAllUsersAndGroupsSelectedState(row)">
                 <div *ngFor="let column of nfRegistryService.userColumns" 
fxLayout="row" fxLayoutAlign="space-between center" class="td-data-table-row">
-                    <div *ngIf="row.users && row.users.length > 0" 
class="td-data-table-cell" fxFlex="{{column.width}}">
+                    <div class="td-data-table-cell" fxFlex="{{column.width}}">
                         <div>
                             <i class="fa fa-users push-right-sm" 
aria-hidden="true"></i>{{column.format ? column.format(row[column.name]) : 
row[column.name]}}
                         </div>
@@ -116,7 +117,7 @@ limitations under the License.
             <div [ngClass]="{'selected' : row.checked}" *ngFor="let row of 
nfRegistryService.filteredUsers"
                  (click)="row.checked = 
!row.checked;nfRegistryService.determineAllUsersAndGroupsSelectedState(row)">
                 <div *ngFor="let column of nfRegistryService.userColumns" 
fxLayout="row" fxLayoutAlign="space-between center" class="td-data-table-row">
-                    <div *ngIf="!row.users" class="td-data-table-cell" 
fxFlex="{{column.width}}">
+                    <div class="td-data-table-cell" fxFlex="{{column.width}}">
                         <div>
                             {{column.format ? column.format(row[column.name]) 
: row[column.name]}}
                         </div>

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.js
index 9c833cb..17e288c 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-administration.js
@@ -105,8 +105,6 @@ NfRegistryUsersAdministration.prototype = {
         this.nfRegistryService.users = this.nfRegistryService.filteredUsers = 
[];
         this.nfRegistryService.groups = 
this.nfRegistryService.filteredUserGroups = [];
         this.nfRegistryService.allUsersAndGroupsSelected = false;
-        this.nfRegistryService.isMultiUserActionsDisabled = true;
-        this.nfRegistryService.isMultiUserGroupActionsDisabled = false;
     },
 
     /**
@@ -124,25 +122,19 @@ NfRegistryUsersAdministration.prototype = {
     },
 
     /**
-     * Opens the add selected users to group dialog.
+     * Opens the add selected users to groups dialog.
      */
     addSelectedUsersToGroup: function () {
-        // the menu button that calls this method should be disabled if a 
group is selected
-        // let's just make sure
-        var selectedUserGroups = 
this.nfRegistryService.filteredUserGroups.filter(function (filteredUserGroup) {
-            return filteredUserGroup.checked;
-        });
-
-        if (selectedUserGroups.length > 0) {
+        if (this.nfRegistryService.getSelectedGroups().length === 0) {
+            // ok...only users are currently selected...go ahead and open the 
dialog to select groups
+            this.dialog.open(NfRegistryAddSelectedUsersToGroup);
+        } else {
             self.dialogService.openConfirm({
                 title: 'Error: Groups may not be added to a group. Please 
deselect any groups and try again',
                 message: error.message,
                 acceptButton: 'Ok',
                 acceptButtonColor: 'fds-warn'
             });
-        } else {
-            // ok...only users are currently selected...go ahead and open the 
dialog to select groups
-            this.dialog.open(NfRegistryAddSelectedUsersToGroup);
         }
     }
 };

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-adminstration.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-adminstration.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-adminstration.spec.js
new file mode 100644
index 0000000..dc82791
--- /dev/null
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/users/nf-registry-users-adminstration.spec.js
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var NfRegistryRoutes = require('nifi-registry/nf-registry.routes.js');
+var ngCoreTesting = require('@angular/core/testing');
+var ngCommonHttpTesting = require('@angular/common/http/testing');
+var ngCommon = require('@angular/common');
+var ngRouter = require('@angular/router');
+var ngPlatformBrowser = require('@angular/platform-browser');
+var FdsDemo = 
require('nifi-registry/components/fluid-design-system/fds-demo.js');
+var NfRegistry = require('nifi-registry/nf-registry.js');
+var NfRegistryApi = require('nifi-registry/services/nf-registry.api.js');
+var NfRegistryService = 
require('nifi-registry/services/nf-registry.service.js');
+var NfPageNotFoundComponent = 
require('nifi-registry/components/page-not-found/nf-registry-page-not-found.js');
+var NfRegistryExplorer = 
require('nifi-registry/components/explorer/nf-registry-explorer.js');
+var NfRegistryAdministration = 
require('nifi-registry/components/administration/nf-registry-administration.js');
+var NfRegistryUsersAdministration = 
require('nifi-registry/components/administration/users/nf-registry-users-administration.js');
+var NfRegistryAddUser = 
require('nifi-registry/components/administration/users/dialogs/add-user/nf-registry-add-user.js');
+var NfRegistryUserDetails = 
require('nifi-registry/components/administration/users/details/nf-registry-user-details.js');
+var NfRegistryUserPermissions = 
require('nifi-registry/components/administration/users/permissions/nf-registry-user-permissions.js');
+var NfRegistryUserGroupPermissions = 
require('nifi-registry/components/administration/user-group/permissions/nf-registry-user-group-permissions.js');
+var NfRegistryBucketPermissions = 
require('nifi-registry/components/administration/workflow/buckets/permissions/nf-registry-bucket-permissions.js');
+var NfRegistryWorkflowAdministration = 
require('nifi-registry/components/administration/workflow/nf-registry-workflow-administration.js');
+var NfRegistryCreateBucket = 
require('nifi-registry/components/administration/workflow/dialogs/nf-registry-create-bucket.js');
+var NfRegistryGridListViewer = 
require('nifi-registry/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js');
+var NfRegistryBucketGridListViewer = 
require('nifi-registry/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js');
+var NfRegistryDropletGridListViewer = 
require('nifi-registry/components/explorer/grid-list/registry/nf-registry-droplet-grid-list-viewer.js');
+var fdsCore = require('@fluid-design-system/core');
+var ngMoment = require('angular2-moment');
+var rxjs = require('rxjs/Rx');
+var ngHttp = require('@angular/http');
+var ngCommonHttp = require('@angular/common/http');
+var NfRegistryTokenInterceptor = 
require('nifi-registry/services/nf-registry.token.interceptor.js');
+var NfRegistryAuthService = 
require('nifi-registry/services/nf-registry.auth.service.js');
+var NfStorage = require('nifi-registry/services/nf-storage.service.js');
+
+describe('NfRegistryUsersAdministration Component', function () {
+    var comp;
+    var fixture;
+    var de;
+    var el;
+    var nfRegistryService;
+    var nfRegistryApi;
+
+    beforeEach(function () {
+        ngCoreTesting.TestBed.configureTestingModule({
+            imports: [
+                ngMoment.MomentModule,
+                ngHttp.HttpModule,
+                ngHttp.JsonpModule,
+                ngCommonHttp.HttpClientModule,
+                fdsCore,
+                NfRegistryRoutes,
+                ngCommonHttpTesting.HttpClientTestingModule
+            ],
+            declarations: [
+                FdsDemo,
+                NfRegistry,
+                NfRegistryExplorer,
+                NfRegistryAdministration,
+                NfRegistryUsersAdministration,
+                NfRegistryUserDetails,
+                NfRegistryUserPermissions,
+                NfRegistryUserGroupPermissions,
+                NfRegistryBucketPermissions,
+                NfRegistryAddUser,
+                NfRegistryWorkflowAdministration,
+                NfRegistryCreateBucket,
+                NfRegistryGridListViewer,
+                NfRegistryBucketGridListViewer,
+                NfRegistryDropletGridListViewer,
+                NfPageNotFoundComponent
+            ],
+            entryComponents: [
+                NfRegistryCreateBucket
+            ],
+            providers: [
+                NfRegistryService,
+                NfRegistryAuthService,
+                NfRegistryApi,
+                NfStorage,
+                {
+                    provide: ngCommonHttp.HTTP_INTERCEPTORS,
+                    useClass: NfRegistryTokenInterceptor,
+                    multi: true
+                },
+                {
+                    provide: ngCommon.APP_BASE_HREF,
+                    useValue: '/'
+                }, {
+                    provide: ngRouter.ActivatedRoute,
+                    useValue: {
+                        params: rxjs.Observable.of({})
+                    }
+                }
+            ]
+        });
+
+        fixture = 
ngCoreTesting.TestBed.createComponent(NfRegistryUsersAdministration);
+
+        // test instance
+        comp = fixture.componentInstance;
+
+        // from the root injector
+        nfRegistryService = ngCoreTesting.TestBed.get(NfRegistryService);
+        nfRegistryApi = ngCoreTesting.TestBed.get(NfRegistryApi);
+        de = 
fixture.debugElement.query(ngPlatformBrowser.By.css('#nifi-registry-users-administration-perspective'));
+        el = de.nativeElement;
+
+        // Spy
+        spyOn(nfRegistryApi, 'ticketExchange').and.callFake(function () 
{}).and.returnValue(rxjs.Observable.of({}));
+        spyOn(nfRegistryService, 'loadCurrentUser').and.callFake(function () 
{}).and.returnValue(rxjs.Observable.of({}));
+        spyOn(nfRegistryApi, 'getUsers').and.callFake(function () {
+        }).and.returnValue(rxjs.Observable.of([{
+            "identifier": "2e04b4fb-9513-47bb-aa74-1ae34616bfdc",
+            "identity": "User #1"
+        }]));
+        spyOn(nfRegistryApi, 'getUserGroups').and.callFake(function () {
+        }).and.returnValue(rxjs.Observable.of([{
+            "identifier": "5e04b4fb-9513-47bb-aa74-1ae34616bfdc",
+            "identity": "Group #1"}]));
+        spyOn(nfRegistryService, 'filterUsersAndGroups');
+    });
+
+    it('should have a defined component', ngCoreTesting.async(function () {
+        fixture.detectChanges();
+        fixture.whenStable().then(function () { // wait for async getBuckets
+            fixture.detectChanges();
+
+            //assertions
+            expect(comp).toBeDefined();
+            expect(de).toBeDefined();
+            expect(nfRegistryService.adminPerspective).toBe('users');
+            expect(nfRegistryService.users[0].identity).toEqual('User #1');
+            expect(nfRegistryService.users.length).toBe(1);
+            expect(nfRegistryService.groups[0].identity).toEqual('Group #1');
+            expect(nfRegistryService.groups.length).toBe(1);
+            expect(nfRegistryService.filterUsersAndGroups).toHaveBeenCalled();
+        });
+    }));
+
+    it('should open a dialog to create a new user', function () {
+        spyOn(comp.dialog, 'open')
+        fixture.detectChanges();
+
+        // the function to test
+        comp.addUser();
+
+        //assertions
+        expect(comp.dialog.open).toHaveBeenCalled();
+    });
+
+    it('should open a dialog to create a new group', function () {
+        spyOn(comp.dialog, 'open')
+        fixture.detectChanges();
+
+        // the function to test
+        comp.createNewGroup();
+
+        //assertions
+        expect(comp.dialog.open).toHaveBeenCalled();
+    });
+
+    it('should open a dialog to add selected users to groups', function () {
+        spyOn(comp.dialog, 'open')
+        fixture.detectChanges();
+
+        // the function to test
+        comp.addSelectedUsersToGroup();
+
+        //assertions
+        expect(comp.dialog.open).toHaveBeenCalled();
+    });
+
+    it('should destroy the component', ngCoreTesting.fakeAsync(function () {
+        fixture.detectChanges();
+        // wait for async getBucket call
+        ngCoreTesting.tick();
+        fixture.detectChanges();
+
+        // The function to test
+        comp.ngOnDestroy();
+
+        //assertions
+        expect(nfRegistryService.adminPerspective).toBe('');
+        expect(nfRegistryService.users.length).toBe(0);
+        expect(nfRegistryService.groups.length).toBe(0);
+        expect(nfRegistryService.filteredUsers.length).toBe(0);
+        expect(nfRegistryService.filteredUserGroups.length).toBe(0);
+        expect(nfRegistryService.allUsersAndGroupsSelected).toBe(false);
+    }));
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/nf-registry-create-bucket.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/nf-registry-create-bucket.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/nf-registry-create-bucket.spec.js
index 82bc02b..b07b235 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/nf-registry-create-bucket.spec.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/nf-registry-create-bucket.spec.js
@@ -15,43 +15,13 @@
  * limitations under the License.
  */
 
-var NfRegistryRoutes = require('nifi-registry/nf-registry.routes.js');
-var ngCoreTesting = require('@angular/core/testing');
-var ngCommon = require('@angular/common');
-var ngRouter = require('@angular/router');
-var ngPlatformBrowser = require('@angular/platform-browser');
-var FdsDemo = 
require('nifi-registry/components/fluid-design-system/fds-demo.js');
-var NfRegistry = require('nifi-registry/nf-registry.js');
 var NfRegistryApi = require('nifi-registry/services/nf-registry.api.js');
 var NfRegistryService = 
require('nifi-registry/services/nf-registry.service.js');
-var NfPageNotFoundComponent = 
require('nifi-registry/components/page-not-found/nf-registry-page-not-found.js');
-var NfRegistryExplorer = 
require('nifi-registry/components/explorer/nf-registry-explorer.js');
-var NfRegistryAdministration = 
require('nifi-registry/components/administration/nf-registry-administration.js');
-var NfRegistryUsersAdministration = 
require('nifi-registry/components/administration/users/nf-registry-users-administration.js');
-var NfRegistryAddUser = 
require('nifi-registry/components/administration/users/dialogs/add-user/nf-registry-add-user.js');
-var NfRegistryUserDetails = 
require('nifi-registry/components/administration/users/details/nf-registry-user-details.js');
-var NfRegistryUserPermissions = 
require('nifi-registry/components/administration/users/permissions/nf-registry-user-permissions.js');
-var NfRegistryUserGroupPermissions = 
require('nifi-registry/components/administration/user-group/permissions/nf-registry-user-group-permissions.js');
-var NfRegistryBucketPermissions = 
require('nifi-registry/components/administration/workflow/buckets/permissions/nf-registry-bucket-permissions.js');
-var NfRegistryWorkflowAdministration = 
require('nifi-registry/components/administration/workflow/nf-registry-workflow-administration.js');
 var NfRegistryCreateBucket = 
require('nifi-registry/components/administration/workflow/dialogs/nf-registry-create-bucket.js');
-var NfRegistryGridListViewer = 
require('nifi-registry/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js');
-var NfRegistryBucketGridListViewer = 
require('nifi-registry/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js');
-var NfRegistryDropletGridListViewer = 
require('nifi-registry/components/explorer/grid-list/registry/nf-registry-droplet-grid-list-viewer.js');
-var fdsCore = require('@fluid-design-system/core');
-var ngMoment = require('angular2-moment');
 var rxjs = require('rxjs/Rx');
-var ngHttp = require('@angular/http');
-var ngCommonHttp = require('@angular/common/http');
-var NfRegistryTokenInterceptor = 
require('nifi-registry/services/nf-registry.token.interceptor.js');
-var NfRegistryAuthService = 
require('nifi-registry/services/nf-registry.auth.service.js');
-var NfStorage = require('nifi-registry/services/nf-storage.service.js');
 
-describe('NfRegistryCreateBucket Component', function () {
+describe('NfRegistryCreateBucket Component isolated unit tests', function () {
     var comp;
-    var fixture;
-    var de;
-    var el;
     var nfRegistryService;
     var nfRegistryApi;
 
@@ -59,17 +29,18 @@ describe('NfRegistryCreateBucket Component', function () {
         nfRegistryService = new NfRegistryService();
         nfRegistryApi = new NfRegistryApi();
         comp = new NfRegistryCreateBucket(nfRegistryApi, nfRegistryService, {
-            close: function() {}
-        })
-    });
+            close: function () {
+            }
+        });
 
-    it('should create a new bucket and close the dialog', function () {
         // Spy
         spyOn(nfRegistryApi, 'createBucket').and.callFake(function () {
         }).and.returnValue(rxjs.Observable.of({name: 'NewBucket'}));
         spyOn(nfRegistryService, 'filterBuckets');
         spyOn(comp.dialogRef, 'close');
+    });
 
+    it('should create a new bucket and close the dialog', function () {
         // The function to test
         comp.createBucket({value: 'NewBucket'});
 
@@ -82,12 +53,6 @@ describe('NfRegistryCreateBucket Component', function () {
     });
 
     it('should create a new bucket and keep the dialog open', function () {
-        // Spy
-        spyOn(nfRegistryApi, 'createBucket').and.callFake(function () {
-        }).and.returnValue(rxjs.Observable.of({name: 'NewBucket'}));
-        spyOn(nfRegistryService, 'filterBuckets');
-        spyOn(comp.dialogRef, 'close');
-
         // setup the component
         comp.keepDialogOpen = true;
 
@@ -103,9 +68,6 @@ describe('NfRegistryCreateBucket Component', function () {
     });
 
     it('should close the dialog', function () {
-        // Spy
-        spyOn(comp.dialogRef, 'close');
-
         // The function to test
         comp.cancel();
 

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/nf-registry-workflow-administration.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/nf-registry-workflow-administration.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/nf-registry-workflow-administration.spec.js
index 2eac9e8..a8e48d0 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/nf-registry-workflow-administration.spec.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/nf-registry-workflow-administration.spec.js
@@ -17,6 +17,7 @@
 
 var NfRegistryRoutes = require('nifi-registry/nf-registry.routes.js');
 var ngCoreTesting = require('@angular/core/testing');
+var ngCommonHttpTesting = require('@angular/common/http/testing');
 var ngCommon = require('@angular/common');
 var ngRouter = require('@angular/router');
 var ngPlatformBrowser = require('@angular/platform-browser');
@@ -63,7 +64,8 @@ describe('NfRegistryWorkflowAdministration Component', 
function () {
                 ngHttp.JsonpModule,
                 ngCommonHttp.HttpClientModule,
                 fdsCore,
-                NfRegistryRoutes
+                NfRegistryRoutes,
+                ngCommonHttpTesting.HttpClientTestingModule
             ],
             declarations: [
                 FdsDemo,
@@ -122,27 +124,8 @@ describe('NfRegistryWorkflowAdministration Component', 
function () {
         // Spy
         spyOn(nfRegistryApi, 'ticketExchange').and.callFake(function () 
{}).and.returnValue(rxjs.Observable.of({}));
         spyOn(nfRegistryService, 'loadCurrentUser').and.callFake(function () 
{}).and.returnValue(rxjs.Observable.of({}));
-        spyOn(nfRegistryService.api, 'getDroplets').and.callFake(function () {
-        }).and.returnValue(rxjs.Observable.of([{
-            "identifier": "2e04b4fb-9513-47bb-aa74-1ae34616bfdc",
-            "name": "Flow #1",
-            "description": "This is flow #1",
-            "bucketIdentifier": "2f7f9e54-dc09-4ceb-aa58-9fe581319cdc",
-            "createdTimestamp": 1505931890999,
-            "modifiedTimestamp": 1505931890999,
-            "type": "FLOW",
-            "snapshotMetadata": null,
-            "link": {
-                "params": {
-                    "rel": "self"
-                },
-                "href": "flows/2e04b4fb-9513-47bb-aa74-1ae34616bfdc"
-            }
-        }]));
         spyOn(nfRegistryApi, 'getBuckets').and.callFake(function () {
         }).and.returnValue(rxjs.Observable.of([{name: 'Bucket #1'}]));
-        spyOn(nfRegistryApi, 'createBucket').and.callFake(function () {
-        }).and.returnValue(rxjs.Observable.of({name: 'Newly Created Bucket'}));
         spyOn(nfRegistryService, 'filterBuckets');
     });
 
@@ -169,7 +152,7 @@ describe('NfRegistryWorkflowAdministration Component', 
function () {
         comp.createBucket();
 
         //assertions
-        expect(comp.dialog.open).toBeDefined();
+        expect(comp.dialog.open).toHaveBeenCalled();
     });
 
     it('should destroy the component', ngCoreTesting.fakeAsync(function () {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js
 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js
index d106d41..d0e9077 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.js
@@ -47,7 +47,10 @@ NfRegistryBucketGridListViewer.prototype = {
     ngOnInit: function () {
         var self = this;
         this.nfRegistryService.explorerViewType = 'grid-list';
+
+        // reset the breadcrumb state
         this.nfRegistryService.droplet = {};
+
         // attempt kerberos authentication
         this.nfRegistryApi.ticketExchange().subscribe(function (jwt) {
             self.nfRegistryService.loadCurrentUser().subscribe(function 
(currentUser) {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.spec.js
index 62f583d..2d58a8b 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.spec.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-bucket-grid-list-viewer.spec.js
@@ -164,6 +164,7 @@ describe('NfRegistryBucketGridListViewer Component', 
function () {
         //assertions
         expect(comp).toBeDefined();
         expect(nfRegistryService.breadCrumbState).toBe('in');
+        expect(nfRegistryService.droplet.identity).toBeUndefined();
         expect(nfRegistryService.bucket.name).toEqual('Bucket #1');
         expect(nfRegistryService.buckets[0].name).toEqual('Bucket #1');
         expect(nfRegistryService.buckets.length).toBe(1);

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js
 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js
index a71f661..042747e 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.js
@@ -47,7 +47,11 @@ NfRegistryGridListViewer.prototype = {
     ngOnInit: function () {
         var self = this;
         this.nfRegistryService.explorerViewType = 'grid-list';
+
+        // reset the breadcrumb state
         this.nfRegistryService.bucket = {};
+        this.nfRegistryService.droplet = {};
+
         // attempt kerberos authentication
         this.nfRegistryApi.ticketExchange().subscribe(function (jwt) {
             self.nfRegistryService.loadCurrentUser().subscribe(function 
(currentUser) {

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.spec.js
----------------------------------------------------------------------
diff --git 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.spec.js
 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.spec.js
index f1d99fd..8c5cc38 100644
--- 
a/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.spec.js
+++ 
b/nifi-registry-web-ui/src/main/webapp/components/explorer/grid-list/registry/nf-registry-grid-list-viewer.spec.js
@@ -152,6 +152,8 @@ describe('NfRegistryGridListViewer Component', function () {
         expect(comp).toBeDefined();
         expect(nfRegistryService.explorerViewType).toBe('grid-list');
         expect(nfRegistryService.breadCrumbState).toBe('in');
+        expect(nfRegistryService.bucket.identity).toBeUndefined();
+        expect(nfRegistryService.droplet.identity).toBeUndefined();
         expect(nfRegistryService.buckets[0].name).toEqual('Bucket #1');
         expect(nfRegistryService.buckets.length).toBe(1);
         expect(nfRegistryService.droplets[0].name).toEqual('Flow #1');

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/nf-registry.animations.js
----------------------------------------------------------------------
diff --git a/nifi-registry-web-ui/src/main/webapp/nf-registry.animations.js 
b/nifi-registry-web-ui/src/main/webapp/nf-registry.animations.js
index c446012..e78f3a4 100644
--- a/nifi-registry-web-ui/src/main/webapp/nf-registry.animations.js
+++ b/nifi-registry-web-ui/src/main/webapp/nf-registry.animations.js
@@ -41,7 +41,7 @@ NfAnimations.prototype = {
             ngAnimate.style({
                 opacity: 0
             }),
-            ngAnimate.animate('0.3s ease-in')
+            ngAnimate.animate('0.5s ease-in')
         ]),
         ngAnimate.transition(':leave', [
             ngAnimate.animate('0.5s ease-out', ngAnimate.style({
@@ -65,7 +65,7 @@ NfAnimations.prototype = {
                 opacity: 0,
                 transform: 'translateX(-100%)'
             }),
-            ngAnimate.animate('0.3s ease-in')
+            ngAnimate.animate('0.5s ease-in')
         ]),
         ngAnimate.transition(':leave', [
             ngAnimate.animate('0.5s ease-out', ngAnimate.style({
@@ -90,7 +90,7 @@ NfAnimations.prototype = {
                 opacity: 0,
                 transform: 'translateY(-100%)'
             }),
-            ngAnimate.animate('0.3s ease-in')
+            ngAnimate.animate('0.5s ease-in')
         ]),
         ngAnimate.transition(':leave', [
             ngAnimate.animate('0.5s ease-out', ngAnimate.style({
@@ -111,7 +111,7 @@ NfAnimations.prototype = {
             ngAnimate.style({transform: 'translateX(100%)'}),
             ngAnimate.animate('0.5s 0.1s ease-in')
         ]),
-        ngAnimate.transition('* => void', ngAnimate.animate('0.3s ease-out', 
ngAnimate.style({transform: 'translateX(-100%)'})))
+        ngAnimate.transition('* => void', ngAnimate.animate('0.5s ease-out', 
ngAnimate.style({transform: 'translateX(-100%)'})))
     ])
 
 };

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/nf-registry.html
----------------------------------------------------------------------
diff --git a/nifi-registry-web-ui/src/main/webapp/nf-registry.html 
b/nifi-registry-web-ui/src/main/webapp/nf-registry.html
index 332a692..4e9c4c4 100644
--- a/nifi-registry-web-ui/src/main/webapp/nf-registry.html
+++ b/nifi-registry-web-ui/src/main/webapp/nf-registry.html
@@ -24,18 +24,18 @@ limitations under the License.
         <mat-toolbar id="nifi-registry-toolbar">
             <img id="nifi-registry-logo" 
src="nifi-registry/images/registry-logo-web-app.svg">
             <div fxFlex="1 1 auto" class="pad-left-xl" 
[@flyInOut]="nfRegistryService.breadCrumbState">
-                <span 
routerLink="/nifi-registry/explorer/{{(nfRegistryService.explorerViewType) ? 
nfRegistryService.explorerViewType : 
'grid-list'}}">{{nfRegistryService.registry.name}}</span>
+                <span class="pointer" 
routerLink="/nifi-registry/explorer/{{(nfRegistryService.explorerViewType) ? 
nfRegistryService.explorerViewType : 
'grid-list'}}">{{nfRegistryService.registry.name}}</span>
                 <mat-menu #availableRegistriesMenu="matMenu" 
[overlapTrigger]="false">
                     <button mat-menu-item *ngFor="let registry of 
nfRegistryService.registries"
                             
routerLink="/nifi-registry/explorer/{{(nfRegistryService.explorerViewType) ? 
nfRegistryService.explorerViewType : 'grid-list'}}">
                         <span>{{registry.name}}</span>
                     </button>
                 </mat-menu>
-                <span *ngIf="nfRegistryService.perspective === 
'administration'"> / Administration</span>
-                <span *ngIf="(nfRegistryService.perspective === 'explorer') && 
nfRegistryService.bucket.identifier"
+                <span class="pointer" *ngIf="nfRegistryService.perspective === 
'administration'"> / Administration</span>
+                <span class="pointer" *ngIf="(nfRegistryService.perspective 
=== 'explorer') && nfRegistryService.bucket.identifier"
                       [matMenuTriggerFor]="availableBucketsMenu"> / 
{{nfRegistryService.bucket.name}}<i
                         class="fa fa-caret-down pad-left-sm" 
aria-hidden="true"></i></span>
-                <span *ngIf="nfRegistryService.perspective === 'explorer' && 
!nfRegistryService.bucket.identifier"
+                <span class="pointer" *ngIf="nfRegistryService.perspective === 
'explorer' && !nfRegistryService.bucket.identifier"
                       [matMenuTriggerFor]="availableBucketsMenu"> / All<i 
class="fa fa-caret-down pad-left-sm"
                                                                          
aria-hidden="true"></i></span>
                 <mat-menu #availableBucketsMenu="matMenu" 
[overlapTrigger]="false">
@@ -48,10 +48,10 @@ limitations under the License.
                         <span>{{bucket.name}}</span>
                     </button>
                 </mat-menu>
-                <span *ngIf="nfRegistryService.perspective === 'explorer' && 
nfRegistryService.droplet.identifier"
+                <span class="pointer" *ngIf="nfRegistryService.perspective === 
'explorer' && nfRegistryService.droplet.identifier"
                       [matMenuTriggerFor]="availableDropletsMenu"> / 
{{nfRegistryService.droplet.name}}<i
                         class="fa fa-caret-down pad-left-sm" 
aria-hidden="true"></i></span>
-                <span [matMenuTriggerFor]="availableDropletsMenu"
+                <span class="pointer" 
[matMenuTriggerFor]="availableDropletsMenu"
                       *ngIf="nfRegistryService.perspective === 'explorer' && 
nfRegistryService.bucket.identifier && !nfRegistryService.droplet.identifier"> 
/ All<i
                         class="fa fa-caret-down pad-left-sm" 
aria-hidden="true"></i></span>
                 <mat-menu #availableDropletsMenu="matMenu" 
[overlapTrigger]="false">

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/nf-registry.js
----------------------------------------------------------------------
diff --git a/nifi-registry-web-ui/src/main/webapp/nf-registry.js 
b/nifi-registry-web-ui/src/main/webapp/nf-registry.js
index 91b1909..df344f7 100644
--- a/nifi-registry-web-ui/src/main/webapp/nf-registry.js
+++ b/nifi-registry-web-ui/src/main/webapp/nf-registry.js
@@ -53,35 +53,10 @@ NfRegistry.prototype = {
     ngOnInit: function () {
         var self = this;
         this.nfRegistryService.sidenav = this.sidenav; //ngCore.ViewChild
-        //
-        // // attempt kerberos authentication
-        // return this.nfRegistryApi.ticketExchange().subscribe(function (jwt) 
{
-        //     // get the payload and store the token with the appropriate 
expiration
-        //     var token = self.nfStorage.getJwtPayload(jwt);
-        //     if(token) {
-        //         var expiration = parseInt(token['exp'], 10) * 
MILLIS_PER_SECOND;
-        //         self.nfStorage.setItem('jwt', jwt, expiration);
-        //     }
-        //     self.nfRegistryApi.loadCurrentUser().subscribe(function 
(currentUser) {
-        //         // if the user is logged, we want to determine if they were 
logged in using a certificate
-        //         if (currentUser.status !== "UNKNOWN") {
-        //             // render the users name
-        //             self.nfRegistryService.currentUser = currentUser;
-        //
-        //             // render the logout button if there is a token locally
-        //             if (self.nfStorage.getItem('jwt') !== null) {
-        //                 self.nfRegistryService.currentUser.canLogout = true;
-        //             }
-        //         } else {
-        //             // set the anonymous user label
-        //             self.nfRegistryService.currentUser.identity = 
'Anonymous';
-        //         }
-        //     });
-        // });
     },
 
     /**
-     * since the child views are updating the nfRegistryService values that 
are used to display
+     * Since the child views are updating the nfRegistryService values that 
are used to display
      * the breadcrumbs in this component's view we need to manually detect 
changes at the correct
      * point in the lifecycle.
      */

http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/2f343e1b/nifi-registry-web-ui/src/main/webapp/nf-registry.spec.js
----------------------------------------------------------------------
diff --git a/nifi-registry-web-ui/src/main/webapp/nf-registry.spec.js 
b/nifi-registry-web-ui/src/main/webapp/nf-registry.spec.js
index ca8097a..98ba0d4 100644
--- a/nifi-registry-web-ui/src/main/webapp/nf-registry.spec.js
+++ b/nifi-registry-web-ui/src/main/webapp/nf-registry.spec.js
@@ -46,6 +46,7 @@ var NfStorage = 
require('nifi-registry/services/nf-storage.service.js');
 describe('NfRegistry Component', function () {
     var comp;
     var fixture;
+    var nfRegistryService;
 
     beforeEach(function () {
         ngCoreTesting.TestBed.configureTestingModule({
@@ -91,13 +92,15 @@ describe('NfRegistry Component', function () {
             ],
             bootstrap: [NfRegistry]
         });
-    });
-
-    it('should create component', function () {
         fixture = ngCoreTesting.TestBed.createComponent(NfRegistry);
         fixture.detectChanges();
         comp = fixture.componentInstance;
 
+        // NfRegistryService from the root injector
+        nfRegistryService = ngCoreTesting.TestBed.get(NfRegistryService);
+    });
+
+    it('should create component', function () {
         //assertions
         expect(comp).toBeDefined();
     });

Reply via email to