From 426aa0231af6cb1911ac7b6fbd36062e338af05a Mon Sep 17 00:00:00 2001 From: SerenaFeng Date: Fri, 27 Oct 2017 16:58:49 +0800 Subject: remove components out side of 3rd_party components are implemented by TestAPI team, they are not the third party code, put them under opnfv_testapi/ui directory Change-Id: Ia0f5e2afe4bcb12bcb74d3a8d78fb28fe1432bec Signed-off-by: SerenaFeng --- .../ui/results-report/partials/editTestModal.html | 65 ++ .../results-report/partials/fullTestListModal.html | 13 + .../ui/results-report/partials/reportDetails.html | 87 +++ .../ui/results-report/resultsReport.html | 185 +++++ .../ui/results-report/resultsReportController.js | 869 +++++++++++++++++++++ 5 files changed, 1219 insertions(+) create mode 100644 testapi/opnfv_testapi/ui/results-report/partials/editTestModal.html create mode 100644 testapi/opnfv_testapi/ui/results-report/partials/fullTestListModal.html create mode 100644 testapi/opnfv_testapi/ui/results-report/partials/reportDetails.html create mode 100644 testapi/opnfv_testapi/ui/results-report/resultsReport.html create mode 100644 testapi/opnfv_testapi/ui/results-report/resultsReportController.js (limited to 'testapi/opnfv_testapi/ui/results-report') diff --git a/testapi/opnfv_testapi/ui/results-report/partials/editTestModal.html b/testapi/opnfv_testapi/ui/results-report/partials/editTestModal.html new file mode 100644 index 0000000..583c9b9 --- /dev/null +++ b/testapi/opnfv_testapi/ui/results-report/partials/editTestModal.html @@ -0,0 +1,65 @@ + diff --git a/testapi/opnfv_testapi/ui/results-report/partials/fullTestListModal.html b/testapi/opnfv_testapi/ui/results-report/partials/fullTestListModal.html new file mode 100644 index 0000000..6db198b --- /dev/null +++ b/testapi/opnfv_testapi/ui/results-report/partials/fullTestListModal.html @@ -0,0 +1,13 @@ + diff --git a/testapi/opnfv_testapi/ui/results-report/partials/reportDetails.html b/testapi/opnfv_testapi/ui/results-report/partials/reportDetails.html new file mode 100644 index 0000000..517e569 --- /dev/null +++ b/testapi/opnfv_testapi/ui/results-report/partials/reportDetails.html @@ -0,0 +1,87 @@ + + + + + {{status | capitalize}} + + (Total: {{ctrl.caps[status].caps.length}} capabilities, {{ctrl.caps[status].count}} tests) + + ({{ctrl.testStatus | capitalize}}: {{ctrl.getStatusTestCount(status)}} tests) + + + + + +
    +
  1. + + + {{capability.id}} + + + [{{ctrl.getCapabilityTestCount(capability)}}] + + + [{{capability.passedTests.length}}/{{capability.passedTests.length + + capability.notPassedTests.length}}] + + +
      + +
    • + + + + + {{test}} + — + [Aliases] +
      +
      • {{alias}}
      +
      +
      +
    • + + + +
    • + + + + + {{test}} + — + [Aliases] +
      +
      • {{alias}}
      +
      +
      +
    • + +
    +
  2. +
+
diff --git a/testapi/opnfv_testapi/ui/results-report/resultsReport.html b/testapi/opnfv_testapi/ui/results-report/resultsReport.html new file mode 100644 index 0000000..5527121 --- /dev/null +++ b/testapi/opnfv_testapi/ui/results-report/resultsReport.html @@ -0,0 +1,185 @@ +

Test Run Results

