Update eslint and update angular style to match

With an updated eslint version and the addition of eslint-config-openstack
and eslint-plugin-angular, there are several more stylistic guidelines to
follow. However, this is what other OpenStack angular projects follow such
as Horizon. Some notable changes are:

* Wrapped javascript content in anonymous functions. This is a safeguard to
  keep the code from conflicting with other variables with the same name in
  other scripts on the same page.

* Explicitly inject dependencies and have controllers, factories, etc as
  explicitly declared functions.

* Use angular "controller as" syntax instead of assigning variables to $scope.

* Added eslint rule that requires JSDoc for every function declaration.

Note these are mainly stylistic changes and all the functionality of RefStack
should remain the same.

Change-Id: I044b1f473d589681a2ae9d2704700dd85687cbb6
This commit is contained in:
Paul Van Eck 2015-09-30 12:31:32 -07:00
parent 8f730eb46f
commit d23adad7f6
24 changed files with 1666 additions and 1337 deletions

View File

@ -32,6 +32,8 @@
"shelljs": false "shelljs": false
}, },
"extends": "openstack",
"globals": { "globals": {
"require": false, "require": false,
"exports": false, "exports": false,
@ -43,16 +45,25 @@
"browser": false "browser": false
}, },
"plugins": [
"angular"
],
"rules": { "rules": {
"quotes": [2, "single"], "quotes": [2, "single"],
"eol-last": 2, "eol-last": 2,
"no-trailing-spaces": 2, "no-trailing-spaces": 2,
"camelcase": 0, "camelcase": 0,
"no-extra-boolean-cast": 0, "no-extra-boolean-cast": 0,
"operator-linebreak": 0,
"require-jsdoc": 2,
// Stylistic // Stylistic
"indent": [2, 4], "indent": [2, 4, {SwitchCase: 1}],
"max-len": [2, 80], "max-len": [2, 80],
"no-undefined": 2 "no-undefined": 2,
// Angular Plugin
"angular/controller-as-vm": [1, "ctrl"]
} }
} }

View File

@ -2,11 +2,13 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"name": "refstack-ui", "name": "refstack-ui",
"description": "A user interface for Refstack", "description": "A user interface for RefStack",
"license": "Apache2", "license": "Apache2",
"devDependencies": { "devDependencies": {
"bower": "1.3.12", "bower": "1.3.12",
"eslint": "^0.21.2", "eslint": "1.5.1",
"eslint-config-openstack": "1.2.1",
"eslint-plugin-angular": "0.12.0",
"http-server": "^0.6.1", "http-server": "^0.6.1",
"karma": "^0.12.23", "karma": "^0.12.23",
"karma-chrome-launcher": "^0.1.5", "karma-chrome-launcher": "^0.1.5",
@ -15,9 +17,7 @@
"karma-jasmine": "^0.2.2", "karma-jasmine": "^0.2.2",
"karma-phantomjs-launcher": "0.2.0", "karma-phantomjs-launcher": "0.2.0",
"phantomjs": "1.9.17", "phantomjs": "1.9.17",
"protractor": "~1.0.0", "protractor": "~1.0.0"
"shelljs": "^0.2.6",
"tmp": "0.0.23"
}, },
"scripts": { "scripts": {
"postinstall": "bower install --config.interactive=false", "postinstall": "bower install --config.interactive=false",

View File

@ -1,16 +1,38 @@
/** Main app module where application dependencies are listed. */ /*
var refstackApp = angular.module('refstackApp', [ * Licensed under the Apache License, Version 2.0 (the "License");
'ui.router', 'ui.bootstrap', 'cgBusy', 'ngResource', 'angular-confirm']); * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
/** *
* Handle application routing. Specific templates and controllers will be * http://www.apache.org/licenses/LICENSE-2.0
* used based on the URL route. *
* 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.
*/ */
refstackApp.config([
'$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
'use strict';
(function () {
'use strict';
/** Main app module where application dependencies are listed. */
angular
.module('refstackApp', [
'ui.router','ui.bootstrap', 'cgBusy',
'ngResource', 'angular-confirm'
]);
angular
.module('refstackApp')
.config(configureRoutes);
configureRoutes.$inject = ['$stateProvider', '$urlRouterProvider'];
/**
* Handle application routing. Specific templates and controllers will be
* used based on the URL route.
*/
function configureRoutes($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/'); $urlRouterProvider.otherwise('/');
$stateProvider. $stateProvider.
state('home', { state('home', {
@ -24,70 +46,80 @@ refstackApp.config([
state('capabilities', { state('capabilities', {
url: '/capabilities', url: '/capabilities',
templateUrl: '/components/capabilities/capabilities.html', templateUrl: '/components/capabilities/capabilities.html',
controller: 'capabilitiesController' controller: 'CapabilitiesController as ctrl'
}). }).
state('communityResults', { state('communityResults', {
url: '/community_results', url: '/community_results',
templateUrl: '/components/results/results.html', templateUrl: '/components/results/results.html',
controller: 'resultsController' controller: 'ResultsController as ctrl'
}). }).
state('userResults', { state('userResults', {
url: '/user_results', url: '/user_results',
templateUrl: '/components/results/results.html', templateUrl: '/components/results/results.html',
controller: 'resultsController' controller: 'ResultsController as ctrl'
}). }).
state('resultsDetail', { state('resultsDetail', {
url: '/results/:testID', url: '/results/:testID',
templateUrl: '/components/results-report/resultsReport.html', templateUrl: '/components/results-report' +
controller: 'resultsReportController' '/resultsReport.html',
controller: 'ResultsReportController as ctrl'
}). }).
state('profile', { state('profile', {
url: '/profile', url: '/profile',
templateUrl: '/components/profile/profile.html', templateUrl: '/components/profile/profile.html',
controller: 'profileController' controller: 'ProfileController as ctrl'
}). }).
state('authFailure', { state('authFailure', {
url: '/auth_failure/:message', url: '/auth_failure/:message',
templateUrl: '/components/home/home.html', templateUrl: '/components/home/home.html',
controller: 'authFailureController' controller: 'AuthFailureController as ctrl'
}); });
} }
]);
/** angular
* Injections in $rootscope .module('refstackApp')
*/ .run(setup);
refstackApp.run(['$http', '$rootScope', '$window', '$state', 'refstackApiUrl', setup.$inject = [
function($http, $rootScope, $window, $state, refstackApiUrl) { '$http', '$rootScope', '$window', '$state', 'refstackApiUrl'
'use strict'; ];
/**
* Set up the app with injections into $rootscope. This is mainly for auth
* functions.
*/
function setup($http, $rootScope, $window, $state, refstackApiUrl) {
/** /**
* This function injects sign in function in all scopes * This function injects sign in function in all scopes
*/ */
$rootScope.auth = {}; $rootScope.auth = {};
$rootScope.auth.doSignIn = doSignIn;
$rootScope.auth.doSignOut = doSignOut;
$rootScope.auth.doSignCheck = doSignCheck;
var sign_in_url = refstackApiUrl + '/auth/signin'; var sign_in_url = refstackApiUrl + '/auth/signin';
$rootScope.auth.doSignIn = function () {
$window.location.href = sign_in_url;
};
/**
* This function injects sign out function in all scopes
*/
var sign_out_url = refstackApiUrl + '/auth/signout'; var sign_out_url = refstackApiUrl + '/auth/signout';
$rootScope.auth.doSignOut = function () { var profile_url = refstackApiUrl + '/profile';
/** This function initiates a sign in. */
function doSignIn() {
$window.location.href = sign_in_url;
}
/** This function will initate a sign out. */
function doSignOut() {
$rootScope.currentUser = null; $rootScope.currentUser = null;
$rootScope.isAuthenticated = false; $rootScope.isAuthenticated = false;
$window.location.href = sign_out_url; $window.location.href = sign_out_url;
}; }
/** /**
* This block tries to authenticate user * This function checks to see if a user is logged in and
* authenticated.
*/ */
var profile_url = refstackApiUrl + '/profile'; function doSignCheck() {
$rootScope.auth.doSignCheck = function () {
return $http.get(profile_url, {withCredentials: true}). return $http.get(profile_url, {withCredentials: true}).
success(function (data) { success(function (data) {
$rootScope.auth.currentUser = data; $rootScope.auth.currentUser = data;
@ -97,30 +129,38 @@ refstackApp.run(['$http', '$rootScope', '$window', '$state', 'refstackApiUrl',
$rootScope.auth.currentUser = null; $rootScope.auth.currentUser = null;
$rootScope.auth.isAuthenticated = false; $rootScope.auth.isAuthenticated = false;
}); });
}; }
$rootScope.auth.doSignCheck(); $rootScope.auth.doSignCheck();
} }
]);
/** angular
* Load config and start up the angular application. .element(document)
*/ .ready(loadConfig);
angular.element(document).ready(function () {
'use strict';
var $http = angular.injector(['ng']).get('$http'); /**
* Load config and start up the angular application.
*/
function loadConfig() {
function startApp(config) { var $http = angular.injector(['ng']).get('$http');
// Add config options as constants.
for (var key in config) { /**
angular.module('refstackApp').constant(key, config[key]); * Store config variables as constants, and start the app.
*/
function startApp(config) {
// Add config options as constants.
angular.forEach(config, function(value, key) {
angular.module('refstackApp').constant(key, value);
});
angular.bootstrap(document, ['refstackApp']);
} }
angular.bootstrap(document, ['refstackApp']);
}
$http.get('config.json').success(function (data) { $http.get('config.json').success(function (data) {
startApp(data); startApp(data);
}).error(function () { }).error(function () {
startApp({}); startApp({});
}); });
}); }
})();

View File

@ -1,17 +1,31 @@
/** /*
* Refstack Auth Failure Controller * Licensed under the Apache License, Version 2.0 (the "License");
* This controller handles messages from Refstack API if user auth fails. * 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 refstackApp = angular.module('refstackApp'); (function () {
'use strict';
refstackApp.controller('authFailureController', angular
[ .module('refstackApp')
'$stateParams', '$state', 'raiseAlert', .controller('AuthFailureController', AuthFailureController);
function($stateParams, $state, raiseAlert) {
'use strict'; AuthFailureController.$inject = ['$stateParams', '$state', 'raiseAlert'];
raiseAlert('danger', 'Authentication Failure:', /**
$stateParams.message); * Refstack Auth Failure Controller
$state.go('home'); * This controller handles messages from Refstack API if user auth fails.
} */
]); function AuthFailureController($stateParams, $state, raiseAlert) {
raiseAlert('danger', 'Authentication Failure:', $stateParams.message);
$state.go('home');
}
})();

View File

@ -4,15 +4,15 @@
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<strong>Version:</strong> <strong>Version:</strong>
<select ng-model="version" ng-change="update()" class="form-control"> <select ng-model="ctrl.version" ng-change="ctrl.update()" class="form-control">
<!-- Slicing the version file name here gets rid of the '.json' file extension. --> <!-- Slicing the version file name here gets rid of the '.json' file extension. -->
<option ng-repeat="versionFile in versionList" value="{{versionFile}}">{{versionFile.slice(0, -5)}}</option> <option ng-repeat="versionFile in ctrl.versionList" value="{{versionFile}}">{{versionFile.slice(0, -5)}}</option>
</select> </select>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<strong>Target Program:</strong> <strong>Target Program:</strong>
<span class="program-about"><a target="_blank" href="http://www.openstack.org/brand/interop/">About</a></span> <span class="program-about"><a target="_blank" href="http://www.openstack.org/brand/interop/">About</a></span>
<select ng-model="target" class="form-control" ng-change="updateTargetCapabilities()"> <select ng-model="ctrl.target" class="form-control" ng-change="ctrl.updateTargetCapabilities()">
<option value="platform">OpenStack Powered Platform</option> <option value="platform">OpenStack Powered Platform</option>
<option value="compute">OpenStack Powered Compute</option> <option value="compute">OpenStack Powered Compute</option>
<option value="object">OpenStack Powered Object Storage</option> <option value="object">OpenStack Powered Object Storage</option>
@ -21,10 +21,10 @@
</div> </div>
<br /> <br />
<div ng-show="capabilities"> <div ng-show="ctrl.capabilities">
<strong>Corresponding OpenStack Releases:</strong> <strong>Corresponding OpenStack Releases:</strong>
<ul class="list-inline"> <ul class="list-inline">
<li ng-repeat="release in capabilities.releases"> <li ng-repeat="release in ctrl.capabilities.releases">
{{release | capitalize}} {{release | capitalize}}
</li> </li>
</ul> </ul>
@ -33,19 +33,19 @@
<strong>Capability Status:</strong> <strong>Capability Status:</strong>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" ng-model="status.required"> <input type="checkbox" ng-model="ctrl.status.required">
<span class="required">Required</span> <span class="required">Required</span>
</label> </label>
<label> <label>
<input type="checkbox" ng-model="status.advisory"> <input type="checkbox" ng-model="ctrl.status.advisory">
<span class="advisory">Advisory</span> <span class="advisory">Advisory</span>
</label> </label>
<label> <label>
<input type="checkbox" ng-model="status.deprecated"> <input type="checkbox" ng-model="ctrl.status.deprecated">
<span class="deprecated">Deprecated</span> <span class="deprecated">Deprecated</span>
</label> </label>
<label> <label>
<input type="checkbox" ng-model="status.removed"> <input type="checkbox" ng-model="ctrl.status.removed">
<span class="removed">Removed</span> <span class="removed">Removed</span>
</label> </label>
</div> </div>
@ -54,14 +54,14 @@
<p><small>Tests marked with <span class="glyphicon glyphicon-flag text-warning"></span> are tests flagged by DefCore.</small></p> <p><small>Tests marked with <span class="glyphicon glyphicon-flag text-warning"></span> are tests flagged by DefCore.</small></p>
<!-- Loading animation divs --> <!-- Loading animation divs -->
<div cg-busy="{promise:versionsRequest,message:'Loading versions'}"></div> <div cg-busy="{promise:ctrl.versionsRequest,message:'Loading versions'}"></div>
<div cg-busy="{promise:capsRequest,message:'Loading capabilities'}"></div> <div cg-busy="{promise:ctrl.capsRequest,message:'Loading capabilities'}"></div>
<!-- Get the version-specific template --> <!-- Get the version-specific template -->
<div ng-include src="detailsTemplate"></div> <div ng-include src="ctrl.detailsTemplate"></div>
<div ng-show="showError" class="alert alert-danger" role="alert"> <div ng-show="showError" class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span> <span class="sr-only">Error:</span>
{{error}} {{ctrl.error}}
</div> </div>

View File

@ -1,172 +1,191 @@
var refstackApp = angular.module('refstackApp'); /*
* Licensed under the Apache License, Version 2.0 (the "License");
/** * you may not use this file except in compliance with the License.
* Refstack Capabilities Controller * You may obtain a copy of the License at
* This controller is for the '/capabilities' page where a user can browse *
* through tests belonging to DefCore-defined capabilities. * 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.
*/ */
refstackApp.controller('capabilitiesController',
['$scope', '$http', 'refstackApiUrl',
function ($scope, $http, refstackApiUrl) {
'use strict';
/** Whether to hide/collapse the achievements for each capability. */ (function () {
$scope.hideAchievements = true; 'use strict';
/** Whether to hide/collapse the tests for each capability. */ angular
$scope.hideTests = true; .module('refstackApp')
.controller('CapabilitiesController', CapabilitiesController);
/** The target OpenStack marketing program to show capabilities for. */ CapabilitiesController.$inject = ['$http', 'refstackApiUrl'];
$scope.target = 'platform';
/** The various possible capability statuses. */ /**
$scope.status = { * RefStack Capabilities Controller
required: true, * This controller is for the '/capabilities' page where a user can browse
advisory: false, * through tests belonging to DefCore-defined capabilities.
deprecated: false, */
removed: false function CapabilitiesController($http, refstackApiUrl) {
}; var ctrl = this;
/** ctrl.getVersionList = getVersionList;
* The template to load for displaying capability details. The value ctrl.update = update;
* of this depends on the schema version of the capabilities file. ctrl.updateTargetCapabilities = updateTargetCapabilities;
*/ ctrl.filterStatus = filterStatus;
$scope.detailsTemplate = null; ctrl.getObjectLength = getObjectLength;
/** /** The target OpenStack marketing program to show capabilities for. */
* Retrieve an array of available capability files from the Refstack ctrl.target = 'platform';
* API server, sort this array reverse-alphabetically, and store it in
* a scoped variable. The scope's selected version is initialized to
* the latest (i.e. first) version here as well. After a successful API
* call, the function to update the capabilities is called.
* Sample API return array: ["2015.03.json", "2015.04.json"]
*/
$scope.getVersionList = function () {
var content_url = refstackApiUrl + '/capabilities';
$scope.versionsRequest =
$http.get(content_url).success(function (data) {
$scope.versionList = data.sort().reverse();
$scope.version = $scope.versionList[0];
$scope.update();
}).error(function (error) {
$scope.showError = true;
$scope.error = 'Error retrieving version list: ' +
JSON.stringify(error);
});
};
/** /** The various possible capability statuses. */
* This will contact the Refstack API server to retrieve the JSON ctrl.status = {
* content of the capability file corresponding to the selected required: true,
* version. advisory: false,
*/ deprecated: false,
$scope.update = function () { removed: false
var content_url = refstackApiUrl + '/capabilities/' + };
$scope.version;
$scope.capsRequest =
$http.get(content_url).success(function (data) {
$scope.capabilities = data;
$scope.detailsTemplate = 'components/capabilities/' +
'partials/capabilityDetailsV' +
data.schema + '.html';
$scope.updateTargetCapabilities();
}).error(function (error) {
$scope.showError = true;
$scope.capabilities = null;
$scope.error = 'Error retrieving capabilities: ' +
JSON.stringify(error);
});
};
/** /**
* This will update the scope's 'targetCapabilities' object with * The template to load for displaying capability details. The value
* capabilities belonging to the selected OpenStack marketing program * of this depends on the schema version of the capabilities file.
* (programs typically correspond to 'components' in the DefCore */
* schema). Each capability will have its status mapped to it. ctrl.detailsTemplate = null;
*/
$scope.updateTargetCapabilities = function () {
$scope.targetCapabilities = {};
var components = $scope.capabilities.components;
var targetCaps = $scope.targetCapabilities;
// The 'platform' target is comprised of multiple components, so /**
// we need to get the capabilities belonging to each of its * Retrieve an array of available capability files from the Refstack
// components. * API server, sort this array reverse-alphabetically, and store it in
if ($scope.target === 'platform') { * a scoped variable. The scope's selected version is initialized to
var platform_components = * the latest (i.e. first) version here as well. After a successful API
$scope.capabilities.platform.required; * call, the function to update the capabilities is called.
* Sample API return array: ["2015.03.json", "2015.04.json"]
*/
function getVersionList() {
var content_url = refstackApiUrl + '/capabilities';
ctrl.versionsRequest =
$http.get(content_url).success(function (data) {
ctrl.versionList = data.sort().reverse();
ctrl.version = ctrl.versionList[0];
ctrl.update();
}).error(function (error) {
ctrl.showError = true;
ctrl.error = 'Error retrieving version list: ' +
angular.toJson(error);
});
}
// This will contain status priority values, where lower /**
// values mean higher priorities. * This will contact the Refstack API server to retrieve the JSON
var statusMap = { * content of the capability file corresponding to the selected
required: 1, * version.
advisory: 2, */
deprecated: 3, function update() {
removed: 4 var content_url = refstackApiUrl + '/capabilities/' +
}; ctrl.version;
ctrl.capsRequest =
$http.get(content_url).success(function (data) {
ctrl.capabilities = data;
ctrl.detailsTemplate = 'components/capabilities/' +
'partials/capabilityDetailsV' +
data.schema + '.html';
ctrl.updateTargetCapabilities();
}).error(function (error) {
ctrl.showError = true;
ctrl.capabilities = null;
ctrl.error = 'Error retrieving capabilities: ' +
angular.toJson(error);
});
}
// For each component required for the platform program. /**
angular.forEach(platform_components, function (component) { * This will update the scope's 'targetCapabilities' object with
// Get each capability list belonging to each status. * capabilities belonging to the selected OpenStack marketing program
angular.forEach(components[component], * (programs typically correspond to 'components' in the DefCore
function (caps, status) { * schema). Each capability will have its status mapped to it.
// For each capability. */
angular.forEach(caps, function(cap) { function updateTargetCapabilities() {
// If the capability has already been added. ctrl.targetCapabilities = {};
if (cap in targetCaps) { var components = ctrl.capabilities.components;
// If the status priority value is less var targetCaps = ctrl.targetCapabilities;
// than the saved priority value, update
// the value.
if (statusMap[status] <
statusMap[targetCaps[cap]]) {
targetCaps[cap] = status;
}
}
else {
targetCaps[cap] = status;
}
});
});
});
}
else {
angular.forEach(components[$scope.target],
function (caps, status) {
angular.forEach(caps, function(cap) {
targetCaps[cap] = status;
});
});
}
};
$scope.getVersionList(); // The 'platform' target is comprised of multiple components, so
// we need to get the capabilities belonging to each of its
// components.
if (ctrl.target === 'platform') {
var platform_components = ctrl.capabilities.platform.required;
/** // This will contain status priority values, where lower
* This filter will check if a capability's status corresponds // values mean higher priorities.
* to a status that is checked/selected in the UI. This filter var statusMap = {
* is meant to be used with the ng-repeat directive. required: 1,
* @param {Object} capability advisory: 2,
* @returns {Boolean} True if capability's status is selected deprecated: 3,
*/ removed: 4
$scope.filterStatus = function (capability) { };
var caps = $scope.targetCapabilities;
return ($scope.status.required &&
caps[capability.id] === 'required') ||
($scope.status.advisory &&
caps[capability.id] === 'advisory') ||
($scope.status.deprecated &&
caps[capability.id] === 'deprecated') ||
($scope.status.removed &&
caps[capability.id] === 'removed');
};
/** // For each component required for the platform program.
* This function will get the length of an Object/dict based on angular.forEach(platform_components, function (component) {
* the number of keys it has. // Get each capability list belonging to each status.
* @param {Object} object angular.forEach(components[component],
* @returns {Number} length of object function (caps, status) {
*/ // For each capability.
$scope.getObjectLength = function (object) { angular.forEach(caps, function(cap) {
return Object.keys(object).length; // If the capability has already been added.
}; if (cap in targetCaps) {
}]); // If the status priority value is less
// than the saved priority value, update
// the value.
if (statusMap[status] <
statusMap[targetCaps[cap]]) {
targetCaps[cap] = status;
}
}
else {
targetCaps[cap] = status;
}
});
});
});
}
else {
angular.forEach(components[ctrl.target],
function (caps, status) {
angular.forEach(caps, function(cap) {
targetCaps[cap] = status;
});
});
}
}
/**
* This filter will check if a capability's status corresponds
* to a status that is checked/selected in the UI. This filter
* is meant to be used with the ng-repeat directive.
* @param {Object} capability
* @returns {Boolean} True if capability's status is selected
*/
function filterStatus(capability) {
var caps = ctrl.targetCapabilities;
return (ctrl.status.required &&
caps[capability.id] === 'required') ||
(ctrl.status.advisory &&
caps[capability.id] === 'advisory') ||
(ctrl.status.deprecated &&
caps[capability.id] === 'deprecated') ||
(ctrl.status.removed &&
caps[capability.id] === 'removed');
}
/**
* This function will get the length of an Object/dict based on
* the number of keys it has.
* @param {Object} object
* @returns {Number} length of object
*/
function getObjectLength(object) {
return Object.keys(object).length;
}
ctrl.getVersionList();
}
})();

