diff --git a/awx/ui/client/lib/components/layout/layout.directive.js b/awx/ui/client/lib/components/layout/layout.directive.js
index 0f40ce7868..0af7746d57 100644
--- a/awx/ui/client/lib/components/layout/layout.directive.js
+++ b/awx/ui/client/lib/components/layout/layout.directive.js
@@ -18,7 +18,7 @@ function AtLayoutController ($scope, strings) {
$scope.$watch('$root.socketStatus', (newStatus) => {
vm.socketState = newStatus;
- vm.socketIconClass = `icon-socket-${$scope.socketStatus}`;
+ vm.socketIconClass = `icon-socket-${vm.socketState}`;
});
$scope.$watch('$root.licenseMissing', (licenseMissing) => {
diff --git a/awx/ui/client/test/unit/index.js b/awx/ui/client/test/unit/index.js
index aa4f73ab95..d26d4c6870 100644
--- a/awx/ui/client/test/unit/index.js
+++ b/awx/ui/client/test/unit/index.js
@@ -1,11 +1,7 @@
// Import angular and angular-mocks to the global scope
-import 'angular';
import 'angular-mocks';
-// Import custom Angular module dependencies
-import '../../src/i18n';
-import '../../lib/services';
-import '../../lib/components';
-
// Import tests
-import './panel-body.spec';
+import './layout.spec';
+import './side-nav.spec';
+import './side-nav-item.spec';
\ No newline at end of file
diff --git a/awx/ui/client/test/unit/karma.conf.js b/awx/ui/client/test/unit/karma.conf.js
index e3343b0a00..b0b33eb6cd 100644
--- a/awx/ui/client/test/unit/karma.conf.js
+++ b/awx/ui/client/test/unit/karma.conf.js
@@ -1,4 +1,3 @@
-let path = require('path');
const webpackConfig = require('../../../build/webpack.test.js');
module.exports = config => {
@@ -11,29 +10,26 @@ module.exports = config => {
browsers: ['PhantomJS'],
reporters: ['progress'],
files: [
- './index.js',
- '../../lib/components/**/*.html'
+ '../../../client/src/vendor.js',
+ '../../../client/src/app.js',
+ '../../../client/src/**/*.html',
+ './index.js',
],
plugins: [
'karma-webpack',
'karma-jasmine',
'karma-phantomjs-launcher',
- 'karma-ng-html2js-preprocessor'
+ 'karma-html2js-preprocessor'
],
preprocessors: {
- '../../lib/components/**/*.html': 'ng-html2js',
+ '../../../client/src/vendor.js': 'webpack',
+ '../../../client/src/app.js': 'webpack',
+ '../../../client/src/**/*.html': 'html2js',
'./index.js': 'webpack'
},
- ngHtml2JsPreprocessor: {
- moduleName: 'at.test.templates',
- cacheIdFromPath: function (filepath) {
- filepath = filepath.replace(path.join(__dirname, '../../lib'), '');
- return '/static/partials' + filepath;
- }
- },
webpack: webpackConfig,
webpackMiddleware: {
noInfo: 'errors-only'
}
});
-};
+};
\ No newline at end of file
diff --git a/awx/ui/client/test/unit/layout.spec.js b/awx/ui/client/test/unit/layout.spec.js
new file mode 100644
index 0000000000..512cfbef50
--- /dev/null
+++ b/awx/ui/client/test/unit/layout.spec.js
@@ -0,0 +1,162 @@
+describe('Components | Layout', () => {
+ let $compile;
+ let $rootScope;
+ let element;
+ let scope;
+ let i18n;
+
+ beforeEach(() => {
+ angular.mock.module('gettext');
+ angular.mock.module('I18N');
+ angular.mock.module('ui.router');
+ angular.mock.module('at.lib.services');
+ angular.mock.module('at.lib.components');
+ });
+
+ beforeEach(angular.mock.inject((_$compile_, _$rootScope_) => {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ scope = $rootScope.$new();
+
+ element = angular.element('');
+ element = $compile(element)(scope);
+ scope.$digest();
+ }));
+
+ describe('AtLayoutController', () => {
+ let controller;
+
+ beforeEach(()=> {
+ controller = element.controller('atLayout');
+ });
+
+ it('$scope.$on($stateChangeSuccess) should assign toState name to currentState', () => {
+ let next = {'name': 'dashboard'};
+ $rootScope.$broadcast('$stateChangeSuccess', next);
+ expect(controller.currentState).toBe('dashboard');
+ });
+
+ describe('$root.current_user watcher should assign value to ', () => {
+ beforeEach(() => {
+ let val = {
+ username: 'admin',
+ id: 1
+ };
+ $rootScope.current_user = val;
+ scope.$digest();
+ });
+
+ it('isLoggedIn', () => {
+ expect(controller.isLoggedIn).toBe('admin');
+
+ $rootScope.current_user = { id: 1 };
+ scope.$digest();
+ expect(controller.isLoggedIn).not.toBeDefined();
+ });
+
+ it('isSuperUser', () => {
+ $rootScope.current_user = 'one';
+ $rootScope.user_is_superuser = true;
+ $rootScope.user_is_system_auditor = false;
+ scope.$digest();
+ expect(controller.isSuperUser).toBe(true);
+
+ $rootScope.current_user = 'two';
+ $rootScope.user_is_superuser = false;
+ $rootScope.user_is_system_auditor = true;
+ scope.$digest();
+ expect(controller.isSuperUser).toBe(true);
+
+ $rootScope.current_user = 'three';
+ $rootScope.user_is_superuser = true;
+ $rootScope.user_is_system_auditor = true;
+ scope.$digest();
+ expect(controller.isSuperUser).toBe(true);
+
+ $rootScope.current_user = 'four';
+ $rootScope.user_is_superuser = false;
+ $rootScope.user_is_system_auditor = false;
+ scope.$digest();
+ expect(controller.isSuperUser).toBe(false);
+ });
+
+ it('currentUsername', () => {
+ expect(controller.currentUsername).toBeTruthy();
+ expect(controller.currentUsername).toBe('admin');
+ });
+
+ it('currentUserId', () => {
+ expect(controller.currentUserId).toBeTruthy();
+ expect(controller.currentUserId).toBe(1);
+ });
+
+ });
+
+ describe('$root.socketStatus watcher should assign newStatus to', () => {
+ let statuses = ['connecting', 'error', 'ok'];
+
+ it('socketState', () => {
+ _.forEach(statuses, (status) => {
+ $rootScope.socketStatus = status;
+ scope.$digest();
+ expect(controller.socketState).toBeTruthy();
+ expect(controller.socketState).toBe(status);
+ });
+ });
+
+ it('socketIconClass', () => {
+ _.forEach(statuses, (status) => {
+ $rootScope.socketStatus = status;
+ scope.$digest();
+ expect(controller.socketIconClass).toBe(`icon-socket-${status}`);
+ });
+ });
+ });
+
+ describe('$root.licenseMissing watcher should assign true or false to', () => {
+ it('licenseIsMissing', () => {
+ $rootScope.licenseMissing = true;
+ scope.$digest();
+ expect(controller.licenseIsMissing).toBe(true);
+
+ $rootScope.licenseMissing = false;
+ scope.$digest();
+ expect(controller.licenseIsMissing).toBe(false);
+ });
+ });
+
+ describe('getString()', () => {
+ it('calls ComponentsStrings get() method', angular.mock.inject((_ComponentsStrings_) => {
+ spyOn(_ComponentsStrings_, 'get');
+ controller.getString('VIEW_DOCS')
+ expect(_ComponentsStrings_.get).toHaveBeenCalled();
+ }));
+
+ it('ComponentsStrings get() method should throw an error if string is not a property name of the layout class', () => {
+ expect(controller.getString.bind(null, 'SUBMISSION_ERROR_TITLE')).toThrow();
+ });
+
+ it('should return layout string', () => {
+ let layoutStrings = {
+ CURRENT_USER_LABEL: 'Logged in as',
+ VIEW_DOCS: 'View Documentation',
+ LOGOUT: 'Logout',
+ };
+
+ _.forEach(layoutStrings, (value, key) => {
+ expect(controller.getString(key)).toBe(value);
+ });
+ });
+
+ it('should return default string', () => {
+ let defaultStrings = {
+ BRAND_NAME: "AWX"
+ };
+
+ _.forEach(defaultStrings, (value, key) => {
+ expect(controller.getString(key)).toBe(value);
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/awx/ui/client/test/unit/panel-body.spec.js b/awx/ui/client/test/unit/panel-body.spec.js
deleted file mode 100644
index 3915195847..0000000000
--- a/awx/ui/client/test/unit/panel-body.spec.js
+++ /dev/null
@@ -1,24 +0,0 @@
-describe('Components | panel/body', () => {
-
- let $compile;
- let $rootScope;
-
- beforeEach(() => {
- angular.mock.module('at.lib.services')
- angular.mock.module('at.lib.components')
- angular.mock.module('at.test.templates');
- });
-
- beforeEach(angular.mock.inject((_$compile_, _$rootScope_) => {
- $compile = _$compile_;
- $rootScope = _$rootScope_;
- }));
-
- it('Should produce at-Panel-body HTML content', () => {
- let element = $compile('yo')($rootScope);
- $rootScope.$digest();
-
- expect(element.hasClass('at-Panel-body')).toBe(true);
- expect(element.html()).toContain('yo');
- });
-});
diff --git a/awx/ui/client/test/unit/side-nav-item.spec.js b/awx/ui/client/test/unit/side-nav-item.spec.js
new file mode 100644
index 0000000000..f605a40e69
--- /dev/null
+++ b/awx/ui/client/test/unit/side-nav-item.spec.js
@@ -0,0 +1,61 @@
+describe('Components | Side Nav Item', () => {
+ let $compile;
+ let $rootScope;
+ let element;
+ let scope;
+
+ beforeEach(() => {
+ angular.mock.module('gettext');
+ angular.mock.module('I18N');
+ angular.mock.module('ui.router');
+ angular.mock.module('at.lib.services')
+ angular.mock.module('at.lib.components')
+ });
+
+ beforeEach(angular.mock.inject((_$compile_, _$rootScope_) => {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ scope = $rootScope.$new();
+
+ element = angular.element('');
+ element = $compile(element)(scope);
+ scope.name = 'dashboard';
+ scope.$digest();
+ }));
+
+ describe('Side Nav Item Controller', () => {
+ let LayoutCtrl;
+ let SideNavItem;
+ let SideNavItemCtrl;
+
+ beforeEach(() => {
+ SideNavItem = angular.element(element[0].querySelector('at-side-nav-item'));
+ SideNavItemCtrl = SideNavItem.controller('atSideNavItem');
+ });
+
+ it('layoutVm.currentState watcher should assign isRoute', () => {
+ let current = {'name': 'dashboard'};
+ $rootScope.$broadcast('$stateChangeSuccess', current);
+ scope.$digest();
+ expect(SideNavItemCtrl.isRoute).toBe(true);
+
+ current = {'name': 'inventories'};
+ $rootScope.$broadcast('$stateChangeSuccess', current);
+ scope.$digest();
+ expect(SideNavItemCtrl.isRoute).toBe(false);
+ });
+
+ it('go() should call $state.go()', angular.mock.inject((_$state_) => {
+ spyOn(_$state_, 'go');
+ SideNavItemCtrl.go();
+ expect(_$state_.go).toHaveBeenCalled();
+ expect(_$state_.go).toHaveBeenCalledWith('dashboard', jasmine.any(Object), jasmine.any(Object));
+ }));
+
+ it('should load name, icon, and route from scope', () => {
+ expect(SideNavItem.isolateScope().name).toBeDefined();
+ expect(SideNavItem.isolateScope().iconClass).toBeDefined();
+ expect(SideNavItem.isolateScope().route).toBeDefined();
+ });
+ });
+});
diff --git a/awx/ui/client/test/unit/side-nav.spec.js b/awx/ui/client/test/unit/side-nav.spec.js
new file mode 100644
index 0000000000..1342e41659
--- /dev/null
+++ b/awx/ui/client/test/unit/side-nav.spec.js
@@ -0,0 +1,78 @@
+describe('Components | Side Nav', () => {
+ let $compile;
+ let $rootScope;
+ let element;
+ let scope;
+ let windowMock = {
+ innerWidth: 500
+ };
+
+ beforeEach(() => {
+ angular.mock.module('gettext');
+ angular.mock.module('I18N');
+ angular.mock.module('ui.router');
+ angular.mock.module('at.lib.services')
+ angular.mock.module('at.lib.components', ($provide) => {
+ $provide.value('$window', windowMock);
+ });
+ });
+
+ beforeEach(angular.mock.inject((_$compile_, _$rootScope_) => {
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ scope = $rootScope.$new();
+
+ element = angular.element("");
+ element = $compile(element)(scope);
+ scope.$digest();
+ }));
+
+ describe('Side Nav Controller', () => {
+ let sideNav;
+ let sideNavCtrl;
+
+ beforeEach(() => {
+ sideNav = angular.element(element[0].querySelector('.at-Layout-side'));
+ sideNavCtrl = sideNav.controller('atSideNav');
+ });
+
+ it('isExpanded defaults to false', () => {
+ expect(sideNavCtrl.isExpanded).toBe(false);
+ });
+
+ it('toggleExpansion()', () => {
+ expect(sideNavCtrl.isExpanded).toBe(false);
+
+ sideNavCtrl.toggleExpansion();
+ expect(sideNavCtrl.isExpanded).toBe(true);
+
+ sideNavCtrl.toggleExpansion();
+ expect(sideNavCtrl.isExpanded).toBe(false);
+
+ sideNavCtrl.toggleExpansion();
+ expect(sideNavCtrl.isExpanded).toBe(true);
+
+ sideNavCtrl.toggleExpansion();
+ expect(sideNavCtrl.isExpanded).toBe(false);
+ });
+
+ it('isExpanded should be false after state change event', () => {
+ sideNavCtrl.isExpanded = true;
+
+ let current = {
+ 'name': 'dashboard'
+ };
+ $rootScope.$broadcast('$stateChangeSuccess', current);
+ scope.$digest();
+ expect(sideNavCtrl.isExpanded).toBe(false);
+ });
+
+ it('clickOutsideSideNav watcher should assign isExpanded to false', () => {
+ sideNavCtrl.isExpanded = true;
+
+ $rootScope.$broadcast('clickOutsideSideNav');
+ scope.$digest();
+ expect(sideNavCtrl.isExpanded).toBe(false);
+ });
+ });
+});
diff --git a/awx/ui/package.json b/awx/ui/package.json
index 3cb89bcec5..e6893ae15c 100644
--- a/awx/ui/package.json
+++ b/awx/ui/package.json
@@ -25,7 +25,7 @@
"jshint": "grunt jshint:source --no-color",
"test:ci": "npm run test -- --single-run --reporter junit,dots --browsers=PhantomJS",
"e2e": "./client/test/e2e/runner.js --config ./client/test/e2e/nightwatch.conf.js",
- "component-test": "karma start client/test/karma.conf.js",
+ "component-test": "karma start client/test/unit/karma.conf.js",
"lint": "eslint -c .eslintrc.js .",
"dev": "webpack --config build/webpack.development.js --progress",
"watch": "webpack-dev-server --config build/webpack.watch.js --progress",
@@ -70,7 +70,6 @@
"karma-html2js-preprocessor": "^1.0.0",
"karma-jasmine": "^1.1.0",
"karma-junit-reporter": "^1.2.0",
- "karma-ng-html2js-preprocessor": "^1.0.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.4",