+ +
+
+
+
+ Test ID: {{ctrl.testId}}
+
Cloud ID: {{ctrl.resultsData.cpid}}
+ Upload Date: {{ctrl.resultsData.created_at}} UTC
+ Duration: {{ctrl.resultsData.duration_seconds}} seconds
+ Total Number of Passed Tests: + + {{ctrl.resultsData.results.length}} + +
+
+
+ Publicly Shared: + Yes + No +
+
+
+ Product: + {{ctrl.resultsData.product_version.product_info.name}} + + ({{ctrl.resultsData.product_version.version}}) +
+
+
+ Associated Guideline: + {{ctrl.resultsData.meta.guideline.slice(0, -5)}} +
+
+ Associated Target Program: + {{ctrl.targetMappings[ctrl.resultsData.meta.target]}} +
+
+ Verified: + YES +
+
+
+ +
+
+ + +
+
+
+
+ +
+
+
+
+
+ +
+

See how these results stack up against Interop Working Group capabilities and OpenStack + target marketing programs. +

+ + +
+
+ Guideline Version: + + +
+
+ Target Program: + +
+
+ + +
+
+ Guideline Status: + {{ctrl.guidelineData.status | capitalize}} +
+ + Corresponding OpenStack Releases: +
    +
  • + {{release | capitalize}} +
  • +
+
+ +
+ Status: +

This cloud passes {{ctrl.requiredPassPercent | number:1}}% + ({{ctrl.caps.required.passedCount}}/{{ctrl.caps.required.count}}) + of the tests in the {{ctrl.version.slice(0, -5)}} required capabilities for the + {{ctrl.targetMappings[target]}} program.
+ Excluding flagged tests, this cloud passes + {{ctrl.nonFlagRequiredPassPercent | number:1}}% + ({{ctrl.nonFlagPassCount}}/{{ctrl.totalNonFlagCount}}) + of the required tests. +

+ +

Compliance with {{ctrl.version.slice(0, -5)}}: + + YES + NO + +

+ +
+

Capability Overview