View File

@ -4,20 +4,20 @@ This expects the JSON data of the capability file to be stored in scope
variable 'capabilities'. variable 'capabilities'.
--> -->
<ol ng-show="capabilities" class="capabilities"> <ol ng-show="ctrl.capabilities" class="capabilities">
<li class="capability-list-item" ng-repeat="capability in capabilities.capabilities | arrayConverter | filter:filterStatus"> <li class="capability-list-item" ng-repeat="capability in ctrl.capabilities.capabilities | arrayConverter | filter:ctrl.filterStatus | orderBy:'id'">
<span class="capability-name">{{capability.id}}</span><br /> <span class="capability-name">{{capability.id}}</span><br />
<em>{{capability.description}}</em><br /> <em>{{capability.description}}</em><br />
Status: <span class="{{targetCapabilities[capability.id]}}">{{targetCapabilities[capability.id]}}</span><br /> Status: <span class="{{ctrl.targetCapabilities[capability.id]}}">{{ctrl.targetCapabilities[capability.id]}}</span><br />
<a ng-click="hideAchievements = !hideAchievements">Achievements ({{capability.achievements.length}})</a><br /> <a ng-click="showAchievements = !showAchievements">Achievements ({{capability.achievements.length}})</a><br />
<ol collapse="hideAchievements" class="list-inline"> <ol collapse="!showAchievements" class="list-inline">
<li ng-repeat="achievement in capability.achievements"> <li ng-repeat="achievement in capability.achievements">
{{achievement}} {{achievement}}
</li> </li>
</ol> </ol>
<a ng-click="hideTests = !hideTests">Tests ({{capability.tests.length}})</a> <a ng-click="showTests = !showTests">Tests ({{capability.tests.length}})</a>
<ul collapse="hideTests"> <ul collapse="!showTests">
<li ng-repeat="test in capability.tests"> <li ng-repeat="test in capability.tests">
<span ng-class="{'glyphicon glyphicon-flag text-warning': capability.flagged.indexOf(test) > -1}"></span> <span ng-class="{'glyphicon glyphicon-flag text-warning': capability.flagged.indexOf(test) > -1}"></span>
{{test}} {{test}}
@ -26,12 +26,12 @@ variable 'capabilities'.
</li> </li>
</ol> </ol>
<div ng-show="capabilities" class="criteria"> <div ng-show="ctrl.capabilities" class="criteria">
<hr> <hr>
<h4><a ng-click="hideCriteria = !hideCriteria">Criteria</a></h4> <h4><a ng-click="showCriteria = !showCriteria">Criteria</a></h4>
<div collapse="hideCriteria"> <div collapse="showCriteria">
<ul> <ul>
<li ng-repeat="(key, criterion) in capabilities.criteria"> <li ng-repeat="(key, criterion) in ctrl.capabilities.criteria">
<span class="criterion-name">{{criterion.name}}</span><br /> <span class="criterion-name">{{criterion.name}}</span><br />
<em>{{criterion.Description}}</em><br /> <em>{{criterion.Description}}</em><br />
Weight: {{criterion.weight}} Weight: {{criterion.weight}}

