Add ability to edit test meta on report page
The ability to edit test metadata on the results report page might be a bit more intuitive for people, so this patch adds that. This also better allows Foundation admins to manage any specific test result. Change-Id: I629408c0f1b05654742aad02129a3872827a8004
This commit is contained in:
parent
0e8721e4ad
commit
39a111ce7d
@ -0,0 +1,65 @@
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" aria-hidden="true" ng-click="modal.close()">×</button>
|
||||
<h4>Edit Test Run Metadata</h4>
|
||||
<p>Make changes to your test metadata.</p>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<strong>Publicly Shared:</strong>
|
||||
<select ng-model="modal.metaCopy.shared"
|
||||
class="form-control">
|
||||
<option value="true">Yes</option>
|
||||
<option value="">No</option>
|
||||
</select>
|
||||
<br />
|
||||
<strong>Associated Guideline:</strong>
|
||||
<select ng-model="modal.metaCopy.guideline"
|
||||
ng-options="o as o.slice(0, -5) for o in modal.versionList"
|
||||
class="form-control">
|
||||
<option value="">None</option>
|
||||
</select>
|
||||
<br />
|
||||
<strong>Associated Target Program:</strong>
|
||||
<select ng-model="modal.metaCopy.target"
|
||||
class="form-control">
|
||||
<option value="">None</option>
|
||||
<option value="platform">OpenStack Powered Platform</option>
|
||||
<option value="compute">OpenStack Powered Compute</option>
|
||||
<option value="object">OpenStack Powered Object Storage</option>
|
||||
</select>
|
||||
<hr>
|
||||
<strong>Associated Product:</strong>
|
||||
<select ng-options="product as product.name for product in modal.products | arrayConverter | orderBy: 'name' track by product.id"
|
||||
ng-model="modal.selectedProduct"
|
||||
ng-change="modal.getProductVersions()"
|
||||
class="form-control">
|
||||
<option value="">-- No Product --</option>
|
||||
</select>
|
||||
|
||||
<span ng-if="modal.productVersions.length">
|
||||
<strong>Product Version:</strong>
|
||||
<select ng-options="version as version.version for version in modal.productVersions | orderBy: 'version' track by version.id"
|
||||
ng-model="modal.selectedVersion"
|
||||
class="form-control">
|
||||
</select>
|
||||
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div ng-show="modal.showError" class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
|
||||
<span class="sr-only">Error:</span>
|
||||
{{modal.error}}
|
||||
</div>
|
||||
<div ng-show="modal.showSuccess" class="alert alert-success" role="success">
|
||||
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
|
||||
<span class="sr-only">Success:</span>
|
||||
Changes saved successfully.
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" type="button" ng-click="modal.saveChanges()">Save Changes</button>
|
||||
<button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button>
|
||||
</div>
|
||||
</div>
|
@ -14,6 +14,12 @@
|
||||
</a>
|
||||
</div>
|
||||
<hr>
|
||||
<div ng-show="ctrl.isResultAdmin()">
|
||||
<strong>Publicly Shared:</strong>
|
||||
<span ng-if="ctrl.resultsData.meta.shared">Yes</span>
|
||||
<span ng-if="!ctrl.resultsData.meta.shared">No</span>
|
||||
<br />
|
||||
</div>
|
||||
<div ng-show="ctrl.resultsData.product_version">
|
||||
<strong>Product:</strong>
|
||||
{{ctrl.resultsData.product_version.product_info.name}}
|
||||
@ -29,12 +35,16 @@
|
||||
<strong>Associated Target Program:</strong>
|
||||
{{ctrl.targetMappings[ctrl.resultsData.meta.target]}}
|
||||
</div>
|
||||
<div ng-show="ctrl.resultsData.verification_status">
|
||||
<strong>Verified:</strong>
|
||||
<span class="yes">YES</span>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="pull-right">
|
||||
<div ng-show="ctrl.isResultAdmin() && !ctrl.resultsData.verification_status">
|
||||
<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 class="btn btn-info" ng-click="ctrl.openEditTestModal()">Edit</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 ng-show="ctrl.resultsData.user_role === 'foundation'">
|
||||
@ -112,8 +122,6 @@
|
||||
<span ng-if="ctrl.nonFlagPassCount !== ctrl.totalNonFlagCount" class="no">NO</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p ng-if="ctrl.resultsData.verification_status"><strong>Verified:</strong>
|
||||
<span class="yes">YES</span>
|
||||
|
||||
<hr>
|
||||
<h4>Capability Overview</h4>
|
||||
|
@ -53,6 +53,7 @@
|
||||
ctrl.getCapabilityTestCount = getCapabilityTestCount;
|
||||
ctrl.getStatusTestCount = getStatusTestCount;
|
||||
ctrl.openFullTestListModal = openFullTestListModal;
|
||||
ctrl.openEditTestModal = openEditTestModal;
|
||||
|
||||
/** The testID extracted from the URL route. */
|
||||
ctrl.testId = $stateParams.testID;
|
||||
@ -610,6 +611,27 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will open the modal that will all a user to edit test run
|
||||
* metadata.
|
||||
*/
|
||||
function openEditTestModal() {
|
||||
$uibModal.open({
|
||||
templateUrl: '/components/results-report/partials' +
|
||||
'/editTestModal.html',
|
||||
backdrop: true,
|
||||
windowClass: 'modal',
|
||||
animation: true,
|
||||
controller: 'EditTestModalController as modal',
|
||||
size: 'lg',
|
||||
resolve: {
|
||||
resultsData: function () {
|
||||
return ctrl.resultsData;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getResults();
|
||||
}
|
||||
|
||||
@ -644,4 +666,197 @@
|
||||
return ctrl.tests.sort().join('\n');
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('refstackApp')
|
||||
.controller('EditTestModalController', EditTestModalController);
|
||||
|
||||
EditTestModalController.$inject = [
|
||||
'$uibModalInstance', '$http', '$state', 'raiseAlert',
|
||||
'refstackApiUrl', 'resultsData'
|
||||
];
|
||||
|
||||
/**
|
||||
* Edit Test Modal Controller
|
||||
* This controller is for the modal that appears if a user wants to edit
|
||||
* test run metadata.
|
||||
*/
|
||||
function EditTestModalController($uibModalInstance, $http, $state,
|
||||
raiseAlert, refstackApiUrl, resultsData) {
|
||||
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.getVersionList = getVersionList;
|
||||
ctrl.getUserProducts = getUserProducts;
|
||||
ctrl.associateProductVersion = associateProductVersion;
|
||||
ctrl.getProductVersions = getProductVersions;
|
||||
ctrl.saveChanges = saveChanges;
|
||||
|
||||
ctrl.resultsData = resultsData;
|
||||
ctrl.metaCopy = angular.copy(resultsData.meta);
|
||||
ctrl.prodVersionCopy = angular.copy(resultsData.product_version);
|
||||
|
||||
ctrl.getVersionList();
|
||||
ctrl.getUserProducts();
|
||||
|
||||
/**
|
||||
* Retrieve an array of available capability files from the Refstack
|
||||
* API server, sort this array reverse-alphabetically, and store it in
|
||||
* a scoped variable.
|
||||
* Sample API return array: ["2015.03.json", "2015.04.json"]
|
||||
*/
|
||||
function getVersionList() {
|
||||
if (ctrl.versionList) {
|
||||
return;
|
||||
}
|
||||
var content_url = refstackApiUrl + '/guidelines';
|
||||
ctrl.versionsRequest =
|
||||
$http.get(content_url).success(function (data) {
|
||||
ctrl.versionList = data.sort().reverse();
|
||||
}).error(function (error) {
|
||||
raiseAlert('danger', error.title,
|
||||
'Unable to retrieve version list');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products user has management rights to or all products depending
|
||||
* on the passed in parameter value.
|
||||
*/
|
||||
function getUserProducts() {
|
||||
var contentUrl = refstackApiUrl + '/products';
|
||||
ctrl.productsRequest =
|
||||
$http.get(contentUrl).success(function (data) {
|
||||
ctrl.products = {};
|
||||
angular.forEach(data.products, function(prod) {
|
||||
if (prod.can_manage) {
|
||||
ctrl.products[prod.id] = prod;
|
||||
}
|
||||
});
|
||||
if (ctrl.prodVersionCopy) {
|
||||
ctrl.selectedProduct = ctrl.products[
|
||||
ctrl.prodVersionCopy.product_info.id
|
||||
];
|
||||
}
|
||||
ctrl.getProductVersions();
|
||||
}).error(function (error) {
|
||||
ctrl.products = null;
|
||||
ctrl.showError = true;
|
||||
ctrl.error =
|
||||
'Error retrieving Products listing from server: ' +
|
||||
angular.toJson(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a PUT request to the API server to associate a product with
|
||||
* a test result.
|
||||
*/
|
||||
function associateProductVersion() {
|
||||
var verId = (ctrl.selectedVersion ?
|
||||
ctrl.selectedVersion.id : null);
|
||||
var testId = resultsData.id;
|
||||
var url = refstackApiUrl + '/results/' + testId;
|
||||
ctrl.associateRequest = $http.put(url, {'product_version_id':
|
||||
verId})
|
||||
.error(function (error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.showSuccess = false;
|
||||
ctrl.error =
|
||||
'Error associating product version with test run: ' +
|
||||
angular.toJson(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all versions for a product.
|
||||
*/
|
||||
function getProductVersions() {
|
||||
if (!ctrl.selectedProduct) {
|
||||
ctrl.productVersions = [];
|
||||
ctrl.selectedVersion = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var url = refstackApiUrl + '/products/' +
|
||||
ctrl.selectedProduct.id + '/versions';
|
||||
ctrl.getVersionsRequest = $http.get(url)
|
||||
.success(function (data) {
|
||||
ctrl.productVersions = data;
|
||||
if (ctrl.prodVersionCopy &&
|
||||
ctrl.prodVersionCopy.product_info.id ==
|
||||
ctrl.selectedProduct.id) {
|
||||
ctrl.selectedVersion = ctrl.prodVersionCopy;
|
||||
}
|
||||
else {
|
||||
angular.forEach(data, function(ver) {
|
||||
if (!ver.version) {
|
||||
ctrl.selectedVersion = ver;
|
||||
}
|
||||
});
|
||||
}
|
||||
}).error(function (error) {
|
||||
raiseAlert('danger', error.title, error.detail);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a PUT request to the server with the changes.
|
||||
*/
|
||||
function saveChanges() {
|
||||
ctrl.showError = false;
|
||||
ctrl.showSuccess = false;
|
||||
var metaBaseUrl = [
|
||||
refstackApiUrl, '/results/', resultsData.id, '/meta/'
|
||||
].join('');
|
||||
var metaFields = ['target', 'guideline', 'shared'];
|
||||
var meta = ctrl.metaCopy;
|
||||
angular.forEach(metaFields, function(field) {
|
||||
var oldMetaValue = (field in ctrl.resultsData.meta) ?
|
||||
ctrl.resultsData.meta[field] : '';
|
||||
if (field in meta && oldMetaValue != meta[field]) {
|
||||
var metaUrl = metaBaseUrl + field;
|
||||
if (meta[field]) {
|
||||
ctrl.assocRequest = $http.post(metaUrl, meta[field])
|
||||
.success(function(data) {
|
||||
ctrl.resultsData.meta[field] = meta[field];
|
||||
})
|
||||
.error(function (error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.showSuccess = false;
|
||||
ctrl.error =
|
||||
'Error associating metadata with ' +
|
||||
'test run: ' + angular.toJson(error);
|
||||
});
|
||||
}
|
||||
else {
|
||||
ctrl.unassocRequest = $http.delete(metaUrl)
|
||||
.success(function (data) {
|
||||
delete ctrl.resultsData.meta[field];
|
||||
delete meta[field];
|
||||
})
|
||||
.error(function (error) {
|
||||
ctrl.showError = true;
|
||||
ctrl.showSuccess = false;
|
||||
ctrl.error =
|
||||
'Error associating metadata with ' +
|
||||
'test run: ' + angular.toJson(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
ctrl.associateProductVersion();
|
||||
if (!ctrl.showError) {
|
||||
ctrl.showSuccess = true;
|
||||
$state.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will close/dismiss the modal.
|
||||
*/
|
||||
ctrl.close = function () {
|
||||
$uibModalInstance.dismiss('exit');
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
@ -734,6 +734,17 @@ describe('Refstack controllers', function () {
|
||||
ctrl.openFullTestListModal();
|
||||
expect(modal.open).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have a method to open a modal for editing test metadata',
|
||||
function () {
|
||||
var modal;
|
||||
inject(function ($uibModal) {
|
||||
modal = $uibModal;
|
||||
});
|
||||
spyOn(modal, 'open');
|
||||
ctrl.openEditTestModal();
|
||||
expect(modal.open).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('FullTestListModalController', function () {
|
||||
@ -766,6 +777,72 @@ describe('Refstack controllers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('EditTestModalController', function () {
|
||||
var modalInstance, ctrl, state;
|
||||
var fakeResultsData = {
|
||||
'results': ['test_id_1'],
|
||||
'id': 'some-id',
|
||||
'meta': {
|
||||
'public_key': 'ssh-rsa', 'guideline': '2015.04.json',
|
||||
'target': 'object'
|
||||
}
|
||||
};
|
||||
var fakeProdResp = {'products': [{'id': 1234}]};
|
||||
var fakeVersionResp = [{'id': 'ver1', 'version': '1.0'},
|
||||
{'id': 'ver2', 'version': null}];
|
||||
|
||||
beforeEach(inject(function ($controller) {
|
||||
modalInstance = {
|
||||
dismiss: jasmine.createSpy('modalInstance.dismiss')
|
||||
};
|
||||
state = {
|
||||
reload: jasmine.createSpy('state.reload')
|
||||
};
|
||||
ctrl = $controller('EditTestModalController',
|
||||
{$uibModalInstance: modalInstance, $state: state,
|
||||
resultsData: fakeResultsData}
|
||||
);
|
||||
$httpBackend.when('GET', fakeApiUrl +
|
||||
'/guidelines').respond(['2015.03.json', '2015.04.json']);
|
||||
$httpBackend.when('GET', fakeApiUrl + '/products')
|
||||
.respond(200, fakeResultsData);
|
||||
$httpBackend.when('GET', fakeApiUrl +
|
||||
'/products/1234/versions').respond(fakeVersionResp);
|
||||
}));
|
||||
|
||||
it('should be able to get product versions', function () {
|
||||
ctrl.selectedProduct = {'id': '1234'};
|
||||
ctrl.products = null;
|
||||
$httpBackend.expectGET(fakeApiUrl + '/products/1234/versions')
|
||||
.respond(200, fakeVersionResp);
|
||||
ctrl.getProductVersions();
|
||||
$httpBackend.flush();
|
||||
expect(ctrl.productVersions).toEqual(fakeVersionResp);
|
||||
var expected = {'id': 'ver2', 'version': null};
|
||||
expect(ctrl.selectedVersion).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should have a method to save all changes made.', function () {
|
||||
ctrl.metaCopy.target = 'platform';
|
||||
ctrl.metaCopy.shared = 'true';
|
||||
ctrl.selectedVersion = {'id': 'ver2', 'version': null};
|
||||
ctrl.saveChanges();
|
||||
// Only meta changed should send a POST request.
|
||||
$httpBackend.expectPOST(
|
||||
fakeApiUrl + '/results/some-id/meta/target',
|
||||
'platform')
|
||||
.respond(201, '');
|
||||
$httpBackend.expectPOST(
|
||||
fakeApiUrl + '/results/some-id/meta/shared',
|
||||
'true')
|
||||
.respond(201, '');
|
||||
$httpBackend.expectPUT(fakeApiUrl + '/results/some-id',
|
||||
{'product_version_id': 'ver2'})
|
||||
.respond(201);
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe('TestRaiseAlertModalController', function() {
|
||||
var data, modalInstance, ctrl;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user