Merge "Add results and test report page"
This commit is contained in:
commit
c4a5f8e69c
1
refstack-ui/.gitignore
vendored
1
refstack-ui/.gitignore
vendored
@ -7,3 +7,4 @@ dist
|
||||
node_modules
|
||||
npm-debug.log
|
||||
app/assets/lib
|
||||
app/config.json
|
||||
|
@ -7,6 +7,10 @@ User interface for interacting with the Refstack API.
|
||||
Setup
|
||||
=====
|
||||
|
||||
Create a config.json file and specify your API endpoint inside this file:
|
||||
|
||||
:code:`cp app/config.json.sample app/config.json`
|
||||
|
||||
You can start a development server by doing the following:
|
||||
|
||||
Install NodeJS and NPM:
|
||||
|
@ -3,8 +3,11 @@
|
||||
/* App Module */
|
||||
|
||||
var refstackApp = angular.module('refstackApp', [
|
||||
'ui.router', 'ui.bootstrap']);
|
||||
'ui.router', 'ui.bootstrap', 'cgBusy']);
|
||||
|
||||
/*
|
||||
* Handle application routing.
|
||||
*/
|
||||
refstackApp.config(['$stateProvider', '$urlRouterProvider',
|
||||
function($stateProvider, $urlRouterProvider) {
|
||||
$urlRouterProvider.otherwise('/');
|
||||
@ -24,7 +27,33 @@ refstackApp.config(['$stateProvider', '$urlRouterProvider',
|
||||
}).
|
||||
state('results', {
|
||||
url: '/results',
|
||||
templateUrl: '/components/results/results.html'
|
||||
templateUrl: '/components/results/results.html',
|
||||
controller: 'resultsController'
|
||||
}).
|
||||
state('resultsDetail', {
|
||||
url: '/results/:testID',
|
||||
templateUrl: '/components/results-report/resultsReport.html',
|
||||
controller: 'resultsReportController'
|
||||
})
|
||||
}]);
|
||||
}
|
||||
]);
|
||||
|
||||
/*
|
||||
* Load Config and start up the angular application.
|
||||
*/
|
||||
angular.element(document).ready(function () {
|
||||
var $http = angular.injector(['ng']).get('$http');
|
||||
function startApp(config) {
|
||||
// Add config options as constants.
|
||||
for (var key in config) {
|
||||
angular.module('refstackApp').constant(key, config[key]);
|
||||
}
|
||||
angular.bootstrap(document, ['refstackApp']);
|
||||
}
|
||||
|
||||
$http.get('config.json').success(function(data) {
|
||||
startApp(data);
|
||||
}).error(function(error) {
|
||||
startApp({});
|
||||
});
|
||||
});
|
||||
|
@ -100,11 +100,6 @@ h1, h2, h3, h4, h5, h6 {
|
||||
content: '\00BB';
|
||||
}
|
||||
|
||||
.flagged:before {
|
||||
color: #E6A100;
|
||||
content: '\2691';
|
||||
}
|
||||
|
||||
.program-about {
|
||||
font-size: .8em;
|
||||
}
|
||||
@ -128,3 +123,10 @@ h1, h2, h3, h4, h5, h6 {
|
||||
width: 70%;
|
||||
height: 70%;
|
||||
}
|
||||
|
||||
.result-filters {
|
||||
padding-bottom: 10px;
|
||||
border-top: 2px solid #C9C9C9;
|
||||
border-bottom: 2px solid #C9C9C9;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
@ -49,7 +49,8 @@
|
||||
<a ng-click="hideTests = !hideTests">Tests ({{capability.tests.length}})</a>
|
||||
<ul collapse="hideTests">
|
||||
<li ng-repeat="test in capability.tests">
|
||||
<span ng-class="{'flagged': capability.flagged.indexOf(test) > -1}"> {{test}}</span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capability.flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
179
refstack-ui/app/components/results-report/resultsReport.html
Normal file
179
refstack-ui/app/components/results-report/resultsReport.html
Normal file
@ -0,0 +1,179 @@
|
||||
<h3>Test Run Results</h3>
|
||||
|
||||
<div cg-busy="{promise:resultsRequest,message:'Loading'}"></div>
|
||||
<div ng-show="resultsData" class="test-report">
|
||||
<strong>Test ID:</strong> {{testId}} <br />
|
||||
<strong>Upload Date:</strong> {{resultsData.created_at}} UTC<br />
|
||||
<strong>Duration:</strong> {{resultsData.duration_seconds}} seconds<br />
|
||||
<strong>Total Number of Passed Tests:</strong> {{resultsData.results.length}} <br />
|
||||
|
||||
<hr>
|
||||
|
||||
<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>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<strong>Capabilities Version:</strong>
|
||||
<select ng-model="version" ng-change="updateCapabilities()" class="form-control">
|
||||
<option value="2015.03">2015.03</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<strong>Target Program:</strong>
|
||||
<select ng-model="target" class="form-control" ng-change="buildCapabilityObject()">
|
||||
<option value="platform">OpenStack Powered Platform</option>
|
||||
<option value="compute">OpenStack Powered Compute</option>
|
||||
<option value="object">OpenStack Powered Object Storage</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div ng-show="capabilityData && resultsData">
|
||||
<strong>Status:</strong>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"
|
||||
role="progressbar"
|
||||
aria-valuenow="{{caps.required.passedCount*100/caps.required.count | number:1}}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
style="min-width: 3em; width: {{caps.required.passedCount*100/caps.required.count}}%;">
|
||||
|
||||
{{caps.required.passedCount*100/caps.required.count | number:1}}%
|
||||
</div>
|
||||
</div>
|
||||
<p>This cloud passes <strong>{{caps.required.passedCount*100/caps.required.count | number:1}}% </strong>({{caps.required.passedCount}}/{{caps.required.count}})
|
||||
of the <strong>{{version}}</strong> capability tests required by the <strong>{{targetMappings[target]}}</strong> program.</p>
|
||||
|
||||
<h4>Capability Overview</h4>
|
||||
|
||||
<accordion close-others=false>
|
||||
<accordion-group heading="Required" is-open="requiredOpen" is-disabled="caps.required.caps.length == 0">
|
||||
<accordion-heading>
|
||||
Required <small>({{caps.required.caps.length}} capabilities)</small>
|
||||
<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': requiredOpen, 'glyphicon-chevron-right': !requiredOpen}"></i>
|
||||
</accordion-heading>
|
||||
<ol class="capabilities">
|
||||
<li ng-repeat="capability in caps.required.caps">
|
||||
<a ng-click="hideTests = !hideTests">{{capability.id}} </a>
|
||||
<span ng-class="{'text-success': (capability.passedTests.length > 0 && capability.notPassedTests.length == 0),
|
||||
'text-danger': (capability.passedTests.length == 0 && capability.notPassedTests.length > 0),
|
||||
'text-warning': (capability.passedTests.length > 0 && capability.notPassedTests.length > 0)}">
|
||||
[{{capability.passedTests.length}}/{{capability.passedTests.length + capability.notPassedTests.length}}]
|
||||
</span>
|
||||
|
||||
<ul class="list-unstyled" collapse="hideTests">
|
||||
<li ng-repeat="test in capability.passedTests">
|
||||
<span class="glyphicon glyphicon-ok text-success" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
<li ng-repeat="test in capability.notPassedTests">
|
||||
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</accordion-group>
|
||||
|
||||
<accordion-group heading="Advisory" is-open="advisoryOpen" is-disabled="caps.advisory.caps.length == 0">
|
||||
<accordion-heading>
|
||||
Advisory <small>({{caps.advisory.caps.length}} capabilities)</small>
|
||||
<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': advisoryOpen, 'glyphicon-chevron-right': !advisoryOpen}"></i>
|
||||
</accordion-heading>
|
||||
<ol class="capabilities">
|
||||
<li ng-repeat="capability in caps.advisory.caps">
|
||||
<a ng-click="hideTests = !hideTests">{{capability.id}} </a>
|
||||
<span ng-class="{'text-success': (capability.passedTests.length > 0 && capability.notPassedTests.length == 0),
|
||||
'text-danger': (capability.passedTests.length == 0 && capability.notPassedTests.length > 0),
|
||||
'text-warning': (capability.passedTests.length > 0 && capability.notPassedTests.length > 0)}">
|
||||
[{{capability.passedTests.length}}/{{capability.passedTests.length + capability.notPassedTests.length}}]
|
||||
</span>
|
||||
|
||||
<ul class="list-unstyled" collapse="hideTests">
|
||||
<li ng-repeat="test in capability.passedTests">
|
||||
<span class="glyphicon glyphicon-ok text-success" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
<li ng-repeat="test in capability.notPassedTests">
|
||||
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</accordion-group>
|
||||
|
||||
<accordion-group heading="Deprecated" is-open="deprecatedOpen" is-disabled="caps.deprecated.caps.length == 0">
|
||||
<accordion-heading>
|
||||
Deprecated <small>({{caps.deprecated.caps.length}} capabilities)</small>
|
||||
<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': deprecatedOpen, 'glyphicon-chevron-right': !deprecatedOpen}"></i>
|
||||
</accordion-heading>
|
||||
<ol class="capabilities">
|
||||
<li ng-repeat="capability in caps.deprecated.caps">
|
||||
<a ng-click="hideTests = !hideTests">{{capability.id}} </a>
|
||||
<span ng-class="{'text-success': (capability.passedTests.length > 0 && capability.notPassedTests.length == 0),
|
||||
'text-danger': (capability.passedTests.length == 0 && capability.notPassedTests.length > 0),
|
||||
'text-warning': (capability.passedTests.length > 0 && capability.notPassedTests.length > 0)}">
|
||||
[{{capability.passedTests.length}}/{{capability.passedTests.length + capability.notPassedTests.length}}]
|
||||
</span>
|
||||
|
||||
<ul class="list-unstyled" collapse="hideTests">
|
||||
<li ng-repeat="test in capability.passedTests">
|
||||
<span class="glyphicon glyphicon-ok text-success" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
<li ng-repeat="test in capability.notPassedTests">
|
||||
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</accordion-group>
|
||||
|
||||
<accordion-group is-open="removedOpen" is-disabled="caps.removed.caps.length == 0">
|
||||
<accordion-heading>
|
||||
Removed <small>({{caps.removed.caps.length}} capabilities)</small>
|
||||
<i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': removedOpen, 'glyphicon-chevron-right': !removedOpen}"></i>
|
||||
</accordion-heading>
|
||||
<ol class="capabilities">
|
||||
<li ng-repeat="capability in caps.removed.caps">
|
||||
<a ng-click="hideTests = !hideTests">{{capability.id}} </a>
|
||||
<span ng-class="{'text-success': (capability.passedTests.length > 0 && capability.notPassedTests.length == 0),
|
||||
'text-danger': (capability.passedTests.length == 0 && capability.notPassedTests.length > 0),
|
||||
'text-warning': (capability.passedTests.length > 0 && capability.notPassedTests.length > 0)}">
|
||||
[{{capability.passedTests.length}}/{{capability.passedTests.length + capability.notPassedTests.length}}]
|
||||
</span>
|
||||
|
||||
<ul class="list-unstyled" collapse="hideTests">
|
||||
<li ng-repeat="test in capability.passedTests">
|
||||
<span class="glyphicon glyphicon-ok text-success" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
<li ng-repeat="test in capability.notPassedTests">
|
||||
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
|
||||
<span ng-class="{'glyphicon glyphicon-flag text-warning': capabilityData.capabilities[capability.id].flagged.indexOf(test) > -1}"></span>
|
||||
{{test}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</accordion-group>
|
||||
</accordion>
|
||||
</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}}
|
||||
</div>
|
@ -0,0 +1,91 @@
|
||||
'use strict';
|
||||
|
||||
/* Refstack Results Report Controller */
|
||||
|
||||
var refstackApp = angular.module('refstackApp');
|
||||
|
||||
refstackApp.controller('resultsReportController', ['$scope', '$http', '$stateParams', 'refstackApiUrl',
|
||||
function($scope, $http, $stateParams, refstackApiUrl) {
|
||||
$scope.testId = $stateParams.testID
|
||||
$scope.version = '2015.03';
|
||||
$scope.hideTests = true;
|
||||
$scope.target = 'platform';
|
||||
$scope.requiredOpen = true;
|
||||
|
||||
$scope.targetMappings = {
|
||||
'platform': 'Openstack Powered Platform',
|
||||
'compute': 'OpenStack Powered Compute',
|
||||
'object': 'OpenStack Powered Object Storage'
|
||||
}
|
||||
|
||||
var content_url = refstackApiUrl +'/results/' + $scope.testId;
|
||||
$scope.resultsRequest = $http.get(content_url).success(function(data) {
|
||||
$scope.resultsData = data;
|
||||
$scope.updateCapabilities();
|
||||
}).error(function(error) {
|
||||
$scope.showError = true;
|
||||
$scope.resultsData = null;
|
||||
$scope.error = "Error retrieving results from server: " + JSON.stringify(error);
|
||||
|
||||
});
|
||||
|
||||
$scope.updateCapabilities = function() {
|
||||
$scope.showError = false;
|
||||
var content_url = 'assets/capabilities/'.concat($scope.version, '.json');
|
||||
$http.get(content_url).success(function(data) {
|
||||
$scope.capabilityData = data;
|
||||
$scope.buildCapabilityObject($scope.capabilityData, $scope.resultsData.results);
|
||||
}).error(function(error) {
|
||||
$scope.showError = true;
|
||||
$scope.capabilityData = null;
|
||||
$scope.error = 'Error retrieving capabilities: ' + JSON.stringify(error);
|
||||
});
|
||||
}
|
||||
|
||||
$scope.buildCapabilityObject = function() {
|
||||
var capabilities = $scope.capabilityData.capabilities;
|
||||
var caps = {'required': {'caps': [], 'count': 0, 'passedCount': 0},
|
||||
'advisory': {'caps': [], 'count': 0, 'passedCount': 0},
|
||||
'deprecated': {'caps': [], 'count': 0, 'passedCount': 0},
|
||||
'removed': {'caps': [], 'count': 0, 'passedCount': 0}};
|
||||
var components = $scope.capabilityData.components;
|
||||
var cap_array = [];
|
||||
// First determine which capabilities are relevant to the target.
|
||||
if ($scope.target === 'platform') {
|
||||
var platform_components = $scope.capabilityData.platform.required;
|
||||
// For each component required for the platform program.
|
||||
angular.forEach(platform_components, function(component) {
|
||||
// Get each capability belonging to each status.
|
||||
angular.forEach(components[component], function(capabilities) {
|
||||
cap_array = cap_array.concat(capabilities);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
angular.forEach(components[$scope.target], function(capabilities) {
|
||||
cap_array = cap_array.concat(capabilities);
|
||||
});
|
||||
}
|
||||
|
||||
angular.forEach(capabilities, function(value, key) {
|
||||
if (cap_array.indexOf(key) > -1) {
|
||||
var cap = { "id": key,
|
||||
"passedTests": [],
|
||||
"notPassedTests": []};
|
||||
caps[value.status].count += value.tests.length;
|
||||
angular.forEach(value.tests, function(test_id) {
|
||||
if ($scope.resultsData.results.indexOf(test_id) > -1) {
|
||||
cap.passedTests.push(test_id);
|
||||
}
|
||||
else {
|
||||
cap.notPassedTests.push(test_id);
|
||||
}
|
||||
});
|
||||
caps[value.status].passedCount += cap.passedTests.length;
|
||||
caps[value.status].caps.push(cap);
|
||||
}
|
||||
});
|
||||
$scope.caps = caps;
|
||||
}
|
||||
}
|
||||
]);
|
@ -1 +1,82 @@
|
||||
<p>Community results list here.</p>
|
||||
<h3>Community Results</h3>
|
||||
<p>The most recently uploaded community test results are listed here. Currently, these results are anonymous.</p>
|
||||
|
||||
<div class="result-filters">
|
||||
<h4>Filters</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<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"
|
||||
close-text="Close" />
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" ng-click="open($event, 'startOpen')">
|
||||
<i class="glyphicon glyphicon-calendar"></i>
|
||||
</button>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<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"
|
||||
close-text="Close" />
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" ng-click="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>
|
||||
</div>
|
||||
</div>
|
||||
</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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Upload Date</th>
|
||||
<th>Test Run ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr ng-repeat="result in data.results">
|
||||
<td>{{result.created_at}}</td>
|
||||
<td><a ui-sref="resultsDetail({testID: result.test_id})">{{result.test_id}}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="pages">
|
||||
<pagination
|
||||
total-items="totalItems"
|
||||
ng-model="currentPage"
|
||||
items-per-page="itemsPerPage"
|
||||
max-size="maxSize"
|
||||
class="pagination-sm"
|
||||
boundary-links="true"
|
||||
rotate="false"
|
||||
num-pages="numPages"
|
||||
ng-change="update()">
|
||||
</pagination>
|
||||
</div>
|
||||
</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}}
|
||||
</div>
|
||||
|
||||
|
51
refstack-ui/app/components/results/resultsController.js
Normal file
51
refstack-ui/app/components/results/resultsController.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
/* Refstack Results Controller */
|
||||
|
||||
var refstackApp = angular.module('refstackApp');
|
||||
|
||||
refstackApp.controller('resultsController', ['$scope', '$http', '$filter', 'refstackApiUrl', function($scope, $http, $filter, refstackApiUrl) {
|
||||
$scope.currentPage = 1;
|
||||
$scope.itemsPerPage = 20;
|
||||
$scope.maxSize = 5;
|
||||
$scope.startDate = "";
|
||||
$scope.endDate = "";
|
||||
$scope.update = function() {
|
||||
$scope.showError = false;
|
||||
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";
|
||||
}
|
||||
|
||||
$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);
|
||||
});
|
||||
}
|
||||
|
||||
$scope.update();
|
||||
|
||||
// This is called when a date filter calendar is opened.
|
||||
$scope.open = function($event, openVar) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
$scope[openVar] = true;
|
||||
};
|
||||
|
||||
$scope.clearFilters = function() {
|
||||
$scope.startDate = null;
|
||||
$scope.endDate = null;
|
||||
$scope.update();
|
||||
};
|
||||
}]);
|
1
refstack-ui/app/config.json.sample
Normal file
1
refstack-ui/app/config.json.sample
Normal file
@ -0,0 +1 @@
|
||||
{"refstackApiUrl": "http://api.refstack.net/v1"}
|
@ -14,7 +14,7 @@
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<html ng-app="refstackApp">
|
||||
<html id="ng-app">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="Refstack">
|
||||
@ -24,17 +24,22 @@
|
||||
<link rel="shorcut icon" href="favicon.ico">
|
||||
|
||||
<link rel="stylesheet" href="assets/lib/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="assets/lib/angular-busy/dist/angular-busy.min.css">
|
||||
<link rel="stylesheet" href="assets/css/style.css">
|
||||
|
||||
<script src="assets/lib/angular/angular.js"></script>
|
||||
<script src="assets/lib/angular-ui-router/release/angular-ui-router.js"></script>
|
||||
<script src="assets/lib/angular-bootstrap/ui-bootstrap.min.js"></script>
|
||||
<script src="assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js"></script>
|
||||
<script src="assets/lib/angular-busy/dist/angular-busy.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="assets/js/refstack.js"></script>
|
||||
|
||||
<!-- Controllers -->
|
||||
<script src="shared/header/headerController.js"></script>
|
||||
<script src="components/capabilities/capabilitiesController.js"></script>
|
||||
<script src="components/results/resultsController.js"></script>
|
||||
<script src="components/results-report/resultsReportController.js"></script>
|
||||
|
||||
<!-- Filters -->
|
||||
<script src="shared/filters.js"></script>
|
||||
|
@ -7,6 +7,7 @@
|
||||
"angular-ui-router": "0.2.13",
|
||||
"angular-resource": "1.3.15",
|
||||
"angular-bootstrap": "0.12.1",
|
||||
"angular-busy": "4.1.3",
|
||||
"bootstrap": "3.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -9,6 +9,8 @@ module.exports = function(config){
|
||||
'app/assets/lib/angular-ui-router/release/angular-ui-router.js',
|
||||
'app/assets/lib/angular-bootstrap/ui-bootstrap.min.js',
|
||||
'app/assets/lib/angular-mocks/angular-mocks.js',
|
||||
'app/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js',
|
||||
'app/assets/lib/angular-busy/dist/angular-busy.min.js',
|
||||
|
||||
// JS files.
|
||||
'app/app.js',
|
||||
|
@ -91,4 +91,121 @@ describe('Refstack controllers', function() {
|
||||
expect(scope.filterProgram({'id': 'cap_id_5'})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resultsController', function() {
|
||||
var scope, ctrl, $httpBackend, refstackApiUrl;
|
||||
var fakeResponse = {'pagination': {'current_page': 1, 'total_pages': 2},
|
||||
'results': [{'created_at': '2015-03-09 01:23:45',
|
||||
'test_id': 'some-id',
|
||||
'cpid': 'some-cpid'}]};
|
||||
|
||||
beforeEach(function() {
|
||||
module('refstackApp');
|
||||
module(function($provide) {
|
||||
$provide.constant('refstackApiUrl', 'http://foo.bar/v1');
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('resultsController', {$scope: scope});
|
||||
}));
|
||||
|
||||
it('should fetch the first page of results with proper URL args', function() {
|
||||
// Initial results should be page 1 of all results.
|
||||
$httpBackend.expectGET('http://foo.bar/v1/results?page=1').respond(fakeResponse);
|
||||
$httpBackend.flush();
|
||||
expect(scope.data).toEqual(fakeResponse);
|
||||
expect(scope.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();
|
||||
$httpBackend.expectGET('http://foo.bar/v1/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);
|
||||
});
|
||||
|
||||
it('should set an error when results cannot be retrieved', function() {
|
||||
$httpBackend.expectGET('http://foo.bar/v1/results?page=1').respond(404, {'detail': 'Not Found'});
|
||||
$httpBackend.flush();
|
||||
expect(scope.data).toBe(null);
|
||||
expect(scope.error).toEqual('Error retrieving results listing from server: {"detail":"Not Found"}');
|
||||
expect(scope.totalItems).toBe(0);
|
||||
expect(scope.showError).toBe(true);
|
||||
});
|
||||
|
||||
it('should have an function to clear filters and update the view', function() {
|
||||
$httpBackend.expectGET('http://foo.bar/v1/results?page=1').respond(fakeResponse);
|
||||
scope.startDate = "some date";
|
||||
scope.endDate = "some other date";
|
||||
scope.clearFilters();
|
||||
expect(scope.startDate).toBe(null);
|
||||
expect(scope.endDate).toBe(null);
|
||||
$httpBackend.expectGET('http://foo.bar/v1/results?page=1').respond(fakeResponse);
|
||||
$httpBackend.flush();
|
||||
expect(scope.data).toEqual(fakeResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resultsReportController', function() {
|
||||
var scope, ctrl, $httpBackend, refstackApiUrl, stateparams;
|
||||
var fakeResultResponse = {'results': ['test_id_1']}
|
||||
var fakeCapabilityResponse = {'platform': {'required': ['compute']},
|
||||
'components': {
|
||||
'compute': {
|
||||
'required': ['cap_id_1'],
|
||||
'advisory': [],
|
||||
'deprecated': [],
|
||||
'removed': []
|
||||
}
|
||||
},
|
||||
'capabilities': {
|
||||
'cap_id_1': {
|
||||
'status': 'required',
|
||||
'flagged': [],
|
||||
'tests': ['test_id_1', 'test_id_2']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
module('refstackApp');
|
||||
module(function($provide) {
|
||||
$provide.constant('refstackApiUrl', 'http://foo.bar/v1');
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
stateparams = {testID: 1234};
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('resultsReportController', {$scope: scope, $stateParams: stateparams});
|
||||
}));
|
||||
|
||||
it('should get the results for a specific test ID and also the relevant capabilities', function() {
|
||||
$httpBackend.expectGET('http://foo.bar/v1/results/1234').respond(fakeResultResponse);
|
||||
$httpBackend.expectGET('assets/capabilities/2015.03.json').respond(fakeCapabilityResponse);
|
||||
$httpBackend.flush();
|
||||
expect(scope.resultsData).toEqual(fakeResultResponse);
|
||||
expect(scope.capabilityData).toEqual(fakeCapabilityResponse);
|
||||
});
|
||||
|
||||
it('should be able to sort the results into a capability object', function() {
|
||||
scope.resultsData = fakeResultResponse;
|
||||
scope.capabilityData = fakeCapabilityResponse;
|
||||
scope.buildCapabilityObject();
|
||||
var expectedCapsObject = {'required': {'caps': [{'id': 'cap_id_1',
|
||||
'passedTests': ['test_id_1'],
|
||||
'notPassedTests': ['test_id_2']}],
|
||||
'count': 2, 'passedCount': 1},
|
||||
'advisory': {'caps': [], 'count': 0, 'passedCount': 0},
|
||||
'deprecated': {'caps': [], 'count': 0, 'passedCount': 0},
|
||||
'removed': {'caps': [], 'count': 0, 'passedCount': 0}};
|
||||
expect(scope.caps).toEqual(expectedCapsObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user