View File

@ -4,21 +4,21 @@ This expects the JSON data of the capability file to be stored in scope
variable 'capabilities'. variable 'capabilities'.
--> -->
<ol ng-show="capabilities" class="capabilities"> <ol ng-show="ctrl.capabilities" class="capabilities">
<li class="capability-list-item" ng-repeat="capability in capabilities.capabilities | arrayConverter | filter:filterStatus"> <li class="capability-list-item" ng-repeat="capability in ctrl.capabilities.capabilities | arrayConverter | filter:ctrl.filterStatus | orderBy:'id'">
<span class="capability-name">{{capability.id}}</span><br /> <span class="capability-name">{{capability.id}}</span><br />
<em>{{capability.description}}</em><br /> <em>{{capability.description}}</em><br />
Status: <span class="{{targetCapabilities[capability.id]}}">{{targetCapabilities[capability.id]}}</span><br /> Status: <span class="{{ctrl.targetCapabilities[capability.id]}}">{{ctrl.targetCapabilities[capability.id]}}</span><br />
Project: {{capability.project | capitalize}}<br /> Project: {{capability.project | capitalize}}<br />
<a ng-click="hideAchievements = !hideAchievements">Achievements ({{capability.achievements.length}})</a><br /> <a ng-click="showAchievements = !hshowAchievements">Achievements ({{capability.achievements.length}})</a><br />
<ol collapse="hideAchievements" class="list-inline"> <ol collapse="!showAchievements" class="list-inline">
<li ng-repeat="achievement in capability.achievements"> <li ng-repeat="achievement in capability.achievements">
{{achievement}} {{achievement}}
</li> </li>
</ol> </ol>
<a ng-click="hideTests = !hideTests">Tests ({{getObjectLength(capability.tests)}})</a> <a ng-click="showTests = !showTests">Tests ({{ctrl.getObjectLength(capability.tests)}})</a>
<ul collapse="hideTests"> <ul collapse="!showTests">
<li ng-repeat="(testName, testDetails) in capability.tests"> <li ng-repeat="(testName, testDetails) in capability.tests">
<span ng-class="{'glyphicon glyphicon-flag text-warning': testDetails.flagged}" title="{{testDetails.flagged.reason}}"></span> <span ng-class="{'glyphicon glyphicon-flag text-warning': testDetails.flagged}" title="{{testDetails.flagged.reason}}"></span>
{{testName}} {{testName}}
@ -27,12 +27,12 @@ variable 'capabilities'.
</li> </li>
</ol> </ol>
<div ng-show="capabilities" class="criteria"> <div ng-show="ctrl.capabilities" class="criteria">
<hr> <hr>
<h4><a ng-click="hideCriteria = !hideCriteria">Criteria</a></h4> <h4><a ng-click="showCriteria = !showCriteria">Criteria</a></h4>
<div collapse="hideCriteria"> <div collapse="showCriteria">
<ul> <ul>
<li ng-repeat="(key, criterion) in capabilities.criteria"> <li ng-repeat="(key, criterion) in ctrl.capabilities.criteria">
<span class="criterion-name">{{criterion.name}}</span><br /> <span class="criterion-name">{{criterion.name}}</span><br />
<em>{{criterion.Description}}</em><br /> <em>{{criterion.Description}}</em><br />
Weight: {{criterion.weight}} Weight: {{criterion.weight}}

View File

@ -1,21 +1,21 @@
<div class="modal-header"> <div class="modal-header">
<h4>Import public key</h4> <h4>Import Public Key</h4>
</div> </div>
<div class="modal-body container-fluid"> <div class="modal-body container-fluid">
<div class="row"> <div class="row">
<div class="col-md-2">Public key</div> <div class="col-md-2">Public Key</div>
<div class="col-md-9 pull-right"> <div class="col-md-9 pull-right">
<textarea type="text" rows="11" cols="42" ng-model="raw_key" required></textarea> <textarea type="text" rows="11" cols="42" ng-model="modal.raw_key" required></textarea>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-2">Signature</div> <div class="col-md-2">Signature</div>
<div class="col-md-9 pull-right"> <div class="col-md-9 pull-right">
<textarea type="text" rows="11" cols="42" ng-model="self_signature" required></textarea> <textarea type="text" rows="11" cols="42" ng-model="modal.self_signature" required></textarea>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button> <button class="btn btn-warning" ng-click="modal.cancel()">Cancel</button>
<button type="button" class="btn btn-default btn-sm" ng-click="importPubKey()">Import public key</button> <button type="button" class="btn btn-default btn-sm" ng-click="modal.importPubKey()">Import Public Key</button>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<h3>User profile</h3> <h3>User profile</h3>
<div cg-busy="{promise:authRequest,message:'Loading'}"></div> <div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div>
<div> <div>
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<tbody> <tbody>
@ -9,15 +9,15 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div ng-show="pubkeys"> <div ng-show="ctrl.pubkeys">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<h4>User public keys</h4> <h4>User Public Keys</h4>
</div> </div>
<div class="col-md-2 pull-right"> <div class="col-md-2 pull-right">
<button type="button" class="btn btn-default btn-sm" ng-click="openImportPubKeyModal()"> <button type="button" class="btn btn-default btn-sm" ng-click="ctrl.openImportPubKeyModal()">
<span class="glyphicon glyphicon-plus"></span> Import public key <span class="glyphicon glyphicon-plus"></span> Import Public Key
</button> </button>
</div> </div>
</div> </div>
@ -26,7 +26,7 @@
<div> <div>
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<tbody> <tbody>
<tr ng-repeat="pubKey in pubkeys" ng-click="openShowPubKeyModal(pubKey)"> <tr ng-repeat="pubKey in ctrl.pubkeys" ng-click="ctrl.openShowPubKeyModal(pubKey)">
<td>{{pubKey.format}}</td> <td>{{pubKey.format}}</td>
<td>{{pubKey.shortKey}}</td> <td>{{pubKey.shortKey}}</td>
<td>{{pubKey.comment}}</td> <td>{{pubKey.comment}}</td>
@ -34,4 +34,4 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>

View File

@ -1,133 +1,217 @@
/** /*
* Refstack User Profile Controller *
* This controller handles user's profile page, where a user can view * Licensed under the Apache License, Version 2.0 (the "License");
* account-specific information. * 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 refstackApp = angular.module('refstackApp'); (function () {
'use strict';
refstackApp.factory('PubKeys', angular
['$resource', 'refstackApiUrl', function($resource, refstackApiUrl) { .module('refstackApp')
'use strict'; .factory('PubKeys', PubKeys);
PubKeys.$inject = ['$resource', 'refstackApiUrl'];
/**
* This is a provider for the user's uploaded public keys.
*/
function PubKeys($resource, refstackApiUrl) {
return $resource(refstackApiUrl + '/profile/pubkeys/:id', null, null); return $resource(refstackApiUrl + '/profile/pubkeys/:id', null, null);
}]); }
refstackApp.controller('profileController', angular
[ .module('refstackApp')
.controller('ProfileController', ProfileController);
ProfileController.$inject = [
'$scope', '$http', 'refstackApiUrl', 'PubKeys', '$scope', '$http', 'refstackApiUrl', 'PubKeys',
'$modal', 'raiseAlert', '$state', '$modal', 'raiseAlert', '$state'
function($scope, $http, refstackApiUrl, ];
PubKeys, $modal, raiseAlert, $state) {
'use strict';
if (!$scope.auth.isAuthenticated) { /**
$state.go('home'); * RefStack Profile Controller
} * This controller handles user's profile page, where a user can view
* account-specific information.
*/
function ProfileController($scope, $http, refstackApiUrl,
PubKeys, $modal, raiseAlert, $state) {
$scope.updatePubKeys = function (){ var ctrl = this;
var keys = PubKeys.query(function(){
$scope.pubkeys = []; ctrl.updatePubKeys = updatePubKeys;
angular.forEach(keys, function (key) { ctrl.openImportPubKeyModal = openImportPubKeyModal;
$scope.pubkeys.push({ ctrl.openShowPubKeyModal = openShowPubKeyModal;
'resource': key,
'format': key.format, // Must be authenticated to view this page.
'shortKey': [ if (!$scope.auth.isAuthenticated) {
key.pubkey.slice(0, 10), $state.go('home');
'.', }
key.pubkey.slice(-10, -1)
].join('.'), /**
'pubkey': key.pubkey, * This function will fetch all the user's public keys from the
'comment': key.comment * server and store them in an array.
}); */
function updatePubKeys() {
var keys = PubKeys.query(function() {
ctrl.pubkeys = [];
angular.forEach(keys, function (key) {
ctrl.pubkeys.push({
'resource': key,
'format': key.format,
'shortKey': [
key.pubkey.slice(0, 10),
'.',
key.pubkey.slice(-10, -1)
].join('.'),
'pubkey': key.pubkey,
'comment': key.comment
}); });
}); });
}; });
$scope.openImportPubKeyModal = function () {
$modal.open({
templateUrl: '/components/profile/importPubKeyModal.html',
backdrop: true,
windowClass: 'modal',
controller: 'importPubKeyModalController'
}).result.finally(function() {
$scope.updatePubKeys();
});
};
$scope.openShowPubKeyModal = function (pubKey) {
$modal.open({
templateUrl: '/components/profile/showPubKeyModal.html',
backdrop: true,
windowClass: 'modal',
controller: 'showPubKeyModalController',
resolve: {
pubKey: function(){
return pubKey;
}
}
}).result.finally(function() {
$scope.updatePubKeys();
});
};
$scope.showRes = function(pubKey){
raiseAlert('success', '', pubKey.pubkey);
};
$scope.authRequest = $scope.auth.doSignCheck()
.then($scope.updatePubKeys);
} }
]);
refstackApp.controller('importPubKeyModalController', /**
['$scope', '$modalInstance', 'PubKeys', 'raiseAlert', * This function will open the modal that will give the user a form
function ($scope, $modalInstance, PubKeys, raiseAlert) { * for importing a public key.
'use strict'; */
$scope.importPubKey = function () { function openImportPubKeyModal() {
var newPubKey = new PubKeys( $modal.open({
{raw_key: $scope.raw_key, templateUrl: '/components/profile/importPubKeyModal.html',
self_signature: $scope.self_signature} backdrop: true,
); windowClass: 'modal',
newPubKey.$save(function(newPubKey_){ controller: 'ImportPubKeyModalController as modal'
raiseAlert('success', }).result.finally(function() {
'', 'Public key saved successfully'); ctrl.updatePubKeys();
$modalInstance.close(newPubKey_); });
},
function(httpResp){
raiseAlert('danger',
httpResp.statusText, httpResp.data.title);
$scope.cancel();
}
);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
} }
]);
refstackApp.controller('showPubKeyModalController', /**
['$scope', '$modalInstance', 'raiseAlert', 'pubKey', * This function will open the modal that will give the full
function ($scope, $modalInstance, raiseAlert, pubKey) { * information regarding a specific public key.
'use strict'; * @param {Object} pubKey resource
$scope.pubKey = pubKey.resource; */
$scope.rawKey = [pubKey.format, function openShowPubKeyModal(pubKey) {
pubKey.pubkey, pubKey.comment].join('\n'); $modal.open({
$scope.deletePubKey = function () { templateUrl: '/components/profile/showPubKeyModal.html',
$scope.pubKey.$remove( backdrop: true,
{id: $scope.pubKey.id}, windowClass: 'modal',
function(){ controller: 'ShowPubKeyModalController as modal',
raiseAlert('success', resolve: {
'', 'Public key deleted successfully'); pubKey: function() {
$modalInstance.close($scope.pubKey.id); return pubKey;
},
function(httpResp){
raiseAlert('danger',
httpResp.statusText, httpResp.data.title);
$scope.cancel();
} }
); }
}; }).result.finally(function() {
$scope.cancel = function () { ctrl.updatePubKeys();
$modalInstance.dismiss('cancel'); });
};
} }
]
); ctrl.authRequest = $scope.auth.doSignCheck().then(ctrl.updatePubKeys);
}
angular
.module('refstackApp')
.controller('ImportPubKeyModalController', ImportPubKeyModalController);
ImportPubKeyModalController.$inject = [
'$modalInstance', 'PubKeys', 'raiseAlert'
];
/**
* Import Pub Key Modal Controller
* This controller is for the modal that appears if a user wants to import
* a public key.
*/
function ImportPubKeyModalController($modalInstance, PubKeys, raiseAlert) {
var ctrl = this;
ctrl.importPubKey = importPubKey;
ctrl.cancel = cancel;
/**
* This function will save a new public key resource to the API server.
*/
function importPubKey() {
var newPubKey = new PubKeys(
{raw_key: ctrl.raw_key, self_signature: ctrl.self_signature}
);
newPubKey.$save(
function(newPubKey_) {
raiseAlert('success', '', 'Public key saved successfully');
$modalInstance.close(newPubKey_);
},
function(httpResp) {
raiseAlert('danger',
httpResp.statusText, httpResp.data.title);
ctrl.cancel();
}
);
}
/**
* This function will dismiss the modal.
*/
function cancel() {
$modalInstance.dismiss('cancel');
}
}
angular
.module('refstackApp')
.controller('ShowPubKeyModalController', ShowPubKeyModalController);
ShowPubKeyModalController.$inject = [
'$modalInstance', 'raiseAlert', 'pubKey'
];
/**
* Show Pub Key Modal Controller
* This controller is for the modal that appears if a user wants to see the
* full details of one of their public keys.
*/
function ShowPubKeyModalController($modalInstance, raiseAlert, pubKey) {
var ctrl = this;
ctrl.deletePubKey = deletePubKey;
ctrl.cancel = cancel;
ctrl.pubKey = pubKey.resource;
ctrl.rawKey = [pubKey.format, pubKey.pubkey, pubKey.comment].join('\n');
/**
* This function will delete a public key resource.
*/
function deletePubKey() {
ctrl.pubKey.$remove(
{id: ctrl.pubKey.id},
function() {
raiseAlert('success',
'', 'Public key deleted successfully');
$modalInstance.close(ctrl.pubKey.id);
},
function(httpResp) {
raiseAlert('danger',
httpResp.statusText, httpResp.data.title);
ctrl.cancel();
}
);
}
/**
* This method will dismiss the modal.
*/
function cancel() {
$modalInstance.dismiss('cancel');
}
}
})();

