diff options
author | SerenaFeng <feng.xiaowei@zte.com.cn> | 2017-05-12 01:49:57 +0800 |
---|---|---|
committer | SerenaFeng <feng.xiaowei@zte.com.cn> | 2017-05-12 10:11:57 +0800 |
commit | f562c31e824f573d9a3254a1eacb4981b29290eb (patch) | |
tree | fd5526fc049fae9760da27b64318ad3fe5ce5767 /utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines | |
parent | a16b903c9765049bd28102c812b8307090a97e16 (diff) |
add web portal framework for TestAPI
Change-Id: I62cea8b59ffe6a6cde98051c130f4502c07d3557
Signed-off-by: SerenaFeng <feng.xiaowei@zte.com.cn>
Diffstat (limited to 'utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines')
4 files changed, 498 insertions, 0 deletions
diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelines.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelines.html new file mode 100644 index 000000000..1dd39ff17 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelines.html @@ -0,0 +1,80 @@ +<h3>OpenStack Powered™ Guidelines</h3> + +<!-- Guideline Filters --> +<div class="row"> + <div class="col-md-3"> + <strong>Version:</strong> + <!-- Slicing the version file name here gets rid of the '.json' file extension --> + <select ng-model="ctrl.version" + ng-change="ctrl.update()" + class="form-control" + ng-options="versionFile.slice(0,-5) for versionFile in ctrl.versionList"> + </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="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> + </select> + </div> +</div> + +<br /> +<div ng-if="ctrl.guidelines"> + <strong>Guideline Status:</strong> + {{ctrl.guidelines.status | capitalize}} +</div> + +<div ng-show="ctrl.guidelines"> + <strong>Corresponding OpenStack Releases:</strong> + <ul class="list-inline"> + <li ng-repeat="release in ctrl.guidelines.releases"> + {{release | capitalize}} + </li> + </ul> +</div> + +<strong>Capability Status:</strong> +<div class="checkbox"> + <label> + <input type="checkbox" ng-model="ctrl.status.required"> + <span class="required">Required</span> + </label> + <label> + <input type="checkbox" ng-model="ctrl.status.advisory"> + <span class="advisory">Advisory</span> + </label> + <label> + <input type="checkbox" ng-model="ctrl.status.deprecated"> + <span class="deprecated">Deprecated</span> + </label> + <label> + <input type="checkbox" ng-model="ctrl.status.removed"> + <span class="removed">Removed</span> + </label> + <a class="test-list-dl pull-right" + title="Get a test list for capabilities matching selected statuses." + ng-click="ctrl.openTestListModal()"> + + Test List <span class="glyphicon glyphicon-file"></span> + </a> +</div> +<!-- End Capability Filters --> + +<p><small>Tests marked with <span class="glyphicon glyphicon-flag text-warning"></span> are tests flagged by Interop Working Group.</small></p> + +<!-- Loading animation divs --> +<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="ctrl.detailsTemplate"></div> + +<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> + {{ctrl.error}} +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelinesController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelinesController.js new file mode 100644 index 000000000..a6f4258a2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelinesController.js @@ -0,0 +1,322 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('GuidelinesController', GuidelinesController); + + GuidelinesController.$inject = ['$http', '$uibModal', 'testapiApiUrl']; + + /** + * TestAPI Guidelines Controller + * This controller is for the '/guidelines' page where a user can browse + * through tests belonging to Interop WG defined capabilities. + */ + function GuidelinesController($http, $uibModal, testapiApiUrl) { + var ctrl = this; + + ctrl.getVersionList = getVersionList; + ctrl.update = update; + ctrl.updateTargetCapabilities = updateTargetCapabilities; + ctrl.filterStatus = filterStatus; + ctrl.getObjectLength = getObjectLength; + ctrl.openTestListModal = openTestListModal; + + /** The target OpenStack marketing program to show capabilities for. */ + ctrl.target = 'platform'; + + /** The various possible capability statuses. */ + ctrl.status = { + required: true, + advisory: false, + deprecated: false, + removed: false + }; + + /** + * The template to load for displaying capability details. + */ + ctrl.detailsTemplate = 'components/guidelines/partials/' + + 'guidelineDetails.html'; + + /** + * Retrieve an array of available guideline files from the TestAPI + * 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 = testapiApiUrl + '/guidelines'; + ctrl.versionsRequest = + $http.get(content_url).success(function (data) { + ctrl.versionList = data.sort().reverse(); + // Default to the first approved guideline which is expected + // to be at index 1. + ctrl.version = ctrl.versionList[1]; + ctrl.update(); + }).error(function (error) { + ctrl.showError = true; + ctrl.error = 'Error retrieving version list: ' + + angular.toJson(error); + }); + } + + /** + * This will contact the TestAPI API server to retrieve the JSON + * content of the guideline file corresponding to the selected + * version. + */ + function update() { + var content_url = testapiApiUrl + '/guidelines/' + ctrl.version; + ctrl.capsRequest = + $http.get(content_url).success(function (data) { + ctrl.guidelines = data; + ctrl.updateTargetCapabilities(); + }).error(function (error) { + ctrl.showError = true; + ctrl.guidelines = null; + ctrl.error = 'Error retrieving guideline content: ' + + angular.toJson(error); + }); + } + + /** + * This will update the scope's 'targetCapabilities' object with + * capabilities belonging to the selected OpenStack marketing program + * (programs typically correspond to 'components' in the Interop WG + * schema). Each capability will have its status mapped to it. + */ + function updateTargetCapabilities() { + ctrl.targetCapabilities = {}; + var components = ctrl.guidelines.components; + var targetCaps = ctrl.targetCapabilities; + + // 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.guidelines.platform.required; + + // This will contain status priority values, where lower + // values mean higher priorities. + var statusMap = { + required: 1, + advisory: 2, + deprecated: 3, + removed: 4 + }; + + // 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; + } + + /** + * This will open the modal that will show a list of all tests + * belonging to capabilities with the selected status(es). + */ + function openTestListModal() { + $uibModal.open({ + templateUrl: '/components/guidelines/partials' + + '/testListModal.html', + backdrop: true, + windowClass: 'modal', + animation: true, + controller: 'TestListModalController as modal', + size: 'lg', + resolve: { + version: function () { + return ctrl.version.slice(0, -5); + }, + target: function () { + return ctrl.target; + }, + status: function () { + return ctrl.status; + } + } + }); + } + + ctrl.getVersionList(); + } + + angular + .module('testapiApp') + .controller('TestListModalController', TestListModalController); + + TestListModalController.$inject = [ + '$uibModalInstance', '$http', 'version', + 'target', 'status', 'testapiApiUrl' + ]; + + /** + * Test List Modal Controller + * This controller is for the modal that appears if a user wants to see the + * test list corresponding to Interop WG capabilities with the selected + * statuses. + */ + function TestListModalController($uibModalInstance, $http, version, + target, status, testapiApiUrl) { + + var ctrl = this; + + ctrl.version = version; + ctrl.target = target; + ctrl.status = status; + ctrl.close = close; + ctrl.updateTestListString = updateTestListString; + + ctrl.aliases = true; + ctrl.flagged = false; + + // Check if the API URL is absolute or relative. + if (testapiApiUrl.indexOf('http') > -1) { + ctrl.url = testapiApiUrl; + } + else { + ctrl.url = location.protocol + '//' + location.host + + testapiApiUrl; + } + + /** + * This function will close/dismiss the modal. + */ + function close() { + $uibModalInstance.dismiss('exit'); + } + + /** + * This function will return a list of statuses based on which ones + * are selected. + */ + function getStatusList() { + var statusList = []; + angular.forEach(ctrl.status, function(value, key) { + if (value) { + statusList.push(key); + } + }); + return statusList; + } + + /** + * This will get the list of tests from the API and update the + * controller's test list string variable. + */ + function updateTestListString() { + var statuses = getStatusList(); + if (!statuses.length) { + ctrl.error = 'No tests matching selected criteria.'; + return; + } + ctrl.testListUrl = [ + ctrl.url, '/guidelines/', ctrl.version, '/tests?', + 'target=', ctrl.target, '&', + 'type=', statuses.join(','), '&', + 'alias=', ctrl.aliases.toString(), '&', + 'flag=', ctrl.flagged.toString() + ].join(''); + ctrl.testListRequest = + $http.get(ctrl.testListUrl). + then(function successCallback(response) { + ctrl.error = null; + ctrl.testListString = response.data; + if (!ctrl.testListString) { + ctrl.testListCount = 0; + } + else { + ctrl.testListCount = + ctrl.testListString.split('\n').length; + } + }, function errorCallback(response) { + ctrl.testListString = null; + ctrl.testListCount = null; + if (angular.isObject(response.data) && + response.data.message) { + ctrl.error = 'Error retrieving test list: ' + + response.data.message; + } + else { + ctrl.error = 'Unknown error retrieving test list.'; + } + }); + } + + updateTestListString(); + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/guidelineDetails.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/guidelineDetails.html new file mode 100644 index 000000000..f020c9a09 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/guidelineDetails.html @@ -0,0 +1,50 @@ +<!-- +HTML for guidelines page for all OpenStack Powered (TM) guideline schemas +This expects the JSON data of the guidelines file to be stored in scope +variable 'guidelines'. +--> + +<ol ng-show="ctrl.guidelines" class="capabilities"> + <li class="capability-list-item" ng-repeat="capability in ctrl.guidelines.capabilities | arrayConverter | filter:ctrl.filterStatus | orderBy:'id'"> + <span class="capability-name">{{capability.id}}</span><br /> + <em>{{capability.description}}</em><br /> + Status: <span class="{{ctrl.targetCapabilities[capability.id]}}">{{ctrl.targetCapabilities[capability.id]}}</span><br /> + <span ng-if="capability.project">Project: {{capability.project | capitalize}}<br /></span> + <a ng-click="showAchievements = !showAchievements">Achievements ({{capability.achievements.length}})</a><br /> + <ol uib-collapse="!showAchievements" class="list-inline"> + <li ng-repeat="achievement in capability.achievements"> + {{achievement}} + </li> + </ol> + + <a ng-click="showTests = !showTests">Tests ({{ctrl.getObjectLength(capability.tests)}})</a> + <ul uib-collapse="!showTests"> + <li ng-if="ctrl.guidelines.schema === '1.2'" ng-repeat="test in capability.tests"> + <span ng-class="{'glyphicon glyphicon-flag text-warning': capability.flagged.indexOf(test) > -1}"></span> + {{test}} + </li> + <li ng-if="ctrl.guidelines.schema > '1.2'" ng-repeat="(testName, testDetails) in capability.tests"> + <span ng-class="{'glyphicon glyphicon-flag text-warning': testDetails.flagged}" title="{{testDetails.flagged.reason}}"></span> + {{testName}} + <div class="test-detail" ng-if="testDetails.aliases"> + <strong>Aliases:</strong> + <ul><li ng-repeat="alias in testDetails.aliases">{{alias}}</li></ul> + </div> + </li> + </ul> + </li> +</ol> + +<div ng-show="ctrl.guidelines" class="criteria"> + <hr> + <h4><a ng-click="showCriteria = !showCriteria">Criteria</a></h4> + <div uib-collapse="showCriteria"> + <ul> + <li ng-repeat="(key, criterion) in ctrl.guidelines.criteria"> + <span class="criterion-name">{{criterion.name}}</span><br /> + <em>{{criterion.Description}}</em><br /> + Weight: {{criterion.weight}} + </li> + </ul> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/testListModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/testListModal.html new file mode 100644 index 000000000..5b1d698d5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/testListModal.html @@ -0,0 +1,46 @@ +<div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" aria-hidden="true" ng-click="modal.close()">×</button> + <h4>Test List ({{modal.testListCount}})</h4> + <p>Use this test list with <a title="testapi-client" target="_blank"href="https://github.com/openstack/testapi-client">testapi-client</a> + to run only tests in the {{modal.version}} OpenStack Powered™ guideline from capabilities with the following statuses: + </p> + <ul class="list-inline"> + <li class="required" ng-if="modal.status.required"> Required</li> + <li class="advisory" ng-if="modal.status.advisory"> Advisory</li> + <li class="deprecated" ng-if="modal.status.deprecated"> Deprecated</li> + <li class="removed" ng-if="modal.status.removed"> Removed</li> + </ul> + <div class="checkbox checkbox-test-list"> + <label><input type="checkbox" ng-model="modal.aliases" ng-change="modal.updateTestListString()">Aliases</label> + <span class="glyphicon glyphicon-info-sign info-hover" aria-hidden="true" + title="Include test aliases as tests may have been renamed over time. It does not hurt to include these."></span> + + <label><input type="checkbox" ng-model="modal.flagged" ng-change="modal.updateTestListString()">Flagged</label> + <span class="glyphicon glyphicon-info-sign info-hover" aria-hidden="true" + title="Include flagged tests."> + </span> + </div> + <p ng-hide="modal.error"> Alternatively, get the test list directly from the API on your CLI:</p> + <code ng-hide="modal.error">wget "{{modal.testListUrl}}" -O {{modal.version}}-test-list.txt</code> + </div> + <div class="modal-body tests-modal-content"> + <div cg-busy="{promise:modal.testListRequest,message:'Loading'}"></div> + <div ng-show="modal.error" 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 class="form-group"> + <textarea class="form-control" rows="16" id="tests" wrap="off">{{modal.testListString}}</textarea> + </div> + </div> + <div class="modal-footer"> + <a target="_blank" href="{{modal.testListUrl}}" download="{{modal.version + '-test-list.txt'}}"> + <button class="btn btn-primary" ng-if="modal.testListCount > 0" type="button"> + Download + </button> + </a> + <button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button> + </div> +</div> |