+ + Test Filters:
+
+ + + + +
+ + + + + +
+ + +
+ + +
+ + +
+
+
+ +
+
+
+
+
+ + diff --git a/testapi/opnfv_testapi/ui/results-report/resultsReportController.js b/testapi/opnfv_testapi/ui/results-report/resultsReportController.js new file mode 100644 index 0000000..591ad40 --- /dev/null +++ b/testapi/opnfv_testapi/ui/results-report/resultsReportController.js @@ -0,0 +1,869 @@ +/* + * 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('ResultsReportController', ResultsReportController); + + ResultsReportController.$inject = [ + '$http', '$stateParams', '$window', + '$uibModal', 'testapiApiUrl', 'raiseAlert' + ]; + + /** + * TestAPI Results Report Controller + * This controller is for the '/results/' page where a user can + * view details for a specific test run. + */ + function ResultsReportController($http, $stateParams, $window, + $uibModal, testapiApiUrl, raiseAlert) { + + var ctrl = this; + + ctrl.getVersionList = getVersionList; + ctrl.getResults = getResults; + ctrl.isResultAdmin = isResultAdmin; + ctrl.isShared = isShared; + ctrl.shareTestRun = shareTestRun; + ctrl.deleteTestRun = deleteTestRun; + ctrl.updateVerificationStatus = updateVerificationStatus; + ctrl.updateGuidelines = updateGuidelines; + ctrl.getTargetCapabilities = getTargetCapabilities; + ctrl.buildCapabilityV1_2 = buildCapabilityV1_2; + ctrl.buildCapabilityV1_3 = buildCapabilityV1_3; + ctrl.buildCapabilitiesObject = buildCapabilitiesObject; + ctrl.isTestFlagged = isTestFlagged; + ctrl.getFlaggedReason = getFlaggedReason; + ctrl.isCapabilityShown = isCapabilityShown; + ctrl.isTestShown = isTestShown; + ctrl.getCapabilityTestCount = getCapabilityTestCount; + ctrl.getStatusTestCount = getStatusTestCount; + ctrl.openFullTestListModal = openFullTestListModal; + ctrl.openEditTestModal = openEditTestModal; + + /** The testID extracted from the URL route. */ + ctrl.testId = $stateParams.testID; + + /** The target OpenStack marketing program to compare against. */ + ctrl.target = 'platform'; + + /** Mappings of Interop WG components to marketing program names. */ + ctrl.targetMappings = { + 'platform': 'Openstack Powered Platform', + 'compute': 'OpenStack Powered Compute', + 'object': 'OpenStack Powered Object Storage' + }; + + /** The schema version of the currently selected guideline data. */ + ctrl.schemaVersion = null; + + /** The selected test status used for test filtering. */ + ctrl.testStatus = 'total'; + + /** The HTML template that all accordian groups will use. */ + ctrl.detailsTemplate = 'components/results-report/partials/' + + 'reportDetails.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(); + if (!ctrl.version) { + // Default to the first approved guideline which is + // expected to be at index 1. + ctrl.version = ctrl.versionList[1]; + } + ctrl.updateGuidelines(); + }).error(function (error) { + ctrl.showError = true; + ctrl.error = 'Error retrieving version list: ' + + angular.toJson(error); + }); + } + + /** + * Retrieve results from the TestAPI API server based on the test + * run id in the URL. This function is the first function that will + * be called from the controller. Upon successful retrieval of results, + * the function that gets the version list will be called. + */ + function getResults() { + var content_url = testapiApiUrl + '/results/' + ctrl.testId; + ctrl.resultsRequest = + $http.get(content_url).success(function (data) { + ctrl.resultsData = data; + ctrl.version = ctrl.resultsData.meta.guideline; + ctrl.isVerified = ctrl.resultsData.verification_status; + if (ctrl.resultsData.meta.target) { + ctrl.target = ctrl.resultsData.meta.target; + } + getVersionList(); + }).error(function (error) { + ctrl.showError = true; + ctrl.resultsData = null; + ctrl.error = 'Error retrieving results from server: ' + + angular.toJson(error); + }); + } + + /** + * This tells you whether the current user has administrative + * privileges for the test result. + * @returns {Boolean} true if the user has admin privileges. + */ + function isResultAdmin() { + return Boolean(ctrl.resultsData && + (ctrl.resultsData.user_role === 'owner' || + ctrl.resultsData.user_role === 'foundation')); + } + /** + * This tells you whether the current results are shared with the + * community or not. + * @returns {Boolean} true if the results are shared + */ + function isShared() { + return Boolean(ctrl.resultsData && + 'shared' in ctrl.resultsData.meta); + } + + /** + * This will send an API request in order to share or unshare the + * current results based on the passed in shareState. + * @param {Boolean} shareState - Whether to share or unshare results. + */ + function shareTestRun(shareState) { + var content_url = [ + testapiApiUrl, '/results/', ctrl.testId, '/meta/shared' + ].join(''); + if (shareState) { + ctrl.shareRequest = + $http.post(content_url, 'true').success(function () { + ctrl.resultsData.meta.shared = 'true'; + raiseAlert('success', '', 'Test run shared!'); + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } else { + ctrl.shareRequest = + $http.delete(content_url).success(function () { + delete ctrl.resultsData.meta.shared; + raiseAlert('success', '', 'Test run unshared!'); + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + } + + /** + * This will send a request to the API to delete the current + * test results set. + */ + function deleteTestRun() { + var content_url = [ + testapiApiUrl, '/results/', ctrl.testId + ].join(''); + ctrl.deleteRequest = + $http.delete(content_url).success(function () { + $window.history.back(); + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * This will send a request to the API to delete the current + * test results set. + */ + function updateVerificationStatus() { + var content_url = [ + testapiApiUrl, '/results/', ctrl.testId + ].join(''); + var data = {'verification_status': ctrl.isVerified}; + ctrl.updateRequest = + $http.put(content_url, data).success( + function () { + ctrl.resultsData.verification_status = ctrl.isVerified; + raiseAlert('success', '', + 'Verification status changed!'); + }).error(function (error) { + ctrl.isVerified = ctrl.resultsData.verification_status; + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * This will contact the TestAPI API server to retrieve the JSON + * content of the guideline file corresponding to the selected + * version. A function to construct an object from the capability + * data will be called upon successful retrieval. + */ + function updateGuidelines() { + ctrl.guidelineData = null; + ctrl.showError = false; + var content_url = testapiApiUrl + '/guidelines/' + + ctrl.version; + ctrl.capsRequest = + $http.get(content_url).success(function (data) { + ctrl.guidelineData = data; + ctrl.schemaVersion = data.schema; + ctrl.buildCapabilitiesObject(); + }).error(function (error) { + ctrl.showError = true; + ctrl.guidelineData = null; + ctrl.error = 'Error retrieving guideline date: ' + + angular.toJson(error); + }); + } + + /** + * This will get all the capabilities relevant to the target and + * their corresponding statuses. + * @returns {Object} Object containing each capability and their status + */ + function getTargetCapabilities() { + var components = ctrl.guidelineData.components; + var targetCaps = {}; + + // 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.guidelineData.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; + }); + }); + } + return targetCaps; + } + + /** + * This will build the a capability object for schema version 1.2. + * This object will contain the information needed to form a report in + * the HTML template. + * @param {String} capId capability ID + */ + function buildCapabilityV1_2(capId) { + var cap = { + 'id': capId, + 'passedTests': [], + 'notPassedTests': [], + 'passedFlagged': [], + 'notPassedFlagged': [] + }; + var capDetails = ctrl.guidelineData.capabilities[capId]; + // Loop through each test belonging to the capability. + angular.forEach(capDetails.tests, + function (testId) { + // If the test ID is in the results' test list, add + // it to the passedTests array. + if (ctrl.resultsData.results.indexOf(testId) > -1) { + cap.passedTests.push(testId); + if (capDetails.flagged.indexOf(testId) > -1) { + cap.passedFlagged.push(testId); + } + } + else { + cap.notPassedTests.push(testId); + if (capDetails.flagged.indexOf(testId) > -1) { + cap.notPassedFlagged.push(testId); + } + } + }); + return cap; + } + + /** + * This will build the a capability object for schema version 1.3 and + * above. This object will contain the information needed to form a + * report in the HTML template. + * @param {String} capId capability ID + */ + function buildCapabilityV1_3(capId) { + var cap = { + 'id': capId, + 'passedTests': [], + 'notPassedTests': [], + 'passedFlagged': [], + 'notPassedFlagged': [] + }; + + // For cases where a capability listed in components is not + // in the capabilities object. + if (!(capId in ctrl.guidelineData.capabilities)) { + return cap; + } + + // Loop through each test belonging to the capability. + angular.forEach(ctrl.guidelineData.capabilities[capId].tests, + function (details, testId) { + var passed = false; + + // If the test ID is in the results' test list. + if (ctrl.resultsData.results.indexOf(testId) > -1) { + passed = true; + } + else if ('aliases' in details) { + var len = details.aliases.length; + for (var i = 0; i < len; i++) { + var alias = details.aliases[i]; + if (ctrl.resultsData.results.indexOf(alias) > -1) { + passed = true; + break; + } + } + } + + // Add to correct array based on whether the test was + // passed or not. + if (passed) { + cap.passedTests.push(testId); + if ('flagged' in details) { + cap.passedFlagged.push(testId); + } + } + else { + cap.notPassedTests.push(testId); + if ('flagged' in details) { + cap.notPassedFlagged.push(testId); + } + } + }); + return cap; + } + + /** + * This will check the schema version of the current capabilities file, + * and will call the correct method to build an object based on the + * capability data retrieved from the TestAPI API server. + */ + function buildCapabilitiesObject() { + // This is the object template where 'count' is the number of + // total tests that fall under the given status, and 'passedCount' + // is the number of tests passed. The 'caps' array will contain + // objects with details regarding each capability. + ctrl.caps = { + 'required': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0}, + 'advisory': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0}, + 'deprecated': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0}, + 'removed': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0} + }; + + switch (ctrl.schemaVersion) { + case '1.2': + var capMethod = 'buildCapabilityV1_2'; + break; + case '1.3': + case '1.4': + case '1.5': + case '1.6': + capMethod = 'buildCapabilityV1_3'; + break; + default: + ctrl.showError = true; + ctrl.guidelineData = null; + ctrl.error = 'The schema version for the guideline ' + + 'file selected (' + ctrl.schemaVersion + + ') is currently not supported.'; + return; + } + + // Get test details for each relevant capability and store + // them in the scope's 'caps' object. + var targetCaps = ctrl.getTargetCapabilities(); + angular.forEach(targetCaps, function(status, capId) { + var cap = ctrl[capMethod](capId); + ctrl.caps[status].count += + cap.passedTests.length + cap.notPassedTests.length; + ctrl.caps[status].passedCount += cap.passedTests.length; + ctrl.caps[status].flagPassCount += cap.passedFlagged.length; + ctrl.caps[status].flagFailCount += + cap.notPassedFlagged.length; + ctrl.caps[status].caps.push(cap); + }); + + ctrl.requiredPassPercent = (ctrl.caps.required.passedCount * + 100 / ctrl.caps.required.count); + + ctrl.totalRequiredFailCount = ctrl.caps.required.count - + ctrl.caps.required.passedCount; + ctrl.totalRequiredFlagCount = + ctrl.caps.required.flagFailCount + + ctrl.caps.required.flagPassCount; + ctrl.totalNonFlagCount = ctrl.caps.required.count - + ctrl.totalRequiredFlagCount; + ctrl.nonFlagPassCount = ctrl.totalNonFlagCount - + (ctrl.totalRequiredFailCount - + ctrl.caps.required.flagFailCount); + + ctrl.nonFlagRequiredPassPercent = (ctrl.nonFlagPassCount * + 100 / ctrl.totalNonFlagCount); + } + + /** + * This will check if a given test is flagged. + * @param {String} test ID of the test to check + * @param {Object} capObj capability that test is under + * @returns {Boolean} truthy value if test is flagged + */ + function isTestFlagged(test, capObj) { + if (!capObj) { + return false; + } + return (((ctrl.schemaVersion === '1.2') && + (capObj.flagged.indexOf(test) > -1)) || + ((ctrl.schemaVersion >= '1.3') && + (capObj.tests[test].flagged))); + } + + /** + * This will return the reason a test is flagged. An empty string + * will be returned if the passed in test is not flagged. + * @param {String} test ID of the test to check + * @param {String} capObj capability that test is under + * @returns {String} reason + */ + function getFlaggedReason(test, capObj) { + if ((ctrl.schemaVersion === '1.2') && + (ctrl.isTestFlagged(test, capObj))) { + + // Return a generic message since schema 1.2 does not + // provide flag reasons. + return 'Interop Working Group has flagged this test.'; + } + else if ((ctrl.schemaVersion >= '1.3') && + (ctrl.isTestFlagged(test, capObj))) { + + return capObj.tests[test].flagged.reason; + } + else { + return ''; + } + } + + /** + * This will check the if a capability should be shown based on the + * test filter selected. If a capability does not have any tests + * belonging under the given filter, it should not be shown. + * @param {Object} capability Built object for capability + * @returns {Boolean} true if capability should be shown + */ + function isCapabilityShown(capability) { + return ((ctrl.testStatus === 'total') || + (ctrl.testStatus === 'passed' && + capability.passedTests.length > 0) || + (ctrl.testStatus === 'not passed' && + capability.notPassedTests.length > 0) || + (ctrl.testStatus === 'flagged' && + (capability.passedFlagged.length + + capability.notPassedFlagged.length > 0))); + } + + /** + * This will check the if a test should be shown based on the test + * filter selected. + * @param {String} test ID of the test + * @param {Object} capability Built object for capability + * @return {Boolean} true if test should be shown + */ + function isTestShown(test, capability) { + return ((ctrl.testStatus === 'total') || + (ctrl.testStatus === 'passed' && + capability.passedTests.indexOf(test) > -1) || + (ctrl.testStatus === 'not passed' && + capability.notPassedTests.indexOf(test) > -1) || + (ctrl.testStatus === 'flagged' && + (capability.passedFlagged.indexOf(test) > -1 || + capability.notPassedFlagged.indexOf(test) > -1))); + } + + /** + * This will give the number of tests belonging under the selected + * test filter for a given capability. + * @param {Object} capability Built object for capability + * @returns {Number} number of tests under filter + */ + function getCapabilityTestCount(capability) { + if (ctrl.testStatus === 'total') { + return capability.passedTests.length + + capability.notPassedTests.length; + } + else if (ctrl.testStatus === 'passed') { + return capability.passedTests.length; + } + else if (ctrl.testStatus === 'not passed') { + return capability.notPassedTests.length; + } + else if (ctrl.testStatus === 'flagged') { + return capability.passedFlagged.length + + capability.notPassedFlagged.length; + } + else { + return 0; + } + } + + /** + * This will give the number of tests belonging under the selected + * test filter for a given status. + * @param {String} capability status + * @returns {Number} number of tests for status under filter + */ + function getStatusTestCount(status) { + if (!ctrl.caps) { + return -1; + } + else if (ctrl.testStatus === 'total') { + return ctrl.caps[status].count; + } + else if (ctrl.testStatus === 'passed') { + return ctrl.caps[status].passedCount; + } + else if (ctrl.testStatus === 'not passed') { + return ctrl.caps[status].count - + ctrl.caps[status].passedCount; + } + else if (ctrl.testStatus === 'flagged') { + return ctrl.caps[status].flagFailCount + + ctrl.caps[status].flagPassCount; + } + else { + return -1; + } + } + + /** + * This will open the modal that will show the full list of passed + * tests for the current results. + */ + function openFullTestListModal() { + $uibModal.open({ + templateUrl: '/components/results-report/partials' + + '/fullTestListModal.html', + backdrop: true, + windowClass: 'modal', + animation: true, + controller: 'FullTestListModalController as modal', + size: 'lg', + resolve: { + tests: function () { + return ctrl.resultsData.results; + } + } + }); + } + + /** + * 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(); + } + + angular + .module('testapiApp') + .controller('FullTestListModalController', FullTestListModalController); + + FullTestListModalController.$inject = ['$uibModalInstance', 'tests']; + + /** + * Full Test List Modal Controller + * This controller is for the modal that appears if a user wants to see the + * full list of passed tests on a report page. + */ + function FullTestListModalController($uibModalInstance, tests) { + var ctrl = this; + + ctrl.tests = tests; + + /** + * This function will close/dismiss the modal. + */ + ctrl.close = function () { + $uibModalInstance.dismiss('exit'); + }; + + /** + * This function will return a string representing the sorted + * tests list separated by newlines. + */ + ctrl.getTestListString = function () { + return ctrl.tests.sort().join('\n'); + }; + } + + angular + .module('testapiApp') + .controller('EditTestModalController', EditTestModalController); + + EditTestModalController.$inject = [ + '$uibModalInstance', '$http', '$state', 'raiseAlert', + 'testapiApiUrl', '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, testapiApiUrl, 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 TestAPI + * 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 = testapiApiUrl + '/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 = testapiApiUrl + '/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 = testapiApiUrl + '/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 = testapiApiUrl + '/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 = [ + testapiApiUrl, '/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'); + }; + } +})(); -- cgit 1.2.3-korg