View File

@ -1,11 +1,11 @@
<div class="modal-header"> <div class="modal-header">
<h4>Public key</h4> <h4>Public Key</h4>
</div> </div>
<div class="modal-body container-fluid"> <div class="modal-body container-fluid">
<textarea type="text" rows="10" cols="67" readonly="readonly">{{::rawKey}}</textarea> <textarea type="text" rows="10" cols="67" readonly="readonly">{{modal.rawKey}}</textarea>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button> <button class="btn btn-warning" ng-click="modal.cancel()">Cancel</button>
<button type="button" class="btn btn-danger btn-sm" ng-click="deletePubKey() " <button type="button" class="btn btn-danger btn-sm" ng-click="modal.deletePubKey() "
confirm="Are you sure you want to delete this public key? You will lose management access to any test results signed with this key.">Delete</button> confirm="Are you sure you want to delete this public key? You will lose management access to any test results signed with this key.">Delete</button>
</div> </div>
</div> </div>

View File

@ -1,13 +1,13 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h4>All Passed Tests ({{tests.length}})</h4> <h4>All Passed Tests ({{modal.tests.length}})</h4>
</div> </div>
<div class="modal-body tests-modal-content"> <div class="modal-body tests-modal-content">
<div class="form-group"> <div class="form-group">
<textarea class="form-control" rows="20" id="tests" wrap="off">{{getTestListString()}}</textarea> <textarea class="form-control" rows="20" id="tests" wrap="off">{{modal.getTestListString()}}</textarea>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="close()">Close</button> <button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button>
</div> </div>
</div> </div>

View File

@ -3,13 +3,13 @@ HTML for each accordion group that separates the status types on the results
report page. report page.
--> -->
<accordion-group is-open="isOpen" is-disabled="caps[status].caps.length == 0"> <accordion-group is-open="isOpen" is-disabled="ctrl.caps[status].caps.length == 0">
<accordion-heading> <accordion-heading>
{{status | capitalize}} {{status | capitalize}}
<small> <small>
(<strong>Total:</strong> {{caps[status].caps.length}} capabilities, {{caps[status].count}} tests) (<strong>Total:</strong> {{ctrl.caps[status].caps.length}} capabilities, {{ctrl.caps[status].count}} tests)
<span ng-if="testStatus !== 'total'"> <span ng-if="ctrl.testStatus !== 'total'">
(<strong>{{testStatus | capitalize}}:</strong> {{getStatusTestCount(status)}} tests) (<strong>{{ctrl.testStatus | capitalize}}:</strong> {{ctrl.getStatusTestCount(status)}} tests)
</span> </span>
</small> </small>
<i class="pull-right glyphicon" <i class="pull-right glyphicon"
@ -17,18 +17,18 @@ report page.
</i> </i>
</accordion-heading> </accordion-heading>
<ol class="capabilities"> <ol class="capabilities">
<li ng-repeat="capability in caps[status].caps | orderBy:'id'" <li ng-repeat="capability in ctrl.caps[status].caps | orderBy:'id'"
ng-if="isCapabilityShown(capability)"> ng-if="ctrl.isCapabilityShown(capability)">
<a ng-click="hideTests = !hideTests" <a ng-click="ctrl.hideTests = !ctrl.hideTests"
title="{{capabilityData.capabilities[capability.id].description}}"> title="{{ctrl.capabilityData.capabilities[capability.id].description}}">
{{capability.id}} {{capability.id}}
</a> </a>
<span ng-class="{'text-success': testStatus === 'passed', <span ng-class="{'text-success': ctrl.testStatus === 'passed',
'text-danger': testStatus === 'not passed', 'text-danger': ctrl.testStatus === 'not passed',
'text-warning': testStatus === 'flagged'}" 'text-warning': ctrl.testStatus === 'flagged'}"
ng-if="testStatus !== 'total'"> ng-if="ctrl.testStatus !== 'total'">
[{{getCapabilityTestCount(capability)}}] [{{ctrl.getCapabilityTestCount(capability)}}]
</span> </span>
<span ng-class="{'text-success': (capability.passedTests.length > 0 && <span ng-class="{'text-success': (capability.passedTests.length > 0 &&
capability.notPassedTests.length == 0), capability.notPassedTests.length == 0),
@ -36,31 +36,31 @@ report page.
capability.notPassedTests.length > 0), capability.notPassedTests.length > 0),
'text-warning': (capability.passedTests.length > 0 && 'text-warning': (capability.passedTests.length > 0 &&
capability.notPassedTests.length > 0)}" capability.notPassedTests.length > 0)}"
ng-if="testStatus === 'total'"> ng-if="ctrl.testStatus === 'total'">
[{{capability.passedTests.length}}/{{capability.passedTests.length + [{{capability.passedTests.length}}/{{capability.passedTests.length +
capability.notPassedTests.length}}] capability.notPassedTests.length}}]
</span> </span>
<ul class="list-unstyled" collapse="hideTests"> <ul class="list-unstyled" collapse="ctrl.hideTests">
<li ng-repeat="test in capability.passedTests | orderBy:'toString()'" <li ng-repeat="test in capability.passedTests | orderBy:'toString()'"
ng-if="isTestShown(test, capability)"> ng-if="ctrl.isTestShown(test, capability)">
<span class="glyphicon glyphicon-ok text-success" <span class="glyphicon glyphicon-ok text-success"
aria-hidden="true"> aria-hidden="true">
</span> </span>
<span ng-class="{'glyphicon glyphicon-flag text-warning': <span ng-class="{'glyphicon glyphicon-flag text-warning':
isTestFlagged(test, capabilityData.capabilities[capability.id])}" ctrl.isTestFlagged(test, ctrl.capabilityData.capabilities[capability.id])}"
title="{{getFlaggedReason(test, capabilityData.capabilities[capability.id])}}"> title="{{ctrl.getFlaggedReason(test, ctrl.capabilityData.capabilities[capability.id])}}">
</span> </span>
{{test}} {{test}}
</li> </li>
<li ng-repeat="test in capability.notPassedTests | orderBy:'toString()'" <li ng-repeat="test in capability.notPassedTests | orderBy:'toString()'"
ng-if="isTestShown(test, capability)"> ng-if="ctrl.isTestShown(test, capability)">
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span> <span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
<span ng-class="{'glyphicon glyphicon-flag text-warning': <span ng-class="{'glyphicon glyphicon-flag text-warning':
isTestFlagged(test, capabilityData.capabilities[capability.id])}" ctrl.isTestFlagged(test, ctrl.capabilityData.capabilities[capability.id])}"
title="{{getFlaggedReason(test, capabilityData.capabilities[capability.id])}}"> title="{{ctrl.getFlaggedReason(test, ctrl.capabilityData.capabilities[capability.id])}}">
</span> </span>
{{test}} {{test}}
</li> </li>

View File

