Add results and test report page

This is the groundwork for the community results listing page
and the test run report page. The report page is fairly basic, primarily
showing how results stack up against defcore capabilities. Design is subject
to change, but this gets the ball rolling. A config.json.sample was also added.
The UI now expects a config.json in the root which will contain the
Refstack API URL.

Change-Id: Id7a376d0bccda5cbb5daf05e52a2c174ad40b497
This commit is contained in:
Paul Van Eck 2015-04-27 02:40:47 -07:00
parent 8f972ce693
commit c094c27eff
14 changed files with 576 additions and 11 deletions

View File

@ -7,3 +7,4 @@ dist
node_modules
npm-debug.log
app/assets/lib
app/config.json

View File

@ -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:

View File

@ -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({});
});
});

View File

@ -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;
}

View File

@ -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>

View 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>

View File

@ -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;
}
}
]);

View File

@ -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>

View 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();
};
}]);

View File

@ -0,0 +1 @@
{"refstackApiUrl": "http://api.refstack.net/v1"}

View File

@ -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>

View File

@ -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": {

View File

@ -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',

View File

@ -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);
});
});
});