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
},
"extends": "openstack",
"globals": {
"require": false,
"exports": false,
@ -43,16 +45,25 @@
"browser": false
},
"plugins": [
"angular"
],
"rules": {
"quotes": [2, "single"],
"eol-last": 2,
"no-trailing-spaces": 2,
"camelcase": 0,
"no-extra-boolean-cast": 0,
"operator-linebreak": 0,
"require-jsdoc": 2,
// Stylistic
"indent": [2, 4],
"indent": [2, 4, {SwitchCase: 1}],
"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",
"private": true,
"name": "refstack-ui",
"description": "A user interface for Refstack",
"description": "A user interface for RefStack",
"license": "Apache2",
"devDependencies": {
"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",
"karma": "^0.12.23",
"karma-chrome-launcher": "^0.1.5",
@ -15,9 +17,7 @@
"karma-jasmine": "^0.2.2",
"karma-phantomjs-launcher": "0.2.0",
"phantomjs": "1.9.17",
"protractor": "~1.0.0",
"shelljs": "^0.2.6",
"tmp": "0.0.23"
"protractor": "~1.0.0"
},
"scripts": {
"postinstall": "bower install --config.interactive=false",

View File

@ -1,16 +1,38 @@
/** Main app module where application dependencies are listed. */
var refstackApp = angular.module('refstackApp', [
'ui.router', 'ui.bootstrap', 'cgBusy', 'ngResource', 'angular-confirm']);
/**
* Handle application routing. Specific templates and controllers will be
* used based on the URL route.
/*
* 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.config([
'$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
'use strict';
(function () {
'use strict';
/** Main app module where application dependencies are listed. */
angular
.module('refstackApp', [
'ui.router','ui.bootstrap', 'cgBusy',
'ngResource', 'angular-confirm'
]);
angular
.module('refstackApp')
.config(configureRoutes);
configureRoutes.$inject = ['$stateProvider', '$urlRouterProvider'];
/**
* Handle application routing. Specific templates and controllers will be
* used based on the URL route.
*/
function configureRoutes($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.
state('home', {
@ -24,70 +46,80 @@ refstackApp.config([
state('capabilities', {
url: '/capabilities',
templateUrl: '/components/capabilities/capabilities.html',
controller: 'capabilitiesController'
controller: 'CapabilitiesController as ctrl'
}).
state('communityResults', {
url: '/community_results',
templateUrl: '/components/results/results.html',
controller: 'resultsController'
controller: 'ResultsController as ctrl'
}).
state('userResults', {
url: '/user_results',
templateUrl: '/components/results/results.html',
controller: 'resultsController'
controller: 'ResultsController as ctrl'
}).
state('resultsDetail', {
url: '/results/:testID',
templateUrl: '/components/results-report/resultsReport.html',
controller: 'resultsReportController'
templateUrl: '/components/results-report' +
'/resultsReport.html',
controller: 'ResultsReportController as ctrl'
}).
state('profile', {
url: '/profile',
templateUrl: '/components/profile/profile.html',
controller: 'profileController'
controller: 'ProfileController as ctrl'
}).
state('authFailure', {
url: '/auth_failure/:message',
templateUrl: '/components/home/home.html',
controller: 'authFailureController'
controller: 'AuthFailureController as ctrl'
});
}
]);
/**
* Injections in $rootscope
*/
angular
.module('refstackApp')
.run(setup);
refstackApp.run(['$http', '$rootScope', '$window', '$state', 'refstackApiUrl',
function($http, $rootScope, $window, $state, refstackApiUrl) {
'use strict';
setup.$inject = [
'$http', '$rootScope', '$window', '$state', 'refstackApiUrl'
];
/**
* Set up the app with injections into $rootscope. This is mainly for auth
* functions.
*/
function setup($http, $rootScope, $window, $state, refstackApiUrl) {
/**
* This function injects sign in function in all scopes
*/
$rootScope.auth = {};
$rootScope.auth.doSignIn = doSignIn;
$rootScope.auth.doSignOut = doSignOut;
$rootScope.auth.doSignCheck = doSignCheck;
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';
$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.isAuthenticated = false;
$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';
$rootScope.auth.doSignCheck = function () {
function doSignCheck() {
return $http.get(profile_url, {withCredentials: true}).
success(function (data) {
$rootScope.auth.currentUser = data;
@ -97,30 +129,38 @@ refstackApp.run(['$http', '$rootScope', '$window', '$state', 'refstackApiUrl',
$rootScope.auth.currentUser = null;
$rootScope.auth.isAuthenticated = false;
});
};
}
$rootScope.auth.doSignCheck();
}
]);
/**
* Load config and start up the angular application.
*/
angular.element(document).ready(function () {
'use strict';
angular
.element(document)
.ready(loadConfig);
var $http = angular.injector(['ng']).get('$http');
/**
* Load config and start up the angular application.
*/
function loadConfig() {
function startApp(config) {
// Add config options as constants.
for (var key in config) {
angular.module('refstackApp').constant(key, config[key]);
var $http = angular.injector(['ng']).get('$http');
/**
* Store config variables as constants, and start the app.
*/
function startApp(config) {
// Add config options as constants.
angular.forEach(config, function(value, key) {
angular.module('refstackApp').constant(key, value);
});
angular.bootstrap(document, ['refstackApp']);
}
angular.bootstrap(document, ['refstackApp']);
}
$http.get('config.json').success(function (data) {
startApp(data);
}).error(function () {
startApp({});
});
});
$http.get('config.json').success(function (data) {
startApp(data);
}).error(function () {
startApp({});
});
}
})();

View File

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

View File

@ -4,15 +4,15 @@
<div class="row">
<div class="col-md-3">
<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. -->
<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>
</div>
<div class="col-md-4">
<strong>Target Program:</strong>
<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="compute">OpenStack Powered Compute</option>
<option value="object">OpenStack Powered Object Storage</option>
@ -21,10 +21,10 @@
</div>
<br />
<div ng-show="capabilities">
<div ng-show="ctrl.capabilities">
<strong>Corresponding OpenStack Releases:</strong>
<ul class="list-inline">
<li ng-repeat="release in capabilities.releases">
<li ng-repeat="release in ctrl.capabilities.releases">
{{release | capitalize}}
</li>
</ul>
@ -33,19 +33,19 @@
<strong>Capability Status:</strong>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="status.required">
<input type="checkbox" ng-model="ctrl.status.required">
<span class="required">Required</span>
</label>
<label>
<input type="checkbox" ng-model="status.advisory">
<input type="checkbox" ng-model="ctrl.status.advisory">
<span class="advisory">Advisory</span>
</label>
<label>
<input type="checkbox" ng-model="status.deprecated">
<input type="checkbox" ng-model="ctrl.status.deprecated">
<span class="deprecated">Deprecated</span>
</label>
<label>
<input type="checkbox" ng-model="status.removed">
<input type="checkbox" ng-model="ctrl.status.removed">
<span class="removed">Removed</span>
</label>
</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>
<!-- Loading animation divs -->
<div cg-busy="{promise:versionsRequest,message:'Loading versions'}"></div>
<div cg-busy="{promise:capsRequest,message:'Loading capabilities'}"></div>
<div cg-busy="{promise:ctrl.versionsRequest,message:'Loading versions'}"></div>
<div cg-busy="{promise:ctrl.capsRequest,message:'Loading capabilities'}"></div>
<!-- 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">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<span class="sr-only">Error:</span>
{{error}}
{{ctrl.error}}
</div>

View File

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

View File

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

View File

@ -1,21 +1,21 @@
<div class="modal-header">
<h4>Import public key</h4>
<h4>Import Public Key</h4>
</div>
<div class="modal-body container-fluid">
<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">
<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 class="row">
<div class="col-md-2">Signature</div>
<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 class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-default btn-sm" ng-click="importPubKey()">Import public key</button>
<button class="btn btn-warning" ng-click="modal.cancel()">Cancel</button>
<button type="button" class="btn btn-default btn-sm" ng-click="modal.importPubKey()">Import Public Key</button>
</div>
</div>
</div>

View File

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

View File

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

View File

@ -1,11 +1,11 @@
<div class="modal-header">
<h4>Public key</h4>
<h4>Public Key</h4>
</div>
<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">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-danger btn-sm" ng-click="deletePubKey() "
<button class="btn btn-warning" ng-click="modal.cancel()">Cancel</button>
<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>
</div>
</div>
</div>

View File

@ -1,13 +1,13 @@
<div class="modal-content">
<div class="modal-header">
<h4>All Passed Tests ({{tests.length}})</h4>
<h4>All Passed Tests ({{modal.tests.length}})</h4>
</div>
<div class="modal-body tests-modal-content">
<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 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>

View File

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

View File

@ -1,31 +1,31 @@
<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="pull-left">
<div class="test-report">
<strong>Test ID:</strong> {{testId}}<br />
<div ng-if="isEditingAllowed()"><strong>Cloud ID:</strong> {{resultsData.cpid}}<br /></div>
<strong>Upload Date:</strong> {{resultsData.created_at}} UTC<br />
<strong>Duration:</strong> {{resultsData.duration_seconds}} seconds<br />
<strong>Test ID:</strong> {{ctrl.testId}}<br />
<div ng-if="ctrl.isEditingAllowed()"><strong>Cloud ID:</strong> {{ctrl.resultsData.cpid}}<br /></div>
<strong>Upload Date:</strong> {{ctrl.resultsData.created_at}} UTC<br />
<strong>Duration:</strong> {{ctrl.resultsData.duration_seconds}} seconds<br />
<strong>Total Number of Passed Tests:</strong>
<a title="See all passed tests" ng-click="openFullTestListModal()">
{{resultsData.results.length}}
<a title="See all passed tests" ng-click="ctrl.openFullTestListModal()">
{{ctrl.resultsData.results.length}}
</a><br />
<hr>
</div>
</div>
<div class="pull-right">
<div ng-show="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-success" ng-show="isShared()" ng-click="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>
<div ng-show="ctrl.isEditingAllowed()">
<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="ctrl.isShared()" ng-click="ctrl.shareTestRun(false)">Unshare</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 ng-show="resultsData">
<div ng-show="ctrl.resultsData">
<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>
</p>
@ -34,14 +34,14 @@
<div class="row">
<div class="col-md-3">
<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 -->
<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>
</div>
<div class="col-md-4">
<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="compute">OpenStack Powered Compute</option>
<option value="object">OpenStack Powered Object Storage</option>
@ -53,28 +53,28 @@
<br />
<strong>Corresponding OpenStack Releases:</strong>
<ul class="list-inline">
<li ng-repeat="release in capabilityData.releases">
<li ng-repeat="release in ctrl.capabilityData.releases">
{{release | capitalize}}
</li>
</ul>
<hr >
<div ng-show="capabilityData">
<div ng-show="ctrl.capabilityData">
<strong>Status:</strong>
<p>This cloud passes <strong>{{requiredPassPercent | number:1}}% </strong>
({{caps.required.passedCount}}/{{caps.required.count}})
of the tests in the <strong>{{version.slice(0, -5)}}</strong> <em>required</em> capabilities for the
<strong>{{targetMappings[target]}}</strong> program. <br />
<p>This cloud passes <strong>{{ctrl.requiredPassPercent | number:1}}% </strong>
({{ctrl.caps.required.passedCount}}/{{ctrl.caps.required.count}})
of the tests in the <strong>{{ctrl.version.slice(0, -5)}}</strong> <em>required</em> capabilities for the
<strong>{{ctrl.targetMappings[target]}}</strong> program. <br />
Excluding flagged tests, this cloud passes
<strong>{{nonFlagRequiredPassPercent | number:1}}%</strong>
({{nonFlagPassCount}}/{{totalNonFlagCount}})
<strong>{{ctrl.nonFlagRequiredPassPercent | number:1}}%</strong>
({{ctrl.nonFlagPassCount}}/{{ctrl.totalNonFlagCount}})
of the <em>required</em> tests.
</p>
<p>Compliance with <strong>{{version.slice(0, -5)}}</strong>:
<p>Compliance with <strong>{{ctrl.version.slice(0, -5)}}</strong>:
<strong>
<span ng-if="nonFlagPassCount === totalNonFlagCount" class="yes">YES</span>
<span ng-if="nonFlagPassCount !== totalNonFlagCount" class="no">NO</span>
<span ng-if="ctrl.nonFlagPassCount === ctrl.totalNonFlagCount" class="yes">YES</span>
<span ng-if="ctrl.nonFlagPassCount !== ctrl.totalNonFlagCount" class="no">NO</span>
</strong>
</p>
@ -83,20 +83,20 @@
Test Filters:<br />
<div class="btn-group button-margin" data-toggle="buttons">
<label class="btn btn-default" ng-class="{'active': testStatus === 'total'}">
<input type="radio" ng-model="testStatus" value="total">
<label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'total'}">
<input type="radio" ng-model="ctrl.testStatus" value="total">
<span class="text-primary">All</span>
</label>
<label class="btn btn-default" ng-class="{'active': testStatus === 'passed'}">
<input type="radio" ng-model="testStatus" value="passed">
<label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'passed'}">
<input type="radio" ng-model="ctrl.testStatus" value="passed">
<span class="text-success">Passed</span>
</label>
<label class="btn btn-default" ng-class="{'active': testStatus === 'not passed'}">
<input type="radio" ng-model="testStatus" value="not passed">
<label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'not passed'}">
<input type="radio" ng-model="ctrl.testStatus" value="not passed">
<span class="text-danger">Not Passed</span>
</label>
<label class="btn btn-default" ng-class="{'active': testStatus === 'flagged'}">
<input type="radio" ng-model="testStatus" value="flagged">
<label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'flagged'}">
<input type="radio" ng-model="ctrl.testStatus" value="flagged">
<span class="text-warning">Flagged</span>
</label>
</div>
@ -105,23 +105,23 @@
<!-- The ng-repeat is used to pass in a local variable to the template. -->
<ng-include
ng-repeat="status in ['required']"
src="detailsTemplate"
src="ctrl.detailsTemplate"
onload="isOpen = true">
</ng-include>
<br />
<ng-include
ng-repeat="status in ['advisory']"
src="detailsTemplate">
src="ctrl.detailsTemplate">
</ng-include>
<br />
<ng-include
ng-repeat="status in ['deprecated']"
src="detailsTemplate">
src="ctrl.detailsTemplate">
</ng-include>
<br />
<ng-include
ng-repeat="status in ['removed']"
src="detailsTemplate">
src="ctrl.detailsTemplate">
</ng-include>
</accordion>
</div>

View File

@ -1,4 +1,4 @@
<h3>{{pageHeader}}</h3>
<h3>{{ctrl.pageHeader}}</h3>
<p>The most recently uploaded community test results are listed here.</p>
<div class="result-filters">
@ -8,12 +8,11 @@
<label for="cpid">Start Date</label>
<p class="input-group">
<input type="text" class="form-control"
datepicker-popup="{{format}}"
ng-model="startDate" is-open="startOpen"
datepicker-options="dateOptions"
datepicker-popup="{{ctrl.format}}"
ng-model="ctrl.startDate" is-open="ctrl.startOpen"
close-text="Close" />
<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>
</button>
</span>
@ -23,62 +22,61 @@
<label for="cpid">End Date</label>
<p class="input-group">
<input type="text" class="form-control"
datepicker-popup="{{format}}"
ng-model="endDate" is-open="endOpen"
datepicker-options="dateOptions"
datepicker-popup="{{ctrl.format}}"
ng-model="ctrl.endDate" is-open="ctrl.endOpen"
close-text="Close" />
<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>
</button>
</span>
</p>
</div>
<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 btn-danger" ng-click="clearFilters()">Clear</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="ctrl.clearFilters()">Clear</button>
</div>
</div>
</div>
<div cg-busy="{promise:authRequest,message:'Loading'}"></div>
<div cg-busy="{promise:resultsRequest,message:'Loading'}"></div>
<div ng-show="data" class="results-table">
<table ng-show="data" class="table table-striped table-hover">
<div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div>
<div cg-busy="{promise:ctrl.resultsRequest,message:'Loading'}"></div>
<div ng-show="ctrl.data" class="results-table">
<table ng-show="ctrl.data" class="table table-striped table-hover">
<thead>
<tr>
<th>Upload Date</th>
<th>Test Run ID</th>
<th ng-if="::isUserResults">Shared</th>
<th ng-if="ctrl.isUserResults">Shared</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="result in data.results">
<tr ng-repeat="result in ctrl.data.results">
<td>{{result.created_at}}</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>
</tbody>
</table>
<div class="pages">
<pagination
total-items="totalItems"
ng-model="currentPage"
items-per-page="itemsPerPage"
max-size="maxSize"
total-items="ctrl.totalItems"
ng-model="ctrl.currentPage"
items-per-page="ctrl.itemsPerPage"
max-size="ctrl.maxSize"
class="pagination-sm"
boundary-links="true"
rotate="false"
num-pages="numPages"
ng-change="update()">
num-pages="ctrl.numPages"
ng-change="ctrl.update()">
</pagination>
</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="sr-only">Error:</span>
{{error}}
{{ctrl.error}}
</div>

View File

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

View File

@ -1,8 +1,8 @@
<div class="modal-body" style="padding:0px">
<div class="alert alert-{{::data.mode}}" style="margin-bottom:0px">
<button type="button" class="close" data-ng-click="close()" >
<div class="alert alert-{{alert.data.mode}}" style="margin-bottom:0px">
<button type="button" class="close" data-ng-click="alert.close()" >
<span class="glyphicon glyphicon-remove-circle"></span>
</button>
<strong>{{::data.title}}</strong> {{::data.text}}
<strong>{{alert.data.title}}</strong> {{alert.data.text}}
</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',
['$modal', function($modal) {
'use strict';
(function () {
'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) {
$modal.open({
templateUrl: '/shared/alerts/alertModal.html',
controller: 'raiseAlertModalController',
controller: 'RaiseAlertModalController as alert',
backdrop: true,
keyboard: true,
backdropClick: true,
@ -22,20 +45,30 @@ refstackApp.factory('raiseAlert',
}
});
};
}]
);
}
angular
.module('refstackApp')
.controller('RaiseAlertModalController', RaiseAlertModalController);
refstackApp.controller('raiseAlertModalController',
['$scope', '$modalInstance', 'data',
function ($scope, $modalInstance, data) {
'use strict';
$scope.data = data;
RaiseAlertModalController.$inject = ['$modalInstance', 'data'];
//wait for users click to close the pop up window.
$scope.close = function() {
$modalInstance.close();
};
/**
* 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,31 +1,53 @@
var refstackApp = angular.module('refstackApp');
/** Refstack AngularJS Filters */
/**
* Convert an object of objects to an array of objects to use with ng-repeat
* filters.
/*
* 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.filter('arrayConverter', function () {
(function () {
'use strict';
return function (objects) {
var array = [];
angular.forEach(objects, function (object, key) {
object.id = key;
array.push(object);
});
return array;
};
});
/**
* Convert an object of objects to an array of objects to use with ng-repeat
* filters.
*/
angular
.module('refstackApp')
.filter('arrayConverter', arrayConverter);
/**
* Angular filter that will capitalize the first letter of a string.
*/
refstackApp.filter('capitalize', function() {
'use strict';
/**
* Convert an object of objects to an array of objects to use with ng-repeat
* filters.
*/
function arrayConverter() {
return function (objects) {
var array = [];
angular.forEach(objects, function (object, key) {
object.id = key;
array.push(object);
});
return array;
};
}
return function (string) {
return string.substring(0, 1).toUpperCase() + string.substring(1);
};
});
angular
.module('refstackApp')
.filter('capitalize', capitalize);
/**
* Angular filter that will capitalize the first letter of a string.
*/
function capitalize() {
return function (string) {
return string.substring(0, 1).toUpperCase() + string.substring(1);
};
}
})();

View File

@ -1,11 +1,11 @@
<div class="heading"><a ui-sref="home"><img src="assets/img/refstack-logo.png" alt="RefStack"></a>
RefStack
</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">
<!-- Brand and toggle get grouped for better mobile display -->
<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="icon-bar"></span>
<span class="icon-bar"></span>
@ -13,12 +13,12 @@ RefStack
</button>
</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">
<li ng-class="{ active: 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: 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('/')}"><a ui-sref="home">Home</a></li>
<li ng-class="{ active: header.isActive('/about')}"><a ui-sref="about">About</a></li>
<li ng-class="{ active: header.isActive('/capabilities')}"><a ui-sref="capabilities">DefCore Capabilities</a></li>
<li ng-class="{ active: header.isActive('/community_results')}"><a ui-sref="communityResults">Community Results</a></li>
</ul>
<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>

View File

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

View File

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