@ -1,31 +1,31 @@
<h3>Test Run Results</h3> <h3>Test Run Results</h3>
<div ng-show="resultsData" class="container-fluid"> <div ng-show="ctrl.resultsData" class="container-fluid">
<div class="row"> <div class="row">
<div class="pull-left"> <div class="pull-left">
<div class="test-report"> <div class="test-report">
<strong>Test ID:</strong> {{testId}}<br /> <strong>Test ID:</strong> {{ctrl.testId}}<br />
<div ng-if="isEditingAllowed()"><strong>Cloud ID:</strong> {{resultsData.cpid}}<br /></div> <div ng-if="ctrl.isEditingAllowed()"><strong>Cloud ID:</strong> {{ctrl.resultsData.cpid}}<br /></div>
<strong>Upload Date:</strong> {{resultsData.created_at}} UTC<br /> <strong>Upload Date:</strong> {{ctrl.resultsData.created_at}} UTC<br />
<strong>Duration:</strong> {{resultsData.duration_seconds}} seconds<br /> <strong>Duration:</strong> {{ctrl.resultsData.duration_seconds}} seconds<br />
<strong>Total Number of Passed Tests:</strong> <strong>Total Number of Passed Tests:</strong>
<a title="See all passed tests" ng-click="openFullTestListModal()"> <a title="See all passed tests" ng-click="ctrl.openFullTestListModal()">
{{resultsData.results.length}} {{ctrl.resultsData.results.length}}
</a><br /> </a><br />
<hr> <hr>
</div> </div>
</div> </div>
<div class="pull-right"> <div class="pull-right">
<div ng-show="isEditingAllowed()"> <div ng-show="ctrl.isEditingAllowed()">
<button class="btn btn-warning" ng-hide="isShared()" ng-click="shareTestRun(true)" confirm="Are you sure you want to share these test run results with the community?">Share</button> <button class="btn btn-warning" ng-hide="ctrl.isShared()" ng-click="ctrl.shareTestRun(true)" confirm="Are you sure you want to share these test run results with the community?">Share</button>
<button class="btn btn-success" ng-show="isShared()" ng-click="shareTestRun(false)">Unshare</button> <button class="btn btn-success" ng-show="ctrl.isShared()" ng-click="ctrl.shareTestRun(false)">Unshare</button>
<button type="button" class="btn btn-danger" ng-click="deleteTestRun()" confirm="Are you sure you want to delete these test run results?">Delete</button> <button type="button" class="btn btn-danger" ng-click="ctrl.deleteTestRun()" confirm="Are you sure you want to delete these test run results?">Delete</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div ng-show="resultsData"> <div ng-show="ctrl.resultsData">
<p>See how these results stack up against DefCore capabilities and OpenStack <p>See how these results stack up against DefCore capabilities and OpenStack
<a target="_blank" href="http://www.openstack.org/brand/interop/">target marketing programs.</a> <a target="_blank" href="http://www.openstack.org/brand/interop/">target marketing programs.</a>
</p> </p>
@ -34,14 +34,14 @@
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<strong>Capabilities Version:</strong> <strong>Capabilities Version:</strong>
<select ng-model="version" ng-change="updateCapabilities()" class="form-control"> <select ng-model="ctrl.version" ng-change="ctrl.updateCapabilities()" class="form-control">
<!-- Slicing the version file name here gets rid of the '.json' file extension --> <!-- Slicing the version file name here gets rid of the '.json' file extension -->
<option ng-repeat="versionFile in versionList" value="{{versionFile}}">{{versionFile.slice(0, -5)}}</option> <option ng-repeat="versionFile in ctrl.versionList" value="{{versionFile}}">{{versionFile.slice(0, -5)}}</option>
</select> </select>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<strong>Target Program:</strong> <strong>Target Program:</strong>
<select ng-model="target" class="form-control" ng-change="buildCapabilitiesObject()"> <select ng-model="ctrl.target" class="form-control" ng-change="ctrl.buildCapabilitiesObject()">
<option value="platform">OpenStack Powered Platform</option> <option value="platform">OpenStack Powered Platform</option>
<option value="compute">OpenStack Powered Compute</option> <option value="compute">OpenStack Powered Compute</option>
<option value="object">OpenStack Powered Object Storage</option> <option value="object">OpenStack Powered Object Storage</option>
@ -53,28 +53,28 @@
<br /> <br />
<strong>Corresponding OpenStack Releases:</strong> <strong>Corresponding OpenStack Releases:</strong>
<ul class="list-inline"> <ul class="list-inline">
<li ng-repeat="release in capabilityData.releases"> <li ng-repeat="release in ctrl.capabilityData.releases">
{{release | capitalize}} {{release | capitalize}}
</li> </li>
</ul> </ul>
<hr > <hr >
<div ng-show="capabilityData"> <div ng-show="ctrl.capabilityData">
<strong>Status:</strong> <strong>Status:</strong>
<p>This cloud passes <strong>{{requiredPassPercent | number:1}}% </strong> <p>This cloud passes <strong>{{ctrl.requiredPassPercent | number:1}}% </strong>
({{caps.required.passedCount}}/{{caps.required.count}}) ({{ctrl.caps.required.passedCount}}/{{ctrl.caps.required.count}})
of the tests in the <strong>{{version.slice(0, -5)}}</strong> <em>required</em> capabilities for the of the tests in the <strong>{{ctrl.version.slice(0, -5)}}</strong> <em>required</em> capabilities for the
<strong>{{targetMappings[target]}}</strong> program. <br /> <strong>{{ctrl.targetMappings[target]}}</strong> program. <br />
Excluding flagged tests, this cloud passes Excluding flagged tests, this cloud passes
<strong>{{nonFlagRequiredPassPercent | number:1}}%</strong> <strong>{{ctrl.nonFlagRequiredPassPercent | number:1}}%</strong>
({{nonFlagPassCount}}/{{totalNonFlagCount}}) ({{ctrl.nonFlagPassCount}}/{{ctrl.totalNonFlagCount}})
of the <em>required</em> tests. of the <em>required</em> tests.
</p> </p>
<p>Compliance with <strong>{{version.slice(0, -5)}}</strong>: <p>Compliance with <strong>{{ctrl.version.slice(0, -5)}}</strong>:
<strong> <strong>
<span ng-if="nonFlagPassCount === totalNonFlagCount" class="yes">YES</span> <span ng-if="ctrl.nonFlagPassCount === ctrl.totalNonFlagCount" class="yes">YES</span>
<span ng-if="nonFlagPassCount !== totalNonFlagCount" class="no">NO</span> <span ng-if="ctrl.nonFlagPassCount !== ctrl.totalNonFlagCount" class="no">NO</span>
</strong> </strong>
</p> </p>
@ -83,20 +83,20 @@
Test Filters:<br /> Test Filters:<br />
<div class="btn-group button-margin" data-toggle="buttons"> <div class="btn-group button-margin" data-toggle="buttons">
<label class="btn btn-default" ng-class="{'active': testStatus === 'total'}"> <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'total'}">
<input type="radio" ng-model="testStatus" value="total"> <input type="radio" ng-model="ctrl.testStatus" value="total">
<span class="text-primary">All</span> <span class="text-primary">All</span>
</label> </label>
<label class="btn btn-default" ng-class="{'active': testStatus === 'passed'}"> <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'passed'}">
<input type="radio" ng-model="testStatus" value="passed"> <input type="radio" ng-model="ctrl.testStatus" value="passed">
<span class="text-success">Passed</span> <span class="text-success">Passed</span>
</label> </label>
<label class="btn btn-default" ng-class="{'active': testStatus === 'not passed'}"> <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'not passed'}">
<input type="radio" ng-model="testStatus" value="not passed"> <input type="radio" ng-model="ctrl.testStatus" value="not passed">
<span class="text-danger">Not Passed</span> <span class="text-danger">Not Passed</span>
</label> </label>
<label class="btn btn-default" ng-class="{'active': testStatus === 'flagged'}"> <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'flagged'}">
<input type="radio" ng-model="testStatus" value="flagged"> <input type="radio" ng-model="ctrl.testStatus" value="flagged">
<span class="text-warning">Flagged</span> <span class="text-warning">Flagged</span>
</label> </label>
</div> </div>
@ -105,23 +105,23 @@
<!-- The ng-repeat is used to pass in a local variable to the template. --> <!-- The ng-repeat is used to pass in a local variable to the template. -->
<ng-include <ng-include
ng-repeat="status in ['required']" ng-repeat="status in ['required']"
src="detailsTemplate" src="ctrl.detailsTemplate"
onload="isOpen = true"> onload="isOpen = true">
</ng-include> </ng-include>
<br /> <br />
<ng-include <ng-include
ng-repeat="status in ['advisory']" ng-repeat="status in ['advisory']"
src="detailsTemplate"> src="ctrl.detailsTemplate">
</ng-include> </ng-include>
<br /> <br />
<ng-include <ng-include
ng-repeat="status in ['deprecated']" ng-repeat="status in ['deprecated']"
src="detailsTemplate"> src="ctrl.detailsTemplate">
</ng-include> </ng-include>
<br /> <br />
<ng-include <ng-include
ng-repeat="status in ['removed']" ng-repeat="status in ['removed']"
src="detailsTemplate"> src="ctrl.detailsTemplate">
</ng-include> </ng-include>
</accordion> </accordion>
</div> </div>

View File

@ -1,4 +1,4 @@
<h3>{{pageHeader}}</h3> <h3>{{ctrl.pageHeader}}</h3>
<p>The most recently uploaded community test results are listed here.</p> <p>The most recently uploaded community test results are listed here.</p>
<div class="result-filters"> <div class="result-filters">
@ -8,12 +8,11 @@
<label for="cpid">Start Date</label> <label for="cpid">Start Date</label>
<p class="input-group"> <p class="input-group">
<input type="text" class="form-control" <input type="text" class="form-control"
datepicker-popup="{{format}}" datepicker-popup="{{ctrl.format}}"
ng-model="startDate" is-open="startOpen" ng-model="ctrl.startDate" is-open="ctrl.startOpen"
datepicker-options="dateOptions"
close-text="Close" /> close-text="Close" />
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event, 'startOpen')"> <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'startOpen')">
<i class="glyphicon glyphicon-calendar"></i> <i class="glyphicon glyphicon-calendar"></i>
</button> </button>
</span> </span>
@ -23,62 +22,61 @@
<label for="cpid">End Date</label> <label for="cpid">End Date</label>
<p class="input-group"> <p class="input-group">
<input type="text" class="form-control" <input type="text" class="form-control"
datepicker-popup="{{format}}" datepicker-popup="{{ctrl.format}}"
ng-model="endDate" is-open="endOpen" ng-model="ctrl.endDate" is-open="ctrl.endOpen"
datepicker-options="dateOptions"
close-text="Close" /> close-text="Close" />
<span class="input-group-btn"> <span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event, 'endOpen')"> <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'endOpen')">
<i class="glyphicon glyphicon-calendar"></i> <i class="glyphicon glyphicon-calendar"></i>
</button> </button>
</span> </span>
</p> </p>
</div> </div>
<div class="col-md-3"style="margin-top:24px;"> <div class="col-md-3"style="margin-top:24px;">
<button type="submit" class="btn btn-primary" ng-click="update()">Filter</button> <button type="submit" class="btn btn-primary" ng-click="ctrl.update()">Filter</button>
<button type="submit" class="btn btn-primary btn-danger" ng-click="clearFilters()">Clear</button> <button type="submit" class="btn btn-primary btn-danger" ng-click="ctrl.clearFilters()">Clear</button>
</div> </div>
</div> </div>
</div> </div>
<div cg-busy="{promise:authRequest,message:'Loading'}"></div> <div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div>
<div cg-busy="{promise:resultsRequest,message:'Loading'}"></div> <div cg-busy="{promise:ctrl.resultsRequest,message:'Loading'}"></div>
<div ng-show="data" class="results-table"> <div ng-show="ctrl.data" class="results-table">
<table ng-show="data" class="table table-striped table-hover"> <table ng-show="ctrl.data" class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Upload Date</th> <th>Upload Date</th>
<th>Test Run ID</th> <th>Test Run ID</th>
<th ng-if="::isUserResults">Shared</th> <th ng-if="ctrl.isUserResults">Shared</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="result in data.results"> <tr ng-repeat="result in ctrl.data.results">
<td>{{result.created_at}}</td> <td>{{result.created_at}}</td>
<td><a ui-sref="resultsDetail({testID: result.id})">{{result.id}}</a></td> <td><a ui-sref="resultsDetail({testID: result.id})">{{result.id}}</a></td>
<td ng-if="::isUserResults"><span ng-show="result.meta.shared" class="glyphicon glyphicon-share"></span></td> <td ng-if="ctrl.isUserResults"><span ng-show="result.meta.shared" class="glyphicon glyphicon-share"></span></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="pages"> <div class="pages">
<pagination <pagination
total-items="totalItems" total-items="ctrl.totalItems"
ng-model="currentPage" ng-model="ctrl.currentPage"
items-per-page="itemsPerPage" items-per-page="ctrl.itemsPerPage"
max-size="maxSize" max-size="ctrl.maxSize"
class="pagination-sm" class="pagination-sm"
boundary-links="true" boundary-links="true"
rotate="false" rotate="false"
num-pages="numPages" num-pages="ctrl.numPages"
ng-change="update()"> ng-change="ctrl.update()">
</pagination> </pagination>
</div> </div>
</div> </div>
<div ng-show="showError" class="alert alert-danger" role="alert"> <div ng-show="ctrl.showError" class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span> <span class="sr-only">Error:</span>
{{error}} {{ctrl.error}}
</div> </div>

View File

@ -1,107 +1,142 @@
var refstackApp = angular.module('refstackApp'); /*
* Licensed under the Apache License, Version 2.0 (the "License");
/** * you may not use this file except in compliance with the License.
* Refstack Results Controller * You may obtain a copy of the License at
* This controller is for the '/results' page where a user can browse *
* a listing of community uploaded results. * 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.
*/ */
refstackApp.controller('resultsController',
['$scope', '$http', '$filter', '$state', 'refstackApiUrl',
function ($scope, $http, $filter, $state, refstackApiUrl) {
'use strict';
/** Initial page to be on. */ (function () {
$scope.currentPage = 1; 'use strict';
/** angular
* How many results should display on each page. Since pagination .module('refstackApp')
* is server-side implemented, this value should match the .controller('ResultsController', ResultsController);
* 'results_per_page' configuration of the Refstack server which
* defaults to 20.
*/
$scope.itemsPerPage = 20;
/** ResultsController.$inject = [
* How many page buttons should be displayed at max before adding '$scope', '$http', '$filter', '$state', 'refstackApiUrl'
* the '...' button. ];
*/
$scope.maxSize = 5;
/** The upload date lower limit to be used in filtering results. */ /**
$scope.startDate = ''; * RefStack Results Controller
* This controller is for the '/results' page where a user can browse
* a listing of community uploaded results.
*/
function ResultsController($scope, $http, $filter, $state, refstackApiUrl) {
var ctrl = this;
/** The upload date upper limit to be used in filtering results. */ ctrl.update = update;
$scope.endDate = ''; ctrl.open = open;
ctrl.clearFilters = clearFilters;
$scope.isUserResults = $state.current.name === 'userResults'; /** Initial page to be on. */
if ($scope.isUserResults && !$scope.auth.isAuthenticated) { ctrl.currentPage = 1;
$state.go('home');
}
$scope.pageHeader = $scope.isUserResults ?
'Private test results' : 'Community test results';
/**
* This will contact the Refstack API to get a listing of test run
* results.
*/
$scope.update = function () {
$scope.showError = false;
// Construct the API URL based on user-specified filters.
var content_url = refstackApiUrl + '/results?page=' +
$scope.currentPage;
var start = $filter('date')($scope.startDate, 'yyyy-MM-dd');
if (start) {
content_url =
content_url + '&start_date=' + start + ' 00:00:00';
}
var end = $filter('date')($scope.endDate, 'yyyy-MM-dd');
if (end) {
content_url = content_url + '&end_date=' + end + ' 23:59:59';
}
if ($scope.isUserResults) {
content_url = content_url + '&signed';
}
$scope.resultsRequest =
$http.get(content_url).success(function (data) {
$scope.data = data;
$scope.totalItems = $scope.data.pagination.total_pages *
$scope.itemsPerPage;
$scope.currentPage = $scope.data.pagination.current_page;
}).error(function (error) {
$scope.data = null;
$scope.totalItems = 0;
$scope.showError = true;
$scope.error =
'Error retrieving results listing from server: ' +
JSON.stringify(error);
});
};
if ($scope.isUserResults) {
$scope.authRequest = $scope.auth.doSignCheck()
.then($scope.update);
} else {
$scope.update();
}
/** /**
* This is called when the date filter calendar is opened. It * How many results should display on each page. Since pagination
* does some event handling, and sets a scope variable so the UI * is server-side implemented, this value should match the
* knows which calendar was opened. * 'results_per_page' configuration of the Refstack server which
* @param {Object} $event - The Event object * defaults to 20.
* @param {String} openVar - Tells which calendar was opened */
*/ ctrl.itemsPerPage = 20;
$scope.open = function ($event, openVar) {
$event.preventDefault();
$event.stopPropagation();
$scope[openVar] = true;
};
/** /**
* This function will clear all filters and update the results * How many page buttons should be displayed at max before adding
* listing. * the '...' button.
*/ */
$scope.clearFilters = function () { ctrl.maxSize = 5;
$scope.startDate = null;
$scope.endDate = null; /** The upload date lower limit to be used in filtering results. */
$scope.update(); ctrl.startDate = '';
};
}]); /** The upload date upper limit to be used in filtering results. */
ctrl.endDate = '';
/** The date format for the date picker. */
ctrl.format = 'yyyy-MM-dd';
/** Check to see if this page should display user-specific results. */
ctrl.isUserResults = $state.current.name === 'userResults';
// Should only be on user-results-page if authenticated.
if (ctrl.isUserResults && !$scope.auth.isAuthenticated) {
$state.go('home');
}
ctrl.pageHeader = ctrl.isUserResults ?
'Private test results' : 'Community test results';
if (ctrl.isUserResults) {
ctrl.authRequest = $scope.auth.doSignCheck()
.then(ctrl.update);
} else {
ctrl.update();
}
/**
* This will contact the Refstack API to get a listing of test run
* results.
*/
function update() {
ctrl.showError = false;
// Construct the API URL based on user-specified filters.
var content_url = refstackApiUrl + '/results' +
'?page=' + ctrl.currentPage;
var start = $filter('date')(ctrl.startDate, 'yyyy-MM-dd');
if (start) {
content_url =
content_url + '&start_date=' + start + ' 00:00:00';
}
var end = $filter('date')(ctrl.endDate, 'yyyy-MM-dd');
if (end) {
content_url = content_url + '&end_date=' + end + ' 23:59:59';
}
if (ctrl.isUserResults) {
content_url = content_url + '&signed';
}
ctrl.resultsRequest =
$http.get(content_url).success(function (data) {
ctrl.data = data;
ctrl.totalItems = ctrl.data.pagination.total_pages *
ctrl.itemsPerPage;
ctrl.currentPage = ctrl.data.pagination.current_page;
}).error(function (error) {
ctrl.data = null;
ctrl.totalItems = 0;
ctrl.showError = true;
ctrl.error =
'Error retrieving results listing from server: ' +
angular.toJson(error);
});
}
/**
* This is called when the date filter calendar is opened. It
* does some event handling, and sets a scope variable so the UI
* knows which calendar was opened.
* @param {Object} $event - The Event object
* @param {String} openVar - Tells which calendar was opened
*/
function open($event, openVar) {
$event.preventDefault();
$event.stopPropagation();
ctrl[openVar] = true;
}
/**
* This function will clear all filters and update the results
* listing.
*/
function clearFilters() {
ctrl.startDate = null;
ctrl.endDate = null;
ctrl.update();
}
}
})();

