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 @@
/*
* 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.
*/
(function () {
'use strict';
/** Main app module where application dependencies are listed. */ /** Main app module where application dependencies are listed. */
var refstackApp = angular.module('refstackApp', [ angular
'ui.router', 'ui.bootstrap', 'cgBusy', 'ngResource', 'angular-confirm']); .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 * Handle application routing. Specific templates and controllers will be
* used based on the URL route. * used based on the URL route.
*/ */
refstackApp.config([ function configureRoutes($stateProvider, $urlRouterProvider) {
'$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
'use strict';
$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
.module('refstackApp')
.run(setup);
setup.$inject = [
'$http', '$rootScope', '$window', '$state', 'refstackApiUrl'
];
/** /**
* Injections in $rootscope * Set up the app with injections into $rootscope. This is mainly for auth
* functions.
*/ */
function setup($http, $rootScope, $window, $state, refstackApiUrl) {
refstackApp.run(['$http', '$rootScope', '$window', '$state', 'refstackApiUrl',
function($http, $rootScope, $window, $state, refstackApiUrl) {
'use strict';
/** /**
* 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,24 +129,31 @@ 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
.element(document)
.ready(loadConfig);
/** /**
* Load config and start up the angular application. * Load config and start up the angular application.
*/ */
angular.element(document).ready(function () { function loadConfig() {
'use strict';
var $http = angular.injector(['ng']).get('$http'); var $http = angular.injector(['ng']).get('$http');
/**
* Store config variables as constants, and start the app.
*/
function startApp(config) { function startApp(config) {
// Add config options as constants. // Add config options as constants.
for (var key in config) { angular.forEach(config, function(value, key) {
angular.module('refstackApp').constant(key, config[key]); angular.module('refstackApp').constant(key, value);
} });
angular.bootstrap(document, ['refstackApp']); angular.bootstrap(document, ['refstackApp']);
} }
@ -123,4 +162,5 @@ angular.element(document).ready(function () {
}).error(function () { }).error(function () {
startApp({}); startApp({});
}); });
}); }
})();

View File

@ -1,17 +1,31 @@
/*
* 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.
*/
(function () {
'use strict';
angular
.module('refstackApp')
.controller('AuthFailureController', AuthFailureController);
AuthFailureController.$inject = ['$stateParams', '$state', 'raiseAlert'];
/** /**
* Refstack Auth Failure Controller * Refstack Auth Failure Controller
* This controller handles messages from Refstack API if user auth fails. * This controller handles messages from Refstack API if user auth fails.
*/ */
function AuthFailureController($stateParams, $state, raiseAlert) {
var refstackApp = angular.module('refstackApp'); raiseAlert('danger', 'Authentication Failure:', $stateParams.message);
refstackApp.controller('authFailureController',
[
'$stateParams', '$state', 'raiseAlert',
function($stateParams, $state, raiseAlert) {
'use strict';
raiseAlert('danger', 'Authentication Failure:',
$stateParams.message);
$state.go('home'); $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,26 +1,45 @@
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.
*/
(function () {
'use strict';
angular
.module('refstackApp')
.controller('CapabilitiesController', CapabilitiesController);
CapabilitiesController.$inject = ['$http', 'refstackApiUrl'];
/** /**
* Refstack Capabilities Controller * RefStack Capabilities Controller
* This controller is for the '/capabilities' page where a user can browse * This controller is for the '/capabilities' page where a user can browse
* through tests belonging to DefCore-defined capabilities. * through tests belonging to DefCore-defined capabilities.
*/ */
refstackApp.controller('capabilitiesController', function CapabilitiesController($http, refstackApiUrl) {
['$scope', '$http', 'refstackApiUrl', var ctrl = this;
function ($scope, $http, refstackApiUrl) {
'use strict';
/** Whether to hide/collapse the achievements for each capability. */ ctrl.getVersionList = getVersionList;
$scope.hideAchievements = true; ctrl.update = update;
ctrl.updateTargetCapabilities = updateTargetCapabilities;
/** Whether to hide/collapse the tests for each capability. */ ctrl.filterStatus = filterStatus;
$scope.hideTests = true; ctrl.getObjectLength = getObjectLength;
/** The target OpenStack marketing program to show capabilities for. */ /** The target OpenStack marketing program to show capabilities for. */
$scope.target = 'platform'; ctrl.target = 'platform';
/** The various possible capability statuses. */ /** The various possible capability statuses. */
$scope.status = { ctrl.status = {
required: true, required: true,
advisory: false, advisory: false,
deprecated: false, deprecated: false,
@ -31,7 +50,7 @@ refstackApp.controller('capabilitiesController',
* The template to load for displaying capability details. The value * The template to load for displaying capability details. The value
* of this depends on the schema version of the capabilities file. * of this depends on the schema version of the capabilities file.
*/ */
$scope.detailsTemplate = null; ctrl.detailsTemplate = null;
/** /**
* Retrieve an array of available capability files from the Refstack * Retrieve an array of available capability files from the Refstack
@ -41,42 +60,42 @@ refstackApp.controller('capabilitiesController',
* call, the function to update the capabilities is called. * call, the function to update the capabilities is called.
* Sample API return array: ["2015.03.json", "2015.04.json"] * Sample API return array: ["2015.03.json", "2015.04.json"]
*/ */
$scope.getVersionList = function () { function getVersionList() {
var content_url = refstackApiUrl + '/capabilities'; var content_url = refstackApiUrl + '/capabilities';
$scope.versionsRequest = ctrl.versionsRequest =
$http.get(content_url).success(function (data) { $http.get(content_url).success(function (data) {
$scope.versionList = data.sort().reverse(); ctrl.versionList = data.sort().reverse();
$scope.version = $scope.versionList[0]; ctrl.version = ctrl.versionList[0];
$scope.update(); ctrl.update();
}).error(function (error) { }).error(function (error) {
$scope.showError = true; ctrl.showError = true;
$scope.error = 'Error retrieving version list: ' + ctrl.error = 'Error retrieving version list: ' +
JSON.stringify(error); angular.toJson(error);
}); });
}; }
/** /**
* This will contact the Refstack API server to retrieve the JSON * This will contact the Refstack API server to retrieve the JSON
* content of the capability file corresponding to the selected * content of the capability file corresponding to the selected
* version. * version.
*/ */
$scope.update = function () { function update() {
var content_url = refstackApiUrl + '/capabilities/' + var content_url = refstackApiUrl + '/capabilities/' +
$scope.version; ctrl.version;
$scope.capsRequest = ctrl.capsRequest =
$http.get(content_url).success(function (data) { $http.get(content_url).success(function (data) {
$scope.capabilities = data; ctrl.capabilities = data;
$scope.detailsTemplate = 'components/capabilities/' + ctrl.detailsTemplate = 'components/capabilities/' +
'partials/capabilityDetailsV' + 'partials/capabilityDetailsV' +
data.schema + '.html'; data.schema + '.html';
$scope.updateTargetCapabilities(); ctrl.updateTargetCapabilities();
}).error(function (error) { }).error(function (error) {
$scope.showError = true; ctrl.showError = true;
$scope.capabilities = null; ctrl.capabilities = null;
$scope.error = 'Error retrieving capabilities: ' + ctrl.error = 'Error retrieving capabilities: ' +
JSON.stringify(error); angular.toJson(error);
}); });
}; }
/** /**
* This will update the scope's 'targetCapabilities' object with * This will update the scope's 'targetCapabilities' object with
@ -84,17 +103,16 @@ refstackApp.controller('capabilitiesController',
* (programs typically correspond to 'components' in the DefCore * (programs typically correspond to 'components' in the DefCore
* schema). Each capability will have its status mapped to it. * schema). Each capability will have its status mapped to it.
*/ */
$scope.updateTargetCapabilities = function () { function updateTargetCapabilities() {
$scope.targetCapabilities = {}; ctrl.targetCapabilities = {};
var components = $scope.capabilities.components; var components = ctrl.capabilities.components;
var targetCaps = $scope.targetCapabilities; var targetCaps = ctrl.targetCapabilities;
// The 'platform' target is comprised of multiple components, so // The 'platform' target is comprised of multiple components, so
// we need to get the capabilities belonging to each of its // we need to get the capabilities belonging to each of its
// components. // components.
if ($scope.target === 'platform') { if (ctrl.target === 'platform') {
var platform_components = var platform_components = ctrl.capabilities.platform.required;
$scope.capabilities.platform.required;
// This will contain status priority values, where lower // This will contain status priority values, where lower
// values mean higher priorities. // values mean higher priorities.
@ -130,16 +148,14 @@ refstackApp.controller('capabilitiesController',
}); });
} }
else { else {
angular.forEach(components[$scope.target], angular.forEach(components[ctrl.target],
function (caps, status) { function (caps, status) {
angular.forEach(caps, function(cap) { angular.forEach(caps, function(cap) {
targetCaps[cap] = status; targetCaps[cap] = status;
}); });
}); });
} }
}; }
$scope.getVersionList();
/** /**
* This filter will check if a capability's status corresponds * This filter will check if a capability's status corresponds
@ -148,17 +164,17 @@ refstackApp.controller('capabilitiesController',
* @param {Object} capability * @param {Object} capability
* @returns {Boolean} True if capability's status is selected * @returns {Boolean} True if capability's status is selected
*/ */
$scope.filterStatus = function (capability) { function filterStatus(capability) {
var caps = $scope.targetCapabilities; var caps = ctrl.targetCapabilities;
return ($scope.status.required && return (ctrl.status.required &&
caps[capability.id] === 'required') || caps[capability.id] === 'required') ||
($scope.status.advisory && (ctrl.status.advisory &&
caps[capability.id] === 'advisory') || caps[capability.id] === 'advisory') ||
($scope.status.deprecated && (ctrl.status.deprecated &&
caps[capability.id] === 'deprecated') || caps[capability.id] === 'deprecated') ||
($scope.status.removed && (ctrl.status.removed &&
caps[capability.id] === 'removed'); caps[capability.id] === 'removed');
}; }
/** /**
* This function will get the length of an Object/dict based on * This function will get the length of an Object/dict based on
@ -166,7 +182,10 @@ refstackApp.controller('capabilitiesController',
* @param {Object} object * @param {Object} object
* @returns {Number} length of object * @returns {Number} length of object
*/ */
$scope.getObjectLength = function (object) { function getObjectLength(object) {
return Object.keys(object).length; 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>

View File

@ -1,34 +1,71 @@
/*
*
* 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.
*/
(function () {
'use strict';
angular
.module('refstackApp')
.factory('PubKeys', PubKeys);
PubKeys.$inject = ['$resource', 'refstackApiUrl'];
/** /**
* Refstack User Profile Controller * This is a provider for the user's uploaded public keys.
*/
function PubKeys($resource, refstackApiUrl) {
return $resource(refstackApiUrl + '/profile/pubkeys/:id', null, null);
}
angular
.module('refstackApp')
.controller('ProfileController', ProfileController);
ProfileController.$inject = [
'$scope', '$http', 'refstackApiUrl', 'PubKeys',
'$modal', 'raiseAlert', '$state'
];
/**
* RefStack Profile Controller
* This controller handles user's profile page, where a user can view * This controller handles user's profile page, where a user can view
* account-specific information. * account-specific information.
*/ */
function ProfileController($scope, $http, refstackApiUrl,
var refstackApp = angular.module('refstackApp');
refstackApp.factory('PubKeys',
['$resource', 'refstackApiUrl', function($resource, refstackApiUrl) {
'use strict';
return $resource(refstackApiUrl + '/profile/pubkeys/:id', null, null);
}]);
refstackApp.controller('profileController',
[
'$scope', '$http', 'refstackApiUrl', 'PubKeys',
'$modal', 'raiseAlert', '$state',
function($scope, $http, refstackApiUrl,
PubKeys, $modal, raiseAlert, $state) { PubKeys, $modal, raiseAlert, $state) {
'use strict';
var ctrl = this;
ctrl.updatePubKeys = updatePubKeys;
ctrl.openImportPubKeyModal = openImportPubKeyModal;
ctrl.openShowPubKeyModal = openShowPubKeyModal;
// Must be authenticated to view this page.
if (!$scope.auth.isAuthenticated) { if (!$scope.auth.isAuthenticated) {
$state.go('home'); $state.go('home');
} }
$scope.updatePubKeys = function (){ /**
* This function will fetch all the user's public keys from the
* server and store them in an array.
*/
function updatePubKeys() {
var keys = PubKeys.query(function() { var keys = PubKeys.query(function() {
$scope.pubkeys = []; ctrl.pubkeys = [];
angular.forEach(keys, function (key) { angular.forEach(keys, function (key) {
$scope.pubkeys.push({ ctrl.pubkeys.push({
'resource': key, 'resource': key,
'format': key.format, 'format': key.format,
'shortKey': [ 'shortKey': [
@ -41,93 +78,140 @@ refstackApp.controller('profileController',
}); });
}); });
}); });
}; }
$scope.openImportPubKeyModal = function () {
/**
* This function will open the modal that will give the user a form
* for importing a public key.
*/
function openImportPubKeyModal() {
$modal.open({ $modal.open({
templateUrl: '/components/profile/importPubKeyModal.html', templateUrl: '/components/profile/importPubKeyModal.html',
backdrop: true, backdrop: true,
windowClass: 'modal', windowClass: 'modal',
controller: 'importPubKeyModalController' controller: 'ImportPubKeyModalController as modal'
}).result.finally(function() { }).result.finally(function() {
$scope.updatePubKeys(); ctrl.updatePubKeys();
}); });
}; }
$scope.openShowPubKeyModal = function (pubKey) { /**
* This function will open the modal that will give the full
* information regarding a specific public key.
* @param {Object} pubKey resource
*/
function openShowPubKeyModal(pubKey) {
$modal.open({ $modal.open({
templateUrl: '/components/profile/showPubKeyModal.html', templateUrl: '/components/profile/showPubKeyModal.html',
backdrop: true, backdrop: true,
windowClass: 'modal', windowClass: 'modal',
controller: 'showPubKeyModalController', controller: 'ShowPubKeyModalController as modal',
resolve: { resolve: {
pubKey: function() { pubKey: function() {
return pubKey; return pubKey;
} }
} }
}).result.finally(function() { }).result.finally(function() {
$scope.updatePubKeys(); ctrl.updatePubKeys();
}); });
};
$scope.showRes = function(pubKey){
raiseAlert('success', '', pubKey.pubkey);
};
$scope.authRequest = $scope.auth.doSignCheck()
.then($scope.updatePubKeys);
} }
]);
refstackApp.controller('importPubKeyModalController', ctrl.authRequest = $scope.auth.doSignCheck().then(ctrl.updatePubKeys);
['$scope', '$modalInstance', 'PubKeys', 'raiseAlert', }
function ($scope, $modalInstance, PubKeys, raiseAlert) {
'use strict'; angular
$scope.importPubKey = function () { .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( var newPubKey = new PubKeys(
{raw_key: $scope.raw_key, {raw_key: ctrl.raw_key, self_signature: ctrl.self_signature}
self_signature: $scope.self_signature}
); );
newPubKey.$save(function(newPubKey_){ newPubKey.$save(
raiseAlert('success', function(newPubKey_) {
'', 'Public key saved successfully'); raiseAlert('success', '', 'Public key saved successfully');
$modalInstance.close(newPubKey_); $modalInstance.close(newPubKey_);
}, },
function(httpResp) { function(httpResp) {
raiseAlert('danger', raiseAlert('danger',
httpResp.statusText, httpResp.data.title); httpResp.statusText, httpResp.data.title);
$scope.cancel(); ctrl.cancel();
} }
); );
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
} }
]);
refstackApp.controller('showPubKeyModalController', /**
['$scope', '$modalInstance', 'raiseAlert', 'pubKey', * This function will dismiss the modal.
function ($scope, $modalInstance, raiseAlert, pubKey) { */
'use strict'; function cancel() {
$scope.pubKey = pubKey.resource; $modalInstance.dismiss('cancel');
$scope.rawKey = [pubKey.format, }
pubKey.pubkey, pubKey.comment].join('\n'); }
$scope.deletePubKey = function () {
$scope.pubKey.$remove( angular
{id: $scope.pubKey.id}, .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() { function() {
raiseAlert('success', raiseAlert('success',
'', 'Public key deleted successfully'); '', 'Public key deleted successfully');
$modalInstance.close($scope.pubKey.id); $modalInstance.close(ctrl.pubKey.id);
}, },
function(httpResp) { function(httpResp) {
raiseAlert('danger', raiseAlert('danger',
httpResp.statusText, httpResp.data.title); httpResp.statusText, httpResp.data.title);
$scope.cancel(); ctrl.cancel();
} }
); );
}; }
$scope.cancel = function () {
/**
* This method will dismiss the modal.
*/
function cancel() {
$modalInstance.dismiss('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,41 +1,82 @@
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.
*/
(function () {
'use strict';
angular
.module('refstackApp')
.controller('ResultsReportController', ResultsReportController);
ResultsReportController.$inject = [
'$http', '$stateParams', '$window',
'$modal', 'refstackApiUrl', 'raiseAlert'
];
/** /**
* Refstack Results Report Controller * RefStack Results Report Controller
* This controller is for the '/results/<test run ID>' page where a user can * This controller is for the '/results/<test run ID>' page where a user can
* view details for a specific test run. * view details for a specific test run.
*/ */
refstackApp.controller('resultsReportController', function ResultsReportController($http, $stateParams, $window,
['$scope', '$http', '$stateParams', $modal, refstackApiUrl, raiseAlert) {
'$window', '$modal', 'refstackApiUrl', 'raiseAlert',
function ($scope, $http, $stateParams, $window, $modal, var ctrl = this;
refstackApiUrl, raiseAlert) {
'use strict'; ctrl.getVersionList = getVersionList;
ctrl.getResults = getResults;
ctrl.isEditingAllowed = isEditingAllowed;
ctrl.isShared = isShared;
ctrl.shareTestRun = shareTestRun;
ctrl.deleteTestRun = deleteTestRun;
ctrl.updateCapabilities = updateCapabilities;
ctrl.getTargetCapabilities = getTargetCapabilities;
ctrl.buildCapabilityV1_2 = buildCapabilityV1_2;
ctrl.buildCapabilityV1_3 = buildCapabilityV1_3;
ctrl.buildCapabilitiesObject = buildCapabilitiesObject;
ctrl.isTestFlagged = isTestFlagged;
ctrl.getFlaggedReason = getFlaggedReason;
ctrl.isCapabilityShown = isCapabilityShown;
ctrl.isTestShown = isTestShown;
ctrl.getCapabilityTestCount = getCapabilityTestCount;
ctrl.getStatusTestCount = getStatusTestCount;
ctrl.openFullTestListModal = openFullTestListModal;
/** The testID extracted from the URL route. */ /** The testID extracted from the URL route. */
$scope.testId = $stateParams.testID; ctrl.testId = $stateParams.testID;
/** Whether to hide tests on start.*/ /** Whether to hide tests on start.*/
$scope.hideTests = true; ctrl.hideTests = true;
/** The target OpenStack marketing program to compare against. */ /** The target OpenStack marketing program to compare against. */
$scope.target = 'platform'; ctrl.target = 'platform';
/** Mappings of DefCore components to marketing program names. */ /** Mappings of DefCore components to marketing program names. */
$scope.targetMappings = { ctrl.targetMappings = {
'platform': 'Openstack Powered Platform', 'platform': 'Openstack Powered Platform',
'compute': 'OpenStack Powered Compute', 'compute': 'OpenStack Powered Compute',
'object': 'OpenStack Powered Object Storage' 'object': 'OpenStack Powered Object Storage'
}; };
/** The schema version of the currently selected capabilities data. */ /** The schema version of the currently selected capabilities data. */
$scope.schemaVersion = null; ctrl.schemaVersion = null;
/** The selected test status used for test filtering. */ /** The selected test status used for test filtering. */
$scope.testStatus = 'total'; ctrl.testStatus = 'total';
/** The HTML template that all accordian groups will use. */ /** The HTML template that all accordian groups will use. */
$scope.detailsTemplate = 'components/results-report/partials/' + ctrl.detailsTemplate = 'components/results-report/partials/' +
'reportDetails.html'; 'reportDetails.html';
/** /**
@ -46,19 +87,19 @@ refstackApp.controller('resultsReportController',
* call, the function to update the capabilities is called. * call, the function to update the capabilities is called.
* Sample API return array: ["2015.03.json", "2015.04.json"] * Sample API return array: ["2015.03.json", "2015.04.json"]
*/ */
var getVersionList = function () { function getVersionList() {
var content_url = refstackApiUrl + '/capabilities'; var content_url = refstackApiUrl + '/capabilities';
$scope.versionsRequest = ctrl.versionsRequest =
$http.get(content_url).success(function (data) { $http.get(content_url).success(function (data) {
$scope.versionList = data.sort().reverse(); ctrl.versionList = data.sort().reverse();
$scope.version = $scope.versionList[0]; ctrl.version = ctrl.versionList[0];
$scope.updateCapabilities(); ctrl.updateCapabilities();
}).error(function (error) { }).error(function (error) {
$scope.showError = true; ctrl.showError = true;
$scope.error = 'Error retrieving version list: ' + ctrl.error = 'Error retrieving version list: ' +
JSON.stringify(error); angular.toJson(error);
}); });
}; }
/** /**
* Retrieve results from the Refstack API server based on the test * Retrieve results from the Refstack API server based on the test
@ -66,68 +107,82 @@ refstackApp.controller('resultsReportController',
* be called from the controller. Upon successful retrieval of results, * be called from the controller. Upon successful retrieval of results,
* the function that gets the version list will be called. * the function that gets the version list will be called.
*/ */
var getResults = function () { function getResults() {
var content_url = refstackApiUrl + '/results/' + $scope.testId; var content_url = refstackApiUrl + '/results/' + ctrl.testId;
$scope.resultsRequest = ctrl.resultsRequest =
$http.get(content_url).success(function (data) { $http.get(content_url).success(function (data) {
$scope.resultsData = data; ctrl.resultsData = data;
getVersionList(); getVersionList();
}).error(function (error) { }).error(function (error) {
$scope.showError = true; ctrl.showError = true;
$scope.resultsData = null; ctrl.resultsData = null;
$scope.error = 'Error retrieving results from server: ' + ctrl.error = 'Error retrieving results from server: ' +
JSON.stringify(error); angular.toJson(error);
});
};
$scope.isEditingAllowed = function () {
return Boolean($scope.resultsData &&
$scope.resultsData.user_role === 'owner');
};
$scope.isShared = function () {
return Boolean($scope.resultsData &&
'shared' in $scope.resultsData.meta);
};
$scope.shareTestRun = function (shareState) {
var content_url = [
refstackApiUrl, '/results/', $scope.testId, '/meta/shared'
].join('');
if (shareState) {
$scope.shareRequest =
$http.post(content_url, 'true').success(function () {
$scope.resultsData.meta.shared = 'true';
raiseAlert('success', '', 'Test run shared!');
}).error(function (error) {
raiseAlert('danger',
error.title, error.detail);
});
} else {
$scope.shareRequest =
$http.delete(content_url).success(function () {
delete $scope.resultsData.meta.shared;
raiseAlert('success', '', 'Test run unshared!');
}).error(function (error) {
raiseAlert('danger',
error.title, error.detail);
}); });
} }
};
$scope.deleteTestRun = function () { /**
* This tells you whether the current results can be edited/managed
* based on if the current user is the owner of the results set.
* @returns {Boolean} true if editing is allowed
*/
function isEditingAllowed() {
return Boolean(ctrl.resultsData &&
ctrl.resultsData.user_role === 'owner');
}
/**
* This tells you whether the current results are shared with the
* community or not.
* @returns {Boolean} true if the results are shared
*/
function isShared() {
return Boolean(ctrl.resultsData &&
'shared' in ctrl.resultsData.meta);
}
/**
* This will send an API request in order to share or unshare the
* current results based on the passed in shareState.
* @param {Boolean} shareState - Whether to share or unshare results.
*/
function shareTestRun(shareState) {
var content_url = [ var content_url = [
refstackApiUrl, '/results/', $scope.testId refstackApiUrl, '/results/', ctrl.testId, '/meta/shared'
].join(''); ].join('');
$scope.deleteRequest = if (shareState) {
ctrl.shareRequest =
$http.post(content_url, 'true').success(function () {
ctrl.resultsData.meta.shared = 'true';
raiseAlert('success', '', 'Test run shared!');
}).error(function (error) {
raiseAlert('danger', error.title, error.detail);
});
} else {
ctrl.shareRequest =
$http.delete(content_url).success(function () {
delete ctrl.resultsData.meta.shared;
raiseAlert('success', '', 'Test run unshared!');
}).error(function (error) {
raiseAlert('danger', error.title, error.detail);
});
}
}
/**
* This will send a request to the API to delete the current
* test results set.
*/
function deleteTestRun() {
var content_url = [
refstackApiUrl, '/results/', ctrl.testId
].join('');
ctrl.deleteRequest =
$http.delete(content_url).success(function () { $http.delete(content_url).success(function () {
$window.history.back(); $window.history.back();
}).error(function (error) { }).error(function (error) {
raiseAlert('danger', raiseAlert('danger', error.title, error.detail);
error.title, error.detail);
}); });
}; }
/** /**
* This will contact the Refstack API server to retrieve the JSON * This will contact the Refstack API server to retrieve the JSON
@ -135,39 +190,39 @@ refstackApp.controller('resultsReportController',
* version. A function to construct an object from the capability * version. A function to construct an object from the capability
* date will be called upon successful retrieval. * date will be called upon successful retrieval.
*/ */
$scope.updateCapabilities = function () { function updateCapabilities() {
$scope.capabilityData = null; ctrl.capabilityData = null;
$scope.showError = false; ctrl.showError = false;
var content_url = refstackApiUrl + '/capabilities/' + var content_url = refstackApiUrl + '/capabilities/' +
$scope.version; ctrl.version;
$scope.capsRequest = ctrl.capsRequest =
$http.get(content_url).success(function (data) { $http.get(content_url).success(function (data) {
$scope.capabilityData = data; ctrl.capabilityData = data;
$scope.schemaVersion = data.schema; ctrl.schemaVersion = data.schema;
$scope.buildCapabilitiesObject(); ctrl.buildCapabilitiesObject();
}).error(function (error) { }).error(function (error) {
$scope.showError = true; ctrl.showError = true;
$scope.capabilityData = null; ctrl.capabilityData = null;
$scope.error = 'Error retrieving capabilities: ' + ctrl.error = 'Error retrieving capabilities: ' +
JSON.stringify(error); angular.toJson(error);
}); });
}; }
/** /**
* This will get all the capabilities relevant to the target and * This will get all the capabilities relevant to the target and
* their corresponding statuses. * their corresponding statuses.
* @returns {Object} Object containing each capability and their status * @returns {Object} Object containing each capability and their status
*/ */
$scope.getTargetCapabilities = function () { function getTargetCapabilities() {
var components = $scope.capabilityData.components; var components = ctrl.capabilityData.components;
var targetCaps = {}; var targetCaps = {};
// The 'platform' target is comprised of multiple components, so // The 'platform' target is comprised of multiple components, so
// we need to get the capabilities belonging to each of its // we need to get the capabilities belonging to each of its
// components. // components.
if ($scope.target === 'platform') { if (ctrl.target === 'platform') {
var platform_components = var platform_components =
$scope.capabilityData.platform.required; ctrl.capabilityData.platform.required;
// This will contain status priority values, where lower // This will contain status priority values, where lower
// values mean higher priorities. // values mean higher priorities.
@ -203,7 +258,7 @@ refstackApp.controller('resultsReportController',
}); });
} }
else { else {
angular.forEach(components[$scope.target], angular.forEach(components[ctrl.target],
function (caps, status) { function (caps, status) {
angular.forEach(caps, function(cap) { angular.forEach(caps, function(cap) {
targetCaps[cap] = status; targetCaps[cap] = status;
@ -211,7 +266,7 @@ refstackApp.controller('resultsReportController',
}); });
} }
return targetCaps; return targetCaps;
}; }
/** /**
* This will build the a capability object for schema version 1.2. * This will build the a capability object for schema version 1.2.
@ -219,7 +274,7 @@ refstackApp.controller('resultsReportController',
* the HTML template. * the HTML template.
* @param {String} capId capability ID * @param {String} capId capability ID
*/ */
$scope.buildCapabilityV1_2 = function (capId) { function buildCapabilityV1_2(capId) {
var cap = { var cap = {
'id': capId, 'id': capId,
'passedTests': [], 'passedTests': [],
@ -227,13 +282,13 @@ refstackApp.controller('resultsReportController',
'passedFlagged': [], 'passedFlagged': [],
'notPassedFlagged': [] 'notPassedFlagged': []
}; };
var capDetails = $scope.capabilityData.capabilities[capId]; var capDetails = ctrl.capabilityData.capabilities[capId];
// Loop through each test belonging to the capability. // Loop through each test belonging to the capability.
angular.forEach(capDetails.tests, angular.forEach(capDetails.tests,
function (testId) { function (testId) {
// If the test ID is in the results' test list, add // If the test ID is in the results' test list, add
// it to the passedTests array. // it to the passedTests array.
if ($scope.resultsData.results.indexOf(testId) > -1) { if (ctrl.resultsData.results.indexOf(testId) > -1) {
cap.passedTests.push(testId); cap.passedTests.push(testId);
if (capDetails.flagged.indexOf(testId) > -1) { if (capDetails.flagged.indexOf(testId) > -1) {
cap.passedFlagged.push(testId); cap.passedFlagged.push(testId);
@ -247,7 +302,7 @@ refstackApp.controller('resultsReportController',
} }
}); });
return cap; return cap;
}; }
/** /**
* This will build the a capability object for schema version 1.3. * This will build the a capability object for schema version 1.3.
@ -255,7 +310,7 @@ refstackApp.controller('resultsReportController',
* the HTML template. * the HTML template.
* @param {String} capId capability ID * @param {String} capId capability ID
*/ */
$scope.buildCapabilityV1_3 = function (capId) { function buildCapabilityV1_3(capId) {
var cap = { var cap = {
'id': capId, 'id': capId,
'passedTests': [], 'passedTests': [],
@ -264,11 +319,11 @@ refstackApp.controller('resultsReportController',
'notPassedFlagged': [] 'notPassedFlagged': []
}; };
// Loop through each test belonging to the capability. // Loop through each test belonging to the capability.
angular.forEach($scope.capabilityData.capabilities[capId].tests, angular.forEach(ctrl.capabilityData.capabilities[capId].tests,
function (details, testId) { function (details, testId) {
// If the test ID is in the results' test list, add // If the test ID is in the results' test list, add
// it to the passedTests array. // it to the passedTests array.
if ($scope.resultsData.results.indexOf(testId) > -1) { if (ctrl.resultsData.results.indexOf(testId) > -1) {
cap.passedTests.push(testId); cap.passedTests.push(testId);
if ('flagged' in details) { if ('flagged' in details) {
cap.passedFlagged.push(testId); cap.passedFlagged.push(testId);
@ -282,19 +337,19 @@ refstackApp.controller('resultsReportController',
} }
}); });
return cap; return cap;
}; }
/** /**
* This will check the schema version of the current capabilities file, * This will check the schema version of the current capabilities file,
* and will call the correct method to build an object based on the * and will call the correct method to build an object based on the
* capability data retrieved from the Refstack API server. * capability data retrieved from the Refstack API server.
*/ */
$scope.buildCapabilitiesObject = function () { function buildCapabilitiesObject() {
// This is the object template where 'count' is the number of // This is the object template where 'count' is the number of
// total tests that fall under the given status, and 'passedCount' // total tests that fall under the given status, and 'passedCount'
// is the number of tests passed. The 'caps' array will contain // is the number of tests passed. The 'caps' array will contain
// objects with details regarding each capability. // objects with details regarding each capability.
$scope.caps = { ctrl.caps = {
'required': {'caps': [], 'count': 0, 'passedCount': 0, 'required': {'caps': [], 'count': 0, 'passedCount': 0,
'flagFailCount': 0, 'flagPassCount': 0}, 'flagFailCount': 0, 'flagPassCount': 0},
'advisory': {'caps': [], 'count': 0, 'passedCount': 0, 'advisory': {'caps': [], 'count': 0, 'passedCount': 0,
@ -305,7 +360,7 @@ refstackApp.controller('resultsReportController',
'flagFailCount': 0, 'flagPassCount': 0} 'flagFailCount': 0, 'flagPassCount': 0}
}; };
switch ($scope.schemaVersion) { switch (ctrl.schemaVersion) {
case '1.2': case '1.2':
var capMethod = 'buildCapabilityV1_2'; var capMethod = 'buildCapabilityV1_2';
break; break;
@ -313,45 +368,45 @@ refstackApp.controller('resultsReportController',
capMethod = 'buildCapabilityV1_3'; capMethod = 'buildCapabilityV1_3';
break; break;
default: default:
$scope.showError = true; ctrl.showError = true;
$scope.capabilityData = null; ctrl.capabilityData = null;
$scope.error = 'The schema version for the capabilities ' + ctrl.error = 'The schema version for the capabilities ' +
'file selected (' + $scope.schemaVersion + 'file selected (' + ctrl.schemaVersion +
') is currently not supported.'; ') is currently not supported.';
return; return;
} }
// Get test details for each relevant capability and store // Get test details for each relevant capability and store
// them in the scope's 'caps' object. // them in the scope's 'caps' object.
var targetCaps = $scope.getTargetCapabilities(); var targetCaps = ctrl.getTargetCapabilities();
angular.forEach(targetCaps, function(status, capId) { angular.forEach(targetCaps, function(status, capId) {
var cap = $scope[capMethod](capId); var cap = ctrl[capMethod](capId);
$scope.caps[status].count += ctrl.caps[status].count +=
cap.passedTests.length + cap.notPassedTests.length; cap.passedTests.length + cap.notPassedTests.length;
$scope.caps[status].passedCount += cap.passedTests.length; ctrl.caps[status].passedCount += cap.passedTests.length;
$scope.caps[status].flagPassCount += cap.passedFlagged.length; ctrl.caps[status].flagPassCount += cap.passedFlagged.length;
$scope.caps[status].flagFailCount += ctrl.caps[status].flagFailCount +=
cap.notPassedFlagged.length; cap.notPassedFlagged.length;
$scope.caps[status].caps.push(cap); ctrl.caps[status].caps.push(cap);
}); });
$scope.requiredPassPercent = ($scope.caps.required.passedCount * ctrl.requiredPassPercent = (ctrl.caps.required.passedCount *
100 / $scope.caps.required.count); 100 / ctrl.caps.required.count);
$scope.totalRequiredFailCount = $scope.caps.required.count - ctrl.totalRequiredFailCount = ctrl.caps.required.count -
$scope.caps.required.passedCount; ctrl.caps.required.passedCount;
$scope.totalRequiredFlagCount = ctrl.totalRequiredFlagCount =
$scope.caps.required.flagFailCount + ctrl.caps.required.flagFailCount +
$scope.caps.required.flagPassCount; ctrl.caps.required.flagPassCount;
$scope.totalNonFlagCount = $scope.caps.required.count - ctrl.totalNonFlagCount = ctrl.caps.required.count -
$scope.totalRequiredFlagCount; ctrl.totalRequiredFlagCount;
$scope.nonFlagPassCount = $scope.totalNonFlagCount - ctrl.nonFlagPassCount = ctrl.totalNonFlagCount -
($scope.totalRequiredFailCount - (ctrl.totalRequiredFailCount -
$scope.caps.required.flagFailCount); ctrl.caps.required.flagFailCount);
$scope.nonFlagRequiredPassPercent = ($scope.nonFlagPassCount * ctrl.nonFlagRequiredPassPercent = (ctrl.nonFlagPassCount *
100 / $scope.totalNonFlagCount); 100 / ctrl.totalNonFlagCount);
}; }
/** /**
* This will check if a given test is flagged. * This will check if a given test is flagged.
@ -359,15 +414,15 @@ refstackApp.controller('resultsReportController',
* @param {Object} capObj capability that test is under * @param {Object} capObj capability that test is under
* @returns {Boolean} truthy value if test is flagged * @returns {Boolean} truthy value if test is flagged
*/ */
$scope.isTestFlagged = function (test, capObj) { function isTestFlagged(test, capObj) {
if (!capObj) { if (!capObj) {
return false; return false;
} }
return ((($scope.schemaVersion === '1.2') && return (((ctrl.schemaVersion === '1.2') &&
(capObj.flagged.indexOf(test) > -1)) || (capObj.flagged.indexOf(test) > -1)) ||
(($scope.schemaVersion === '1.3') && ((ctrl.schemaVersion === '1.3') &&
(capObj.tests[test].flagged))); (capObj.tests[test].flagged)));
}; }
/** /**
* This will return the reason a test is flagged. An empty string * This will return the reason a test is flagged. An empty string
@ -376,23 +431,23 @@ refstackApp.controller('resultsReportController',
* @param {String} capObj capability that test is under * @param {String} capObj capability that test is under
* @returns {String} reason * @returns {String} reason
*/ */
$scope.getFlaggedReason = function (test, capObj) { function getFlaggedReason(test, capObj) {
if (($scope.schemaVersion === '1.2') && if ((ctrl.schemaVersion === '1.2') &&
($scope.isTestFlagged(test, capObj))){ (ctrl.isTestFlagged(test, capObj))) {
// Return a generic message since schema 1.2 does not // Return a generic message since schema 1.2 does not
// provide flag reasons. // provide flag reasons.
return 'DefCore has flagged this test.'; return 'DefCore has flagged this test.';
} }
else if (($scope.schemaVersion === '1.3') && else if ((ctrl.schemaVersion === '1.3') &&
($scope.isTestFlagged(test, capObj))){ (ctrl.isTestFlagged(test, capObj))) {
return capObj.tests[test].flagged.reason; return capObj.tests[test].flagged.reason;
} }
else { else {
return ''; return '';
} }
}; }
/** /**
* This will check the if a capability should be shown based on the * This will check the if a capability should be shown based on the
@ -401,16 +456,16 @@ refstackApp.controller('resultsReportController',
* @param {Object} capability Built object for capability * @param {Object} capability Built object for capability
* @returns {Boolean} true if capability should be shown * @returns {Boolean} true if capability should be shown
*/ */
$scope.isCapabilityShown = function (capability) { function isCapabilityShown(capability) {
return (($scope.testStatus === 'total') || return ((ctrl.testStatus === 'total') ||
($scope.testStatus === 'passed' && (ctrl.testStatus === 'passed' &&
capability.passedTests.length > 0) || capability.passedTests.length > 0) ||
($scope.testStatus === 'not passed' && (ctrl.testStatus === 'not passed' &&
capability.notPassedTests.length > 0) || capability.notPassedTests.length > 0) ||
($scope.testStatus === 'flagged' && (ctrl.testStatus === 'flagged' &&
(capability.passedFlagged.length + (capability.passedFlagged.length +
capability.notPassedFlagged.length > 0))); capability.notPassedFlagged.length > 0)));
}; }
/** /**
* This will check the if a test should be shown based on the test * This will check the if a test should be shown based on the test
@ -419,16 +474,16 @@ refstackApp.controller('resultsReportController',
* @param {Object} capability Built object for capability * @param {Object} capability Built object for capability
* @return {Boolean} true if test should be shown * @return {Boolean} true if test should be shown
*/ */
$scope.isTestShown = function (test, capability) { function isTestShown(test, capability) {
return (($scope.testStatus === 'total') || return ((ctrl.testStatus === 'total') ||
($scope.testStatus === 'passed' && (ctrl.testStatus === 'passed' &&
capability.passedTests.indexOf(test) > -1) || capability.passedTests.indexOf(test) > -1) ||
($scope.testStatus === 'not passed' && (ctrl.testStatus === 'not passed' &&
capability.notPassedTests.indexOf(test) > -1) || capability.notPassedTests.indexOf(test) > -1) ||
($scope.testStatus === 'flagged' && (ctrl.testStatus === 'flagged' &&
(capability.passedFlagged.indexOf(test) > -1 || (capability.passedFlagged.indexOf(test) > -1 ||
capability.notPassedFlagged.indexOf(test) > -1))); capability.notPassedFlagged.indexOf(test) > -1)));
}; }
/** /**
* This will give the number of tests belonging under the selected * This will give the number of tests belonging under the selected
@ -436,25 +491,25 @@ refstackApp.controller('resultsReportController',
* @param {Object} capability Built object for capability * @param {Object} capability Built object for capability
* @returns {Number} number of tests under filter * @returns {Number} number of tests under filter
*/ */
$scope.getCapabilityTestCount = function (capability) { function getCapabilityTestCount(capability) {
if ($scope.testStatus === 'total') { if (ctrl.testStatus === 'total') {
return capability.passedTests.length + return capability.passedTests.length +
capability.notPassedTests.length; capability.notPassedTests.length;
} }
else if ($scope.testStatus === 'passed') { else if (ctrl.testStatus === 'passed') {
return capability.passedTests.length; return capability.passedTests.length;
} }
else if ($scope.testStatus === 'not passed') { else if (ctrl.testStatus === 'not passed') {
return capability.notPassedTests.length; return capability.notPassedTests.length;
} }
else if ($scope.testStatus === 'flagged') { else if (ctrl.testStatus === 'flagged') {
return capability.passedFlagged.length + return capability.passedFlagged.length +
capability.notPassedFlagged.length; capability.notPassedFlagged.length;
} }
else { else {
return 0; return 0;
} }
}; }
/** /**
* This will give the number of tests belonging under the selected * This will give the number of tests belonging under the selected
@ -462,68 +517,73 @@ refstackApp.controller('resultsReportController',
* @param {String} capability status * @param {String} capability status
* @returns {Number} number of tests for status under filter * @returns {Number} number of tests for status under filter
*/ */
$scope.getStatusTestCount = function (status) { function getStatusTestCount(status) {
if (!$scope.caps) { if (!ctrl.caps) {
return -1; return -1;
} }
else if ($scope.testStatus === 'total') { else if (ctrl.testStatus === 'total') {
return $scope.caps[status].count; return ctrl.caps[status].count;
} }
else if ($scope.testStatus === 'passed') { else if (ctrl.testStatus === 'passed') {
return $scope.caps[status].passedCount; return ctrl.caps[status].passedCount;
} }
else if ($scope.testStatus === 'not passed') { else if (ctrl.testStatus === 'not passed') {
return $scope.caps[status].count - return ctrl.caps[status].count -
$scope.caps[status].passedCount; ctrl.caps[status].passedCount;
} }
else if ($scope.testStatus === 'flagged') { else if (ctrl.testStatus === 'flagged') {
return $scope.caps[status].flagFailCount + return ctrl.caps[status].flagFailCount +
$scope.caps[status].flagPassCount; ctrl.caps[status].flagPassCount;
} }
else { else {
return -1; return -1;
} }
}; }
$scope.openFullTestListModal = function () { /**
* This will open the modal that will show the full list of passed
* tests for the current results.
*/
function openFullTestListModal() {
$modal.open({ $modal.open({
templateUrl: '/components/results-report/partials' + templateUrl: '/components/results-report/partials' +
'/fullTestListModal.html', '/fullTestListModal.html',
backdrop: true, backdrop: true,
windowClass: 'modal', windowClass: 'modal',
animation: true, animation: true,
controller: 'fullTestListModalController', controller: 'FullTestListModalController as modal',
size: 'lg', size: 'lg',
resolve: { resolve: {
tests: function () { tests: function () {
return $scope.resultsData.results; return ctrl.resultsData.results;
} }
} }
}); });
}; }
getResults(); getResults();
} }
]
);
angular
.module('refstackApp')
.controller('FullTestListModalController', FullTestListModalController);
FullTestListModalController.$inject = ['$modalInstance', 'tests'];
/** /**
* Full Test List Modal Controller * Full Test List Modal Controller
* This controller is for the modal that appears if a user wants to see the * This controller is for the modal that appears if a user wants to see the
* full list of passed tests on a report page. * full list of passed tests on a report page.
*/ */
refstackApp.controller('fullTestListModalController', function FullTestListModalController($modalInstance, tests) {
['$scope', '$modalInstance', 'tests', var ctrl = this;
function ($scope, $modalInstance, tests) {
'use strict';
$scope.tests = tests; ctrl.tests = tests;
/** /**
* This function will close/dismiss the modal. * This function will close/dismiss the modal.
*/ */
$scope.close = function () { ctrl.close = function () {
$modalInstance.dismiss('exit'); $modalInstance.dismiss('exit');
}; };
@ -531,8 +591,8 @@ refstackApp.controller('fullTestListModalController',
* This function will return a string representing the sorted * This function will return a string representing the sorted
* tests list separated by newlines. * tests list separated by newlines.
*/ */
$scope.getTestListString = function () { ctrl.getTestListString = function () {
return $scope.tests.sort().join('\n'); return ctrl.tests.sort().join('\n');
}; };
}] }
); })();

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,17 +1,42 @@
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.
*/
(function () {
'use strict';
angular
.module('refstackApp')
.controller('ResultsController', ResultsController);
ResultsController.$inject = [
'$scope', '$http', '$filter', '$state', 'refstackApiUrl'
];
/** /**
* Refstack Results Controller * RefStack Results Controller
* This controller is for the '/results' page where a user can browse * This controller is for the '/results' page where a user can browse
* a listing of community uploaded results. * a listing of community uploaded results.
*/ */
refstackApp.controller('resultsController', function ResultsController($scope, $http, $filter, $state, refstackApiUrl) {
['$scope', '$http', '$filter', '$state', 'refstackApiUrl', var ctrl = this;
function ($scope, $http, $filter, $state, refstackApiUrl) {
'use strict'; ctrl.update = update;
ctrl.open = open;
ctrl.clearFilters = clearFilters;
/** Initial page to be on. */ /** Initial page to be on. */
$scope.currentPage = 1; ctrl.currentPage = 1;
/** /**
* How many results should display on each page. Since pagination * How many results should display on each page. Since pagination
@ -19,67 +44,76 @@ refstackApp.controller('resultsController',
* 'results_per_page' configuration of the Refstack server which * 'results_per_page' configuration of the Refstack server which
* defaults to 20. * defaults to 20.
*/ */
$scope.itemsPerPage = 20; ctrl.itemsPerPage = 20;
/** /**
* How many page buttons should be displayed at max before adding * How many page buttons should be displayed at max before adding
* the '...' button. * the '...' button.
*/ */
$scope.maxSize = 5; ctrl.maxSize = 5;
/** The upload date lower limit to be used in filtering results. */ /** The upload date lower limit to be used in filtering results. */
$scope.startDate = ''; ctrl.startDate = '';
/** The upload date upper limit to be used in filtering results. */ /** The upload date upper limit to be used in filtering results. */
$scope.endDate = ''; ctrl.endDate = '';
$scope.isUserResults = $state.current.name === 'userResults'; /** The date format for the date picker. */
if ($scope.isUserResults && !$scope.auth.isAuthenticated) { 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'); $state.go('home');
} }
$scope.pageHeader = $scope.isUserResults ?
ctrl.pageHeader = ctrl.isUserResults ?
'Private test results' : 'Community test results'; '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 * This will contact the Refstack API to get a listing of test run
* results. * results.
*/ */
$scope.update = function () { function update() {
$scope.showError = false; ctrl.showError = false;
// Construct the API URL based on user-specified filters. // Construct the API URL based on user-specified filters.
var content_url = refstackApiUrl + '/results?page=' + var content_url = refstackApiUrl + '/results' +
$scope.currentPage; '?page=' + ctrl.currentPage;
var start = $filter('date')($scope.startDate, 'yyyy-MM-dd'); var start = $filter('date')(ctrl.startDate, 'yyyy-MM-dd');
if (start) { if (start) {
content_url = content_url =
content_url + '&start_date=' + start + ' 00:00:00'; content_url + '&start_date=' + start + ' 00:00:00';
} }
var end = $filter('date')($scope.endDate, 'yyyy-MM-dd'); var end = $filter('date')(ctrl.endDate, 'yyyy-MM-dd');
if (end) { if (end) {
content_url = content_url + '&end_date=' + end + ' 23:59:59'; content_url = content_url + '&end_date=' + end + ' 23:59:59';
} }
if ($scope.isUserResults) { if (ctrl.isUserResults) {
content_url = content_url + '&signed'; content_url = content_url + '&signed';
} }
$scope.resultsRequest = ctrl.resultsRequest =
$http.get(content_url).success(function (data) { $http.get(content_url).success(function (data) {
$scope.data = data; ctrl.data = data;
$scope.totalItems = $scope.data.pagination.total_pages * ctrl.totalItems = ctrl.data.pagination.total_pages *
$scope.itemsPerPage; ctrl.itemsPerPage;
$scope.currentPage = $scope.data.pagination.current_page; ctrl.currentPage = ctrl.data.pagination.current_page;
}).error(function (error) { }).error(function (error) {
$scope.data = null; ctrl.data = null;
$scope.totalItems = 0; ctrl.totalItems = 0;
$scope.showError = true; ctrl.showError = true;
$scope.error = ctrl.error =
'Error retrieving results listing from server: ' + 'Error retrieving results listing from server: ' +
JSON.stringify(error); angular.toJson(error);
}); });
};
if ($scope.isUserResults) {
$scope.authRequest = $scope.auth.doSignCheck()
.then($scope.update);
} else {
$scope.update();
} }
/** /**
@ -89,19 +123,20 @@ refstackApp.controller('resultsController',
* @param {Object} $event - The Event object * @param {Object} $event - The Event object
* @param {String} openVar - Tells which calendar was opened * @param {String} openVar - Tells which calendar was opened
*/ */
$scope.open = function ($event, openVar) { function open($event, openVar) {
$event.preventDefault(); $event.preventDefault();
$event.stopPropagation(); $event.stopPropagation();
$scope[openVar] = true; ctrl[openVar] = true;
}; }
/** /**
* This function will clear all filters and update the results * This function will clear all filters and update the results
* listing. * listing.
*/ */
$scope.clearFilters = function () { function clearFilters() {
$scope.startDate = null; ctrl.startDate = null;
$scope.endDate = null; ctrl.endDate = null;
$scope.update(); 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',
} }
}); });
}; };
}]
);
refstackApp.controller('raiseAlertModalController',
['$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() {
$modalInstance.close();
};
} }
]
); angular
.module('refstackApp')
.controller('RaiseAlertModalController', RaiseAlertModalController);
RaiseAlertModalController.$inject = ['$modalInstance', 'data'];
/**
* This is the controller for the alert pop-up.
*/
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,14 +1,33 @@
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.
*/
/** Refstack AngularJS Filters */ (function () {
'use strict';
/** /**
* Convert an object of objects to an array of objects to use with ng-repeat * Convert an object of objects to an array of objects to use with ng-repeat
* filters. * filters.
*/ */
refstackApp.filter('arrayConverter', function () { angular
'use strict'; .module('refstackApp')
.filter('arrayConverter', arrayConverter);
/**
* Convert an object of objects to an array of objects to use with ng-repeat
* filters.
*/
function arrayConverter() {
return function (objects) { return function (objects) {
var array = []; var array = [];
angular.forEach(objects, function (object, key) { angular.forEach(objects, function (object, key) {
@ -17,15 +36,18 @@ refstackApp.filter('arrayConverter', function () {
}); });
return array; return array;
}; };
}); }
angular
.module('refstackApp')
.filter('capitalize', capitalize);
/** /**
* Angular filter that will capitalize the first letter of a string. * Angular filter that will capitalize the first letter of a string.
*/ */
refstackApp.filter('capitalize', function() { function capitalize() {
'use strict';
return function (string) { return function (string) {
return string.substring(0, 1).toUpperCase() + string.substring(1); 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.
* 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.
*/
(function () {
'use strict';
angular
.module('refstackApp')
.controller('HeaderController', HeaderController);
HeaderController.$inject = ['$location'];
/** /**
* Refstack Header Controller * Refstack Header Controller
* This controller is for the header template which contains the site * This controller is for the header template which contains the site
* navigation. * navigation.
*/ */
refstackApp.controller('headerController', function HeaderController($location) {
['$scope', '$location', function ($scope, $location) { var ctrl = this;
'use strict';
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

@ -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': {
@ -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,11 +370,12 @@ 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 = {
'tests': {
'test1': { 'test1': {
'flagged': { 'flagged': {
'action': 'foo', 'action': 'foo',
@ -393,11 +390,11 @@ describe('Refstack controllers', function () {
} }
}; };
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,14 +402,15 @@ 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 = {
'tests': {
'test1': { 'test1': {
'flagged': { 'flagged': {
'action': 'foo', 'action': 'foo',
@ -424,8 +422,8 @@ describe('Refstack controllers', function () {
} }
}; };
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,48 +542,43 @@ 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',
@ -593,22 +586,19 @@ describe('Refstack controllers', function () {
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();
}); });
}); });