View File

@ -1,8 +1,8 @@
<div class="modal-body" style="padding:0px"> <div class="modal-body" style="padding:0px">
<div class="alert alert-{{::data.mode}}" style="margin-bottom:0px"> <div class="alert alert-{{alert.data.mode}}" style="margin-bottom:0px">
<button type="button" class="close" data-ng-click="close()" > <button type="button" class="close" data-ng-click="alert.close()" >
<span class="glyphicon glyphicon-remove-circle"></span> <span class="glyphicon glyphicon-remove-circle"></span>
</button> </button>
<strong>{{::data.title}}</strong> {{::data.text}} <strong>{{alert.data.title}}</strong> {{alert.data.text}}
</div> </div>
</div> </div>

View File

@ -1,12 +1,35 @@
var refstackApp = angular.module('refstackApp'); /*
* Licensed 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.
*/
refstackApp.factory('raiseAlert', (function () {
['$modal', function($modal) { 'use strict';
'use strict';
angular
.module('refstackApp')
.factory('raiseAlert', raiseAlert);
raiseAlert.$inject = ['$modal'];
/**
* This allows alert pop-ups to be raised. Just inject it as a dependency
* in the calling controller.
*/
function raiseAlert($modal) {
return function(mode, title, text) { return function(mode, title, text) {
$modal.open({ $modal.open({
templateUrl: '/shared/alerts/alertModal.html', templateUrl: '/shared/alerts/alertModal.html',
controller: 'raiseAlertModalController', controller: 'RaiseAlertModalController as alert',
backdrop: true, backdrop: true,
keyboard: true, keyboard: true,
backdropClick: true, backdropClick: true,
@ -22,20 +45,30 @@ refstackApp.factory('raiseAlert',
} }
}); });
}; };
}] }
);
angular
.module('refstackApp')
.controller('RaiseAlertModalController', RaiseAlertModalController);
refstackApp.controller('raiseAlertModalController', RaiseAlertModalController.$inject = ['$modalInstance', 'data'];
['$scope', '$modalInstance', 'data',
function ($scope, $modalInstance, data) {
'use strict';
$scope.data = data;
//wait for users click to close the pop up window. /**
$scope.close = function() { * This is the controller for the alert pop-up.
$modalInstance.close(); */
}; function RaiseAlertModalController($modalInstance, data) {
var ctrl = this;
ctrl.close = close;
ctrl.data = data;
/**
* This method will close the alert modal. The modal will close
* when the user clicks the close button or clicks outside of the
* modal.
*/
function close() {
$modalInstance.close();
} }
] }
); })();

View File

@ -1,31 +1,53 @@
var refstackApp = angular.module('refstackApp'); /*
* Licensed under the Apache License, Version 2.0 (the "License");
/** Refstack AngularJS Filters */ * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
/** *
* Convert an object of objects to an array of objects to use with ng-repeat * http://www.apache.org/licenses/LICENSE-2.0
* filters. *
* 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.
*/ */
refstackApp.filter('arrayConverter', function () {
(function () {
'use strict'; 'use strict';
return function (objects) { /**
var array = []; * Convert an object of objects to an array of objects to use with ng-repeat
angular.forEach(objects, function (object, key) { * filters.
object.id = key; */
array.push(object); angular
}); .module('refstackApp')
return array; .filter('arrayConverter', arrayConverter);
};
});
/** /**
* Angular filter that will capitalize the first letter of a string. * Convert an object of objects to an array of objects to use with ng-repeat
*/ * filters.
refstackApp.filter('capitalize', function() { */
'use strict'; function arrayConverter() {
return function (objects) {
var array = [];
angular.forEach(objects, function (object, key) {
object.id = key;
array.push(object);
});
return array;
};
}
return function (string) { angular
return string.substring(0, 1).toUpperCase() + string.substring(1); .module('refstackApp')
}; .filter('capitalize', capitalize);
});
/**
* Angular filter that will capitalize the first letter of a string.
*/
function capitalize() {
return function (string) {
return string.substring(0, 1).toUpperCase() + string.substring(1);
};
}
})();

View File

@ -1,11 +1,11 @@
<div class="heading"><a ui-sref="home"><img src="assets/img/refstack-logo.png" alt="RefStack"></a> <div class="heading"><a ui-sref="home"><img src="assets/img/refstack-logo.png" alt="RefStack"></a>
RefStack RefStack
</div> </div>
<nav class="navbar navbar-default" role="navigation" ng-controller="headerController"> <nav class="navbar navbar-default" role="navigation" ng-controller="HeaderController as header">
<div class="container-fluid"> <div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display --> <!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" ng-click="navbarCollapsed = !navbarCollapsed"> <button type="button" class="navbar-toggle collapsed" ng-click="header.navbarCollapsed = !header.navbarCollapsed">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -13,12 +13,12 @@ RefStack
</button> </button>
</div> </div>
<div class="collapse navbar-collapse" id="navbar" collapse="navbarCollapsed"> <div class="collapse navbar-collapse" id="navbar" collapse="header.navbarCollapsed">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li ng-class="{ active: isActive('/')}"><a ui-sref="home">Home</a></li> <li ng-class="{ active: header.isActive('/')}"><a ui-sref="home">Home</a></li>
<li ng-class="{ active: isActive('/about')}"><a ui-sref="about">About</a></li> <li ng-class="{ active: header.isActive('/about')}"><a ui-sref="about">About</a></li>
<li ng-class="{ active: isActive('/capabilities')}"><a ui-sref="capabilities">DefCore Capabilities</a></li> <li ng-class="{ active: header.isActive('/capabilities')}"><a ui-sref="capabilities">DefCore Capabilities</a></li>
<li ng-class="{ active: isActive('/community_results')}"><a ui-sref="communityResults">Community Results</a></li> <li ng-class="{ active: header.isActive('/community_results')}"><a ui-sref="communityResults">Community Results</a></li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li ng-class="{ active: isActive('/user_results')}" ng-if="auth.isAuthenticated"><a ui-sref="userResults">My Results</a></li> <li ng-class="{ active: isActive('/user_results')}" ng-if="auth.isAuthenticated"><a ui-sref="userResults">My Results</a></li>

View File

@ -1,22 +1,44 @@
var refstackApp = angular.module('refstackApp'); /*
* Licensed under the Apache License, Version 2.0 (the "License");
/** * you may not use this file except in compliance with the License.
* Refstack Header Controller * You may obtain a copy of the License at
* This controller is for the header template which contains the site *
* navigation. * 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.
*/ */
refstackApp.controller('headerController',
['$scope', '$location', function ($scope, $location) { (function () {
'use strict'; 'use strict';
angular
.module('refstackApp')
.controller('HeaderController', HeaderController);
HeaderController.$inject = ['$location'];
/**
* Refstack Header Controller
* This controller is for the header template which contains the site
* navigation.
*/
function HeaderController($location) {
var ctrl = this;
ctrl.isActive = isActive;
/** Whether the Navbar is collapsed for small displays. */ /** Whether the Navbar is collapsed for small displays. */
$scope.navbarCollapsed = true; ctrl.navbarCollapsed = true;
/** /**
* This determines whether a button should be in the active state based * This determines whether a button should be in the active state based
* on the URL. * on the URL.
*/ */
$scope.isActive = function (viewLocation) { function isActive(viewLocation) {
var path = $location.path().substr(0, viewLocation.length); var path = $location.path().substr(0, viewLocation.length);
if (path === viewLocation) { if (path === viewLocation) {
// Make sure "/" only matches when viewLocation is "/". // Make sure "/" only matches when viewLocation is "/".
@ -26,5 +48,6 @@ refstackApp.controller('headerController',
} }
} }
return false; return false;
}; }
}]); }
})();

View File

@ -9,7 +9,7 @@ describe('Refstack controllers', function () {
$provide.constant('refstackApiUrl', fakeApiUrl); $provide.constant('refstackApiUrl', fakeApiUrl);
}); });
module('refstackApp'); module('refstackApp');
inject(function(_$httpBackend_){ inject(function(_$httpBackend_) {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
}); });
$httpBackend.whenGET(fakeApiUrl + '/profile').respond(401); $httpBackend.whenGET(fakeApiUrl + '/profile').respond(401);
@ -17,45 +17,41 @@ describe('Refstack controllers', function () {
.respond('<div>mock template</div>'); .respond('<div>mock template</div>');
}); });
describe('headerController', function () { describe('HeaderController', function () {
var scope, $location; var $location, ctrl;
beforeEach(inject(function ($rootScope, $controller, _$location_) { beforeEach(inject(function ($controller, _$location_) {
scope = $rootScope.$new();
$location = _$location_; $location = _$location_;
$controller('headerController', {$scope: scope}); ctrl = $controller('HeaderController', {});
})); }));
it('should set "navbarCollapsed" to true', function () { it('should set "navbarCollapsed" to true', function () {
expect(scope.navbarCollapsed).toBe(true); expect(ctrl.navbarCollapsed).toBe(true);
}); });
it('should have a function to check if the URL path is active', it('should have a function to check if the URL path is active',
function () { function () {
$location.path('/'); $location.path('/');
expect($location.path()).toBe('/'); expect($location.path()).toBe('/');
expect(scope.isActive('/')).toBe(true); expect(ctrl.isActive('/')).toBe(true);
expect(scope.isActive('/about')).toBe(false); expect(ctrl.isActive('/about')).toBe(false);
$location.path('/results?cpid=123&foo=bar'); $location.path('/results?cpid=123&foo=bar');
expect($location.path()).toBe('/results?cpid=123&foo=bar'); expect($location.path()).toBe('/results?cpid=123&foo=bar');
expect(scope.isActive('/results')).toBe(true); expect(ctrl.isActive('/results')).toBe(true);
}); });
}); });
describe('capabilitiesController', function () { describe('CapabilitiesController', function () {
var scope; var ctrl;
beforeEach(inject(function ($rootScope, $controller) { beforeEach(inject(function ($controller) {
scope = $rootScope.$new(); ctrl = $controller('CapabilitiesController', {});
$controller('capabilitiesController', {$scope: scope});
})); }));
it('should set default states', function () { it('should set default states', function () {
expect(scope.hideAchievements).toBe(true); expect(ctrl.target).toBe('platform');
expect(scope.hideTests).toBe(true); expect(ctrl.status).toEqual({
expect(scope.target).toBe('platform');
expect(scope.status).toEqual({
required: true, advisory: false, required: true, advisory: false,
deprecated: false, removed: false deprecated: false, removed: false
}); });
@ -84,24 +80,24 @@ describe('Refstack controllers', function () {
'/capabilities/2015.04.json').respond(fakeCaps); '/capabilities/2015.04.json').respond(fakeCaps);
$httpBackend.flush(); $httpBackend.flush();
// The version list should be sorted latest first. // The version list should be sorted latest first.
expect(scope.versionList).toEqual(['2015.04.json', expect(ctrl.versionList).toEqual(['2015.04.json',
'2015.03.json']); '2015.03.json']);
expect(scope.capabilities).toEqual(fakeCaps); expect(ctrl.capabilities).toEqual(fakeCaps);
var expectedTemplate = 'components/capabilities/partials/' + var expectedTemplate = 'components/capabilities/partials/' +
'capabilityDetailsV1.3.html'; 'capabilityDetailsV1.3.html';
expect(scope.detailsTemplate).toEqual(expectedTemplate); expect(ctrl.detailsTemplate).toEqual(expectedTemplate);
var expectedTargetCaps = { var expectedTargetCaps = {
'cap_id_1': 'required', 'cap_id_1': 'required',
'cap_id_2': 'advisory', 'cap_id_2': 'advisory',
'cap_id_3': 'deprecated', 'cap_id_3': 'deprecated',
'cap_id_4': 'removed' 'cap_id_4': 'removed'
}; };
expect(scope.targetCapabilities).toEqual(expectedTargetCaps); expect(ctrl.targetCapabilities).toEqual(expectedTargetCaps);
}); });
it('should have a function to check if a capability status is selected', it('should have a function to check if a capability status is selected',
function () { function () {
scope.targetCapabilities = { ctrl.targetCapabilities = {
'cap_id_1': 'required', 'cap_id_1': 'required',
'cap_id_2': 'advisory', 'cap_id_2': 'advisory',
'cap_id_3': 'deprecated', 'cap_id_3': 'deprecated',
@ -109,12 +105,12 @@ describe('Refstack controllers', function () {
}; };
// Expect only the required capability to return true. // Expect only the required capability to return true.
expect(scope.filterStatus({'id': 'cap_id_1'})).toBe(true); expect(ctrl.filterStatus({'id': 'cap_id_1'})).toBe(true);
expect(scope.filterStatus({'id': 'cap_id_2'})).toBe(false); expect(ctrl.filterStatus({'id': 'cap_id_2'})).toBe(false);
expect(scope.filterStatus({'id': 'cap_id_3'})).toBe(false); expect(ctrl.filterStatus({'id': 'cap_id_3'})).toBe(false);
expect(scope.filterStatus({'id': 'cap_id_4'})).toBe(false); expect(ctrl.filterStatus({'id': 'cap_id_4'})).toBe(false);
scope.status = { ctrl.status = {
required: true, required: true,
advisory: true, advisory: true,
deprecated: true, deprecated: true,
@ -122,10 +118,10 @@ describe('Refstack controllers', function () {
}; };
// Every capability should return true now. // Every capability should return true now.
expect(scope.filterStatus({'id': 'cap_id_1'})).toBe(true); expect(ctrl.filterStatus({'id': 'cap_id_1'})).toBe(true);
expect(scope.filterStatus({'id': 'cap_id_2'})).toBe(true); expect(ctrl.filterStatus({'id': 'cap_id_2'})).toBe(true);
expect(scope.filterStatus({'id': 'cap_id_3'})).toBe(true); expect(ctrl.filterStatus({'id': 'cap_id_3'})).toBe(true);
expect(scope.filterStatus({'id': 'cap_id_4'})).toBe(true); expect(ctrl.filterStatus({'id': 'cap_id_4'})).toBe(true);
}); });
it('should have a function to get the length of an object/dict', it('should have a function to get the length of an object/dict',
@ -138,12 +134,12 @@ describe('Refstack controllers', function () {
'idempotent_id': 'id-5678' 'idempotent_id': 'id-5678'
} }
}; };
expect(scope.getObjectLength(testObject)).toBe(2); expect(ctrl.getObjectLength(testObject)).toBe(2);
}); });
}); });
describe('resultsController', function () { describe('resultsController', function () {
var scope; var scope, ctrl;
var fakeResponse = { var fakeResponse = {
'pagination': {'current_page': 1, 'total_pages': 2}, 'pagination': {'current_page': 1, 'total_pages': 2},
'results': [{ 'results': [{
@ -155,7 +151,7 @@ describe('Refstack controllers', function () {
beforeEach(inject(function ($rootScope, $controller) { beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
$controller('resultsController', {$scope: scope}); ctrl = $controller('ResultsController', {$scope: scope});
})); }));
it('should fetch the first page of results with proper URL args', it('should fetch the first page of results with proper URL args',
@ -164,46 +160,46 @@ describe('Refstack controllers', function () {
$httpBackend.expectGET(fakeApiUrl + '/results?page=1') $httpBackend.expectGET(fakeApiUrl + '/results?page=1')
.respond(fakeResponse); .respond(fakeResponse);
$httpBackend.flush(); $httpBackend.flush();
expect(scope.data).toEqual(fakeResponse); expect(ctrl.data).toEqual(fakeResponse);
expect(scope.currentPage).toBe(1); expect(ctrl.currentPage).toBe(1);
// Simulate the user adding date filters. // Simulate the user adding date filters.
scope.startDate = new Date('2015-03-10T11:51:00'); ctrl.startDate = new Date('2015-03-10T11:51:00');
scope.endDate = new Date('2015-04-10T11:51:00'); ctrl.endDate = new Date('2015-04-10T11:51:00');
scope.update(); ctrl.update();
$httpBackend.expectGET(fakeApiUrl + $httpBackend.expectGET(fakeApiUrl +
'/results?page=1' + '/results?page=1' +
'&start_date=2015-03-10 00:00:00' + '&start_date=2015-03-10 00:00:00' +
'&end_date=2015-04-10 23:59:59') '&end_date=2015-04-10 23:59:59')
.respond(fakeResponse); .respond(fakeResponse);
$httpBackend.flush(); $httpBackend.flush();
expect(scope.data).toEqual(fakeResponse); expect(ctrl.data).toEqual(fakeResponse);
expect(scope.currentPage).toBe(1); expect(ctrl.currentPage).toBe(1);
}); });
it('should set an error when results cannot be retrieved', function () { it('should set an error when results cannot be retrieved', function () {
$httpBackend.expectGET(fakeApiUrl + '/results?page=1').respond(404, $httpBackend.expectGET(fakeApiUrl + '/results?page=1').respond(404,
{'detail': 'Not Found'}); {'detail': 'Not Found'});
$httpBackend.flush(); $httpBackend.flush();
expect(scope.data).toBe(null); expect(ctrl.data).toBe(null);
expect(scope.error).toEqual('Error retrieving results listing ' + expect(ctrl.error).toEqual('Error retrieving results listing ' +
'from server: {"detail":"Not Found"}'); 'from server: {"detail":"Not Found"}');
expect(scope.totalItems).toBe(0); expect(ctrl.totalItems).toBe(0);
expect(scope.showError).toBe(true); expect(ctrl.showError).toBe(true);
}); });
it('should have an function to clear filters and update the view', it('should have an function to clear filters and update the view',
function () { function () {
scope.startDate = 'some date'; ctrl.startDate = 'some date';
scope.endDate = 'some other date'; ctrl.endDate = 'some other date';
scope.clearFilters(); ctrl.clearFilters();
expect(scope.startDate).toBe(null); expect(ctrl.startDate).toBe(null);
expect(scope.endDate).toBe(null); expect(ctrl.endDate).toBe(null);
}); });
}); });
describe('resultsReportController', function () { describe('ResultsReportController', function () {
var scope, stateparams; var stateparams, ctrl;
var fakeResultResponse = {'results': ['test_id_1']}; var fakeResultResponse = {'results': ['test_id_1']};
var fakeCapabilityResponse = { var fakeCapabilityResponse = {
'platform': {'required': ['compute']}, 'platform': {'required': ['compute']},
@ -224,11 +220,11 @@ describe('Refstack controllers', function () {
} }
}; };
beforeEach(inject(function ($rootScope, $controller) { beforeEach(inject(function ($controller) {
stateparams = {testID: 1234}; stateparams = {testID: 1234};
scope = $rootScope.$new(); ctrl = $controller('ResultsReportController',
$controller('resultsReportController', {$stateParams: stateparams}
{$scope: scope, $stateParams: stateparams}); );
})); }));
it('should make all necessary API requests to get results ' + it('should make all necessary API requests to get results ' +
@ -242,18 +238,18 @@ describe('Refstack controllers', function () {
$httpBackend.expectGET(fakeApiUrl + $httpBackend.expectGET(fakeApiUrl +
'/capabilities/2015.04.json').respond(fakeCapabilityResponse); '/capabilities/2015.04.json').respond(fakeCapabilityResponse);
$httpBackend.flush(); $httpBackend.flush();
expect(scope.resultsData).toEqual(fakeResultResponse); expect(ctrl.resultsData).toEqual(fakeResultResponse);
// The version list should be sorted latest first. // The version list should be sorted latest first.
expect(scope.versionList).toEqual(['2015.04.json', expect(ctrl.versionList).toEqual(['2015.04.json',
'2015.03.json']); '2015.03.json']);
expect(scope.capabilityData).toEqual(fakeCapabilityResponse); expect(ctrl.capabilityData).toEqual(fakeCapabilityResponse);
expect(scope.schemaVersion).toEqual('1.2'); expect(ctrl.schemaVersion).toEqual('1.2');
}); });
it('should have a method that creates an object containing each ' + it('should have a method that creates an object containing each ' +
'relevant capability and its highest priority status', 'relevant capability and its highest priority status',
function () { function () {
scope.capabilityData = { ctrl.capabilityData = {
'schema': '1.3', 'schema': '1.3',
'platform': {'required': ['compute', 'object']}, 'platform': {'required': ['compute', 'object']},
'components': { 'components': {
@ -276,16 +272,16 @@ describe('Refstack controllers', function () {
'cap_id_2': 'required', 'cap_id_2': 'required',
'cap_id_3': 'advisory' 'cap_id_3': 'advisory'
}; };
expect(scope.getTargetCapabilities()).toEqual(expected); expect(ctrl.getTargetCapabilities()).toEqual(expected);
}); });
it('should be able to sort the results into a capability object for ' + it('should be able to sort the results into a capability object for ' +
'schema version 1.2', 'schema version 1.2',
function () { function () {
scope.resultsData = fakeResultResponse; ctrl.resultsData = fakeResultResponse;
scope.capabilityData = fakeCapabilityResponse; ctrl.capabilityData = fakeCapabilityResponse;
scope.schemaVersion = '1.2'; ctrl.schemaVersion = '1.2';
scope.buildCapabilitiesObject(); ctrl.buildCapabilitiesObject();
var expectedCapsObject = { var expectedCapsObject = {
'required': { 'required': {
'caps': [{ 'caps': [{
@ -305,16 +301,16 @@ describe('Refstack controllers', function () {
'removed': {'caps': [], 'count': 0, 'passedCount': 0, 'removed': {'caps': [], 'count': 0, 'passedCount': 0,
'flagFailCount': 0, 'flagPassCount': 0} 'flagFailCount': 0, 'flagPassCount': 0}
}; };
expect(scope.caps).toEqual(expectedCapsObject); expect(ctrl.caps).toEqual(expectedCapsObject);
expect(scope.requiredPassPercent).toEqual(50); expect(ctrl.requiredPassPercent).toEqual(50);
expect(scope.nonFlagPassCount).toEqual(0); expect(ctrl.nonFlagPassCount).toEqual(0);
}); });
it('should be able to sort the results into a capability object for ' + it('should be able to sort the results into a capability object for ' +
'schema version 1.3', 'schema version 1.3',
function () { function () {
scope.resultsData = fakeResultResponse; ctrl.resultsData = fakeResultResponse;
scope.capabilityData = { ctrl.capabilityData = {
'platform': {'required': ['compute']}, 'platform': {'required': ['compute']},
'schema': '1.3', 'schema': '1.3',
'components': { 'components': {
@ -333,7 +329,7 @@ describe('Refstack controllers', function () {
'action': 'foo', 'action': 'foo',
'date': '2015-03-24', 'date': '2015-03-24',
'reason': 'bar' 'reason': 'bar'
}, },
'idempotent_id': 'id-1234' 'idempotent_id': 'id-1234'
}, },
'test_id_2': { 'test_id_2': {
@ -343,8 +339,8 @@ describe('Refstack controllers', function () {
} }
} }
}; };
scope.schemaVersion = '1.3'; ctrl.schemaVersion = '1.3';
scope.buildCapabilitiesObject(); ctrl.buildCapabilitiesObject();
var expectedCapsObject = { var expectedCapsObject = {
'required': { 'required': {
'caps': [{ 'caps': [{
@ -364,9 +360,9 @@ describe('Refstack controllers', function () {
'removed': {'caps': [], 'count': 0, 'passedCount': 0, 'removed': {'caps': [], 'count': 0, 'passedCount': 0,
'flagFailCount': 0, 'flagPassCount': 0} 'flagFailCount': 0, 'flagPassCount': 0}
}; };
expect(scope.caps).toEqual(expectedCapsObject); expect(ctrl.caps).toEqual(expectedCapsObject);
expect(scope.requiredPassPercent).toEqual(50); expect(ctrl.requiredPassPercent).toEqual(50);
expect(scope.nonFlagPassCount).toEqual(0); expect(ctrl.nonFlagPassCount).toEqual(0);
}); });
it('should have a method to determine if a test is flagged', it('should have a method to determine if a test is flagged',
@ -374,30 +370,31 @@ describe('Refstack controllers', function () {
var capObj = {'flagged': [ 'test1'], var capObj = {'flagged': [ 'test1'],
'tests': ['test1', 'test2']}; 'tests': ['test1', 'test2']};
scope.schemaVersion = '1.2'; ctrl.schemaVersion = '1.2';
expect(scope.isTestFlagged('test1', capObj)).toEqual(true); expect(ctrl.isTestFlagged('test1', capObj)).toEqual(true);
expect(scope.isTestFlagged('test2', capObj)).toEqual(false); expect(ctrl.isTestFlagged('test2', capObj)).toEqual(false);
capObj = {'tests': { capObj = {
'test1': { 'tests': {
'flagged': { 'test1': {
'action': 'foo', 'flagged': {
'date': '2015-03-24', 'action': 'foo',
'reason': 'bar' 'date': '2015-03-24',
}, 'reason': 'bar'
'idempotent_id': 'id-1234' },
}, 'idempotent_id': 'id-1234'
'test2': { },
'idempotent_id': 'id-5678' 'test2': {
} 'idempotent_id': 'id-5678'
} }
}; }
};
scope.schemaVersion = '1.3'; ctrl.schemaVersion = '1.3';
expect(scope.isTestFlagged('test1', capObj)).toBeTruthy(); expect(ctrl.isTestFlagged('test1', capObj)).toBeTruthy();
expect(scope.isTestFlagged('test2', capObj)).toBeFalsy(); expect(ctrl.isTestFlagged('test2', capObj)).toBeFalsy();
expect(scope.isTestFlagged('test2', null)).toEqual(false); expect(ctrl.isTestFlagged('test2', null)).toEqual(false);
}); });
it('should have a method to get the reason a flagged test is flagged', it('should have a method to get the reason a flagged test is flagged',
@ -405,27 +402,28 @@ describe('Refstack controllers', function () {
var capObj = {'flagged': [ 'test1'], var capObj = {'flagged': [ 'test1'],
'tests': ['test1', 'test2']}; 'tests': ['test1', 'test2']};
scope.schemaVersion = '1.2'; ctrl.schemaVersion = '1.2';
expect(scope.getFlaggedReason('test1', capObj)).toEqual( expect(ctrl.getFlaggedReason('test1', capObj)).toEqual(
'DefCore has flagged this test.'); 'DefCore has flagged this test.');
// Check that non-flagged test returns empty string. // Check that non-flagged test returns empty string.
expect(scope.getFlaggedReason('test2', capObj)).toEqual(''); expect(ctrl.getFlaggedReason('test2', capObj)).toEqual('');
capObj = {'tests': { capObj = {
'test1': { 'tests': {
'flagged': { 'test1': {
'action': 'foo', 'flagged': {
'date': '2015-03-24', 'action': 'foo',
'reason': 'bar' 'date': '2015-03-24',
}, 'reason': 'bar'
'idempotent_id': 'id-1234' },
} 'idempotent_id': 'id-1234'
} }
}; }
};
scope.schemaVersion = '1.3'; ctrl.schemaVersion = '1.3';
expect(scope.getFlaggedReason('test1', capObj)).toEqual('bar'); expect(ctrl.getFlaggedReason('test1', capObj)).toEqual('bar');
}); });
it('should have a method to determine whether a capability should ' + it('should have a method to determine whether a capability should ' +
@ -445,23 +443,23 @@ describe('Refstack controllers', function () {
}]; }];
// Check that all capabilities are shown by default. // Check that all capabilities are shown by default.
expect(scope.isCapabilityShown(caps[0])).toEqual(true); expect(ctrl.isCapabilityShown(caps[0])).toEqual(true);
expect(scope.isCapabilityShown(caps[1])).toEqual(true); expect(ctrl.isCapabilityShown(caps[1])).toEqual(true);
// Check that only capabilities with passed tests are shown. // Check that only capabilities with passed tests are shown.
scope.testStatus = 'passed'; ctrl.testStatus = 'passed';
expect(scope.isCapabilityShown(caps[0])).toEqual(true); expect(ctrl.isCapabilityShown(caps[0])).toEqual(true);
expect(scope.isCapabilityShown(caps[1])).toEqual(false); expect(ctrl.isCapabilityShown(caps[1])).toEqual(false);
// Check that only capabilities with passed tests are shown. // Check that only capabilities with passed tests are shown.
scope.testStatus = 'not passed'; ctrl.testStatus = 'not passed';
expect(scope.isCapabilityShown(caps[0])).toEqual(false); expect(ctrl.isCapabilityShown(caps[0])).toEqual(false);
expect(scope.isCapabilityShown(caps[1])).toEqual(true); expect(ctrl.isCapabilityShown(caps[1])).toEqual(true);
// Check that only capabilities with flagged tests are shown. // Check that only capabilities with flagged tests are shown.
scope.testStatus = 'flagged'; ctrl.testStatus = 'flagged';
expect(scope.isCapabilityShown(caps[0])).toEqual(true); expect(ctrl.isCapabilityShown(caps[0])).toEqual(true);
expect(scope.isCapabilityShown(caps[1])).toEqual(false); expect(ctrl.isCapabilityShown(caps[1])).toEqual(false);
}); });
it('should have a method to determine whether a test should be shown', it('should have a method to determine whether a test should be shown',
@ -473,13 +471,13 @@ describe('Refstack controllers', function () {
'notPassedFlagged': [] 'notPassedFlagged': []
}; };
expect(scope.isTestShown('test_id_1', cap)).toEqual(true); expect(ctrl.isTestShown('test_id_1', cap)).toEqual(true);
scope.testStatus = 'passed'; ctrl.testStatus = 'passed';
expect(scope.isTestShown('test_id_1', cap)).toEqual(true); expect(ctrl.isTestShown('test_id_1', cap)).toEqual(true);
scope.testStatus = 'not passed'; ctrl.testStatus = 'not passed';
expect(scope.isTestShown('test_id_1', cap)).toEqual(false); expect(ctrl.isTestShown('test_id_1', cap)).toEqual(false);
scope.testStatus = 'flagged'; ctrl.testStatus = 'flagged';
expect(scope.isTestShown('test_id_1', cap)).toEqual(true); expect(ctrl.isTestShown('test_id_1', cap)).toEqual(true);
}); });
it('should have a method to determine how many tests in a ' + it('should have a method to determine how many tests in a ' +
@ -493,48 +491,48 @@ describe('Refstack controllers', function () {
}; };
// Should return the count of all tests. // Should return the count of all tests.
expect(scope.getCapabilityTestCount(cap)).toEqual(7); expect(ctrl.getCapabilityTestCount(cap)).toEqual(7);
// Should return the count of passed tests. // Should return the count of passed tests.
scope.testStatus = 'passed'; ctrl.testStatus = 'passed';
expect(scope.getCapabilityTestCount(cap)).toEqual(3); expect(ctrl.getCapabilityTestCount(cap)).toEqual(3);
// Should return the count of failed tests. // Should return the count of failed tests.
scope.testStatus = 'not passed'; ctrl.testStatus = 'not passed';
expect(scope.getCapabilityTestCount(cap)).toEqual(4); expect(ctrl.getCapabilityTestCount(cap)).toEqual(4);
// Should return the count of flagged tests. // Should return the count of flagged tests.
scope.testStatus = 'flagged'; ctrl.testStatus = 'flagged';
expect(scope.getCapabilityTestCount(cap)).toEqual(3); expect(ctrl.getCapabilityTestCount(cap)).toEqual(3);
}); });
it('should have a method to determine how many tests in a status ' + it('should have a method to determine how many tests in a status ' +
'belong under the current test filter', 'belong under the current test filter',
function () { function () {
scope.caps = {'required': {'caps': [], 'count': 10, ctrl.caps = {'required': {'caps': [], 'count': 10,
'passedCount': 6, 'flagFailCount': 3, 'passedCount': 6, 'flagFailCount': 3,
'flagPassCount': 2}}; 'flagPassCount': 2}};
// Should return the count of all tests (count). // Should return the count of all tests (count).
expect(scope.getStatusTestCount('required')).toEqual(10); expect(ctrl.getStatusTestCount('required')).toEqual(10);
// Should return the count of passed tests (passedCount). // Should return the count of passed tests (passedCount).
scope.testStatus = 'passed'; ctrl.testStatus = 'passed';
expect(scope.getStatusTestCount('required')).toEqual(6); expect(ctrl.getStatusTestCount('required')).toEqual(6);
// Should return the count of failed tests // Should return the count of failed tests
// (count - passedCount). // (count - passedCount).
scope.testStatus = 'not passed'; ctrl.testStatus = 'not passed';
expect(scope.getStatusTestCount('required')).toEqual(4); expect(ctrl.getStatusTestCount('required')).toEqual(4);
// Should return the count of flagged tests // Should return the count of flagged tests
// (flagFailCount + flagPassCount). // (flagFailCount + flagPassCount).
scope.testStatus = 'flagged'; ctrl.testStatus = 'flagged';
expect(scope.getStatusTestCount('required')).toEqual(5); expect(ctrl.getStatusTestCount('required')).toEqual(5);
// Test when caps has not been set yet. // Test when caps has not been set yet.
scope.caps = null; ctrl.caps = null;
expect(scope.getStatusTestCount('required')).toEqual(-1); expect(ctrl.getStatusTestCount('required')).toEqual(-1);
}); });
it('should have a method to open a modal for the full passed test list', it('should have a method to open a modal for the full passed test list',
@ -544,71 +542,63 @@ describe('Refstack controllers', function () {
modal = $modal; modal = $modal;
}); });
spyOn(modal, 'open'); spyOn(modal, 'open');
scope.openFullTestListModal(); ctrl.openFullTestListModal();
expect(modal.open).toHaveBeenCalled(); expect(modal.open).toHaveBeenCalled();
}); });
}); });
describe('fullTestListModalController', function () { describe('FullTestListModalController', function () {
var scope; var modalInstance, ctrl;
var modalInstance;
beforeEach(inject(function ($rootScope, $controller) { beforeEach(inject(function ($controller) {
scope = $rootScope.$new();
modalInstance = { modalInstance = {
dismiss: jasmine.createSpy('modalInstance.dismiss') dismiss: jasmine.createSpy('modalInstance.dismiss')
}; };
$controller('fullTestListModalController', { ctrl = $controller('FullTestListModalController',
$scope: scope, {$modalInstance: modalInstance, tests: ['t1', 't2']}
$modalInstance: modalInstance, );
tests: ['t1', 't2']
});
})); }));
it('should set a scope variable to the passed in tests', function () { it('should set a scope variable to the passed in tests', function () {
expect(scope.tests).toEqual(['t1', 't2']); expect(ctrl.tests).toEqual(['t1', 't2']);
}); });
it('should have a method to close the modal', it('should have a method to close the modal',
function () { function () {
scope.close(); ctrl.close();
expect(modalInstance.dismiss).toHaveBeenCalledWith('exit'); expect(modalInstance.dismiss).toHaveBeenCalledWith('exit');
}); });
it('should have a method to convert the tests to a string', it('should have a method to convert the tests to a string',
function () { function () {
scope.tests = ['t2', 't1', 't3']; ctrl.tests = ['t2', 't1', 't3'];
var expectedString = 't1\nt2\nt3'; var expectedString = 't1\nt2\nt3';
expect(scope.getTestListString()).toEqual(expectedString); expect(ctrl.getTestListString()).toEqual(expectedString);
}); });
}); });
describe('testRaiseAlertModalController', function() { describe('TestRaiseAlertModalController', function() {
var data; var data, modalInstance, ctrl;
var scope, modalInstance;
data = { data = {
mode: 'success', mode: 'success',
title: '', title: '',
text: 'operation successful' text: 'operation successful'
}; };
beforeEach(inject(function ($rootScope, $controller) { beforeEach(inject(function ($controller) {
scope = $rootScope.$new();
modalInstance = { modalInstance = {
dismiss: jasmine.createSpy('modalInstance.dismiss'), dismiss: jasmine.createSpy('modalInstance.dismiss'),
close: jasmine.createSpy('modalInstance.close') close: jasmine.createSpy('modalInstance.close')
}; };
$controller('raiseAlertModalController', { ctrl = $controller('RaiseAlertModalController',
$scope: scope, {$modalInstance: modalInstance, data: data}
$modalInstance: modalInstance, );
data: data
});
})); }));
it('should close', it('should close',
function () { function () {
scope.close(); ctrl.close();
expect(modalInstance.close).toHaveBeenCalledWith(); expect(modalInstance.close).toHaveBeenCalledWith();
}); });
}); });