diff options
Diffstat (limited to 'testapi/opnfv_testapi')
23 files changed, 2197 insertions, 488 deletions
diff --git a/testapi/opnfv_testapi/common/check.py b/testapi/opnfv_testapi/common/check.py index 713f0fb..333871d 100644 --- a/testapi/opnfv_testapi/common/check.py +++ b/testapi/opnfv_testapi/common/check.py @@ -21,7 +21,7 @@ from opnfv_testapi.db import api as dbapi def is_authorized(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): - if CONF.api_authenticate and self.table in ['pods', 'projects']: + if CONF.api_authenticate and self.table in ['pods', 'projects', 'testcases']: testapi_id = self.get_secure_cookie(constants.TESTAPI_ID) if not testapi_id: raises.Unauthorized(message.not_login()) diff --git a/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js new file mode 100644 index 0000000..a14f8ea --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js @@ -0,0 +1,363 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000/" + +describe('testing the result page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/results', + method: 'GET', + queryString: { + page: '1' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "results": [ + { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "trust_indicator": null, + "public": "true", + "version": "euphrates", + "details": "", + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f4", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + ] + } + } + }, + { + request: { + path: '/api/v1/results', + method: 'GET', + queryString: { + page: '1', + project: 'testproject' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "results": [ + { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "trust_indicator": null, + "public": "true", + "version": "euphrates", + "details": "", + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f5", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + ] + } + } + }, + { + request: { + path: '/api/v1/results', + method: 'GET', + queryString: { + page: '1', + project: 'testproject', + case: 'testcase' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "results": [ + { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "trust_indicator": null, + "public": "true", + "version": "euphrates", + "details": "", + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f6", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the results page for anonymous user', function() { + browser.get(baseURL+"#/results"); + expect(element(by.cssContainingText(".ng-binding.ng-scope","Test Results")).isDisplayed()).toBe(true); + }); + + it( 'navigate anonymous user to testCase page', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Results')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/results'), 10000); + }); + + it('Should show the results in results page for anonymous user ', function () { + browser.get(baseURL+"#/results"); + var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f4"); + }); + + it('Should show the results in results page related to the filters for anonymous user ', function () { + browser.get(baseURL+"#/results"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('project'); + filterText.sendKeys('testproject'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f5"); + filter.sendKeys('case'); + filterText.sendKeys('testcase') + buttonFilter.click(); + expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f6"); + }); + it('Should not show the results in results page related to the filters for anonymous user ', function () { + browser.get(baseURL+"#/results"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('project'); + filterText.sendKeys('testproject1'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(true); + }); + +}); + +describe('testing the result page for user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/results', + method: 'GET', + queryString: { + page: '1' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "results": [ + { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "trust_indicator": null, + "public": "true", + "version": "euphrates", + "details": "", + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f4", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + ] + } + } + }, + { + request: { + path: '/api/v1/results', + method: 'GET', + queryString: { + page: '1', + project: 'testproject' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "results": [ + { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "trust_indicator": null, + "public": "true", + "version": "euphrates", + "details": "", + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f5", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + ] + } + } + }, + { + request: { + path: '/api/v1/results', + method: 'GET', + queryString: { + page: '1', + project: 'testproject', + case: 'testcase' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "results": [ + { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "trust_indicator": null, + "public": "true", + "version": "euphrates", + "details": "", + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f6", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the results page for user', function() { + browser.get(baseURL+"#/results"); + expect(element(by.cssContainingText(".ng-binding.ng-scope","Test Results")).isDisplayed()).toBe(true); + }); + + it( 'navigate user to testCase page', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Results')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/results'), 10000); + }); + + it('Should show the results in results page for user ', function () { + browser.get(baseURL+"#/results"); + var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f4"); + }); + + it('Should show the results in results page related to the filters for user ', function () { + browser.get(baseURL+"#/results"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('project'); + filterText.sendKeys('testproject'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f5"); + filter.sendKeys('case'); + filterText.sendKeys('testcase') + buttonFilter.click(); + expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f6"); + }); + it('Should not show the results in results page related to the filters for user ', function () { + browser.get(baseURL+"#/results"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('project'); + filterText.sendKeys('testproject1'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(true); + }); + +});
\ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js new file mode 100644 index 0000000..d6b5b9f --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js @@ -0,0 +1,658 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000/" + +describe('testing the testCases page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data: { + "projects": [ + { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject', + method: 'GET' + }, + response: { + data: { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases', + method: 'GET' + }, + response: { + data: { + "testcases": [ + { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases/testCase', + method: 'GET' + }, + response: { + data: { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the testCases for anonymous user', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var row = element.all(by.repeater('(index, testcase) in testCasesCtrl.data.testcases')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("testCase"); + }); + + it( 'navigate anonymous user to testCase page', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var testCase = element(by.linkText('testCase')); + testCase.click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/projects/testproject/testCase'), 10000); + }); + + it('create button is not visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + + it('Delete button is not visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBeFalsy(); + }); + + it('delete Operation is not visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBeFalsy(); + }); + + it('Edit Operation is not visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var editOperation = element(by.css('a[title=Edit]')); + expect(editOperation.isDisplayed()).toBeFalsy(); + }); +}); + +describe('testing the testcaese page for user who is not in submitter group', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data: { + "projects": [ + { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject', + method: 'GET' + }, + response: { + data: { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases', + method: 'GET' + }, + response: { + data: { + "testcases": [ + { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases/testCase', + method: 'GET' + }, + response: { + data: { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the testCases for user', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var row = element.all(by.repeater('(index, testcase) in testCasesCtrl.data.testcases')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("testCase"); + }); + + it( 'navigate user to testCase page', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var testCase = element(by.linkText('testCase')); + testCase.click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/projects/testproject/testCase'), 10000); + }); + + it('create button is not visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + + it('Delete button is not visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBeFalsy(); + }); + + it('delete Operation is not visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBeFalsy(); + }); + + it('Edit Operation is not visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var editOperation = element(by.css('a[title=Edit]')); + expect(editOperation.isDisplayed()).toBeFalsy(); + }); +}) + +describe('testing the test cases page for user who is in submitter group', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-testProject-submitters"], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases', + method: 'POST' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject/cases/testCase" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases/testCase', + method: 'PUT' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject/cases/testCase" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases/testCase', + method: 'DELETE' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject/cases/testCase" + } + } + }, + { + request: { + path: '/api/v1/projects/testProject3/cases', + method: 'POST', + data: { + name: 'testCase2', + description : 'demoDescription', + } + }, + response: { + status : 403, + data : 'You do not have permission to perform this action' + } + }, + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data: { + "projects": [ + { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject', + method: 'GET' + }, + response: { + data: { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases', + method: 'GET' + }, + response: { + data: { + "testcases": [ + { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases/testCase', + method: 'GET' + }, + response: { + data: { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the testCases for user', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var row = element.all(by.repeater('(index, testcase) in testCasesCtrl.data.testcases')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("testCase"); + }); + + it( 'navigate user to testCase page', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var testCase = element(by.linkText('testCase')); + testCase.click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/projects/testproject/testCase'), 10000); + }); + + it('create button is visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('Delete button is visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true); + }); + + it('delete Operation is visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('Edit Operation is visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var editOperation = element(by.css('a[title=Edit]')); + expect(editOperation.isDisplayed()).toBe(true); + }); + + it('View Operation is visible for user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var viewOperation = element(by.css('a[class=text-info]')); + expect(viewOperation.isDisplayed()).toBe(true); + }); + + it('Create the test case', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('TestCaseModalCtrl.testcase.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + expect(element(by.cssContainingText(".alert","Testcase is successfully created.")) + .isDisplayed()).toBe(true); + }); + + it('Showing error when creating with a empty name ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('TestCaseModalCtrl.testcase.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + expect(element(by.cssContainingText(".alert","Name is missing.")) + .isDisplayed()).toBe(true); + }); + + it('cancel the delete confimation modal of the test case ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonCancel = element(by.buttonText('Cancel')); + buttonCancel.click(); + expect(buttonCancel.isPresent()).toBe(false); + }); + + it('Delete the test case ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + expect(element(by.cssContainingText(".alert","Test case is successfully deleted")) + .isDisplayed()).toBe(true); + }); + + it('cancel the Edit modal of the test case ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var editOperation = element(by.css('a[title=Edit]')); + editOperation.click(); + var name = element(by.model('TestCaseModalCtrl.testcase.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test1'); + var buttonCancel = element(by.buttonText('Cancel')); + buttonCancel.click(); + expect(name.isPresent()).toBe(false); + }); + + it('Edit the test case ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var editOperation = element(by.css('a[title=Edit]')); + editOperation.click(); + var name = element(by.model('TestCaseModalCtrl.testcase.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test1'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + expect(element(by.cssContainingText(".alert","Test case is successfully updated")) + .isDisplayed()).toBe(true); + }); + + it('view the test case ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var viewOperation = element(by.css('a[class=text-info]')); + viewOperation.click(); + var name = element(by.model('TestCaseModalCtrl.testcase.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/projects/testproject/testCase'), 10000); + }); + + it('Batch Delete the test Cases ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var checkBox = element(by.model('testCasesCtrl.checkBox[index]')); + checkBox.click(); + var buttonDelete = element(by.buttonText('Delete'));; + buttonDelete.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + expect(element(by.cssContainingText(".alert","Test case is successfully deleted")) + .isDisplayed()).toBe(true); + }); +}) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_result.py b/testapi/opnfv_testapi/tests/unit/handlers/test_result.py index bd482a6..ac74741 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_result.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_result.py @@ -52,6 +52,7 @@ class Details(object): class TestResultBase(base.TestBase): + @executor.mock_valid_lfid() def setUp(self): super(TestResultBase, self).setUp() self.pod = self.pod_d.name diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py b/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py index d5e32e3..643057c 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py @@ -57,21 +57,26 @@ class TestCaseBase(base.TestBase): self.assertIsNotNone(new._id) self.assertIsNotNone(new.creation_date) + @executor.mock_valid_lfid() def create_d(self): return super(TestCaseBase, self).create_d(self.project) + @executor.mock_valid_lfid() def create_e(self): return super(TestCaseBase, self).create_e(self.project) def get(self, case=None): return super(TestCaseBase, self).get(self.project, case) + @executor.mock_valid_lfid() def create(self, req=None, *args): return super(TestCaseBase, self).create(req, self.project) + @executor.mock_valid_lfid() def update(self, new=None, case=None): return super(TestCaseBase, self).update(new, self.project, case) + @executor.mock_valid_lfid() def delete(self, case): return super(TestCaseBase, self).delete(self.project, case) diff --git a/testapi/opnfv_testapi/ui/Gruntfile.js b/testapi/opnfv_testapi/ui/Gruntfile.js index dd116a0..72a47e1 100644 --- a/testapi/opnfv_testapi/ui/Gruntfile.js +++ b/testapi/opnfv_testapi/ui/Gruntfile.js @@ -1,154 +1,155 @@ module.exports = function (grunt) { - require('load-grunt-tasks')(grunt); - require('grunt-protractor-coverage')(grunt); - grunt.loadNpmTasks('grunt-shell-spawn'); - grunt.loadNpmTasks('grunt-wait'); - grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-contrib-connect'); - grunt.initConfig({ - connect: { - server: { - options: { - port: 8000, - base: './', - middleware: function(connect, options, middlewares) { - middlewares.unshift(function(req, res, next) { - if (req.method.toUpperCase() == 'POST' || req.method.toUpperCase() == "PUT"){ - req.method='GET'; - } - return next(); - }); - return middlewares; - } - } - } - }, - copy: { - assets: { - expand: true, - cwd: '../../3rd_party/static/testapi-ui/assets', - src: '**', - dest: 'testapi-ui/assets', - }, - components: { - expand: true, - cwd: 'components', - src: '**', - dest: 'testapi-ui/components', - }, - shared: { - expand: true, - cwd: 'shared', - src: '**', - dest: 'testapi-ui/shared', - }, - filesPng: { - expand: true, - src: '*.png', - dest: 'testapi-ui/', - }, - filesIco: { - expand: true, - src: '*.ico', - dest: 'testapi-ui/', - }, - filesJs: { - expand: true, - src: 'app.js', - dest: 'testapi-ui/', - }, - filesJson: { - expand: true, - src: 'config.json', - dest: 'testapi-ui/', - } - }, - wait: { - default: { - options: { - delay: 3000 - } - } - }, - shell: { - updateSelenium: { - command: 'node_modules/protractor/bin/webdriver-manager update', - options: { - async: false - } - }, - startSelenium: { - command: 'node_modules/protractor/bin/webdriver-manager start', - options: { - async: true - } - }, - deleteFiles: { - command: 'rm -r testapi-ui', - options: { - async: false - } - }, - options: { - stdout: false, - stderr: false - } - }, - instrument: { - files: ['components/**/*.js'], - options: { - lazy: false, - basePath: "./testapi-ui/" - } - }, - karma: { - unit: { - configFile: 'karma.conf.js' - } - }, - protractor_coverage: { - options: { - keepAlive: true, - noColor: false, - coverageDir: '../tests/UI/coverage', - args: { - specs: ['../tests/UI/e2e/podsControllerSpec.js', - '../tests/UI/e2e/projectsControllerSpec.js', - '../tests/UI/e2e/projectControllerSpec.js'] - } - }, - local: { - options: { - configFile: '../tests/UI/protractor-conf.js' - } - } - }, - makeReport: { - src: '../tests/UI/coverage/*.json', - options: { - print: 'detail' - } - } - }); - grunt.registerTask('test', [ - 'karma:unit' - ]); - grunt.registerTask('e2e', [ - 'copy:assets', - 'copy:components', - 'copy:shared', - 'copy:filesPng', - 'copy:filesIco', - 'copy:filesJs', - 'copy:filesJson', - 'instrument', - 'connect', - 'shell:updateSelenium', - 'shell:startSelenium', - 'wait:default', - 'protractor_coverage', - 'makeReport', - 'shell:deleteFiles' - ]); + require('load-grunt-tasks')(grunt); + require('grunt-protractor-coverage')(grunt); + grunt.loadNpmTasks('grunt-shell-spawn'); + grunt.loadNpmTasks('grunt-wait'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.initConfig({ + connect: { + server: { + options: { + port: 8000, + base: './', + middleware: function(connect, options, middlewares) { + middlewares.unshift(function(req, res, next) { + if (req.method.toUpperCase() == 'POST' || req.method.toUpperCase() == "PUT" || + req.method.toUpperCase() == "DELETE") + { + req.method='GET'; + } + return next(); + }); + return middlewares; + } + } + } + }, + copy: { + assets: { + expand: true, + cwd: '../../3rd_party/static/testapi-ui/assets', + src: '**', + dest: 'testapi-ui/assets', + }, + components: { + expand: true, + cwd: 'components', + src: '**', + dest: 'testapi-ui/components', + }, + shared: { + expand: true, + cwd: 'shared', + src: '**', + dest: 'testapi-ui/shared', + }, + filesPng: { + expand: true, + src: '*.png', + dest: 'testapi-ui/', + }, + filesIco: { + expand: true, + src: '*.ico', + dest: 'testapi-ui/', + }, + filesJs: { + expand: true, + src: 'app.js', + dest: 'testapi-ui/', + }, + filesJson: { + expand: true, + src: 'config.json', + dest: 'testapi-ui/', + } + }, + wait: { + default: { + options: { + delay: 3000 + } + } + }, + shell: { + updateSelenium: { + command: 'node_modules/protractor/bin/webdriver-manager update', + options: { + async: false + } + }, + startSelenium: { + command: 'node_modules/protractor/bin/webdriver-manager start', + options: { + async: true + } + }, + deleteFiles: { + command: 'rm -r testapi-ui', + options: { + async: false + } + }, + options: { + stdout: false, + stderr: false + } + }, + instrument: { + files: ['components/**/*.js'], + options: { + lazy: false, + basePath: "./testapi-ui/" + } + }, + karma: { + unit: { + configFile: 'karma.conf.js' + } + }, + protractor_coverage: { + options: { + keepAlive: true, + noColor: false, + coverageDir: '../tests/UI/coverage', + args: { + specs: ['../tests/UI/e2e/testCasesControllerSpec.js', + '../tests/UI/e2e/resultsControllerSpec.js'] + } + }, + local: { + options: { + configFile: '../tests/UI/protractor-conf.js' + } + } + }, + makeReport: { + src: '../tests/UI/coverage/*.json', + options: { + print: 'detail' + } + } + }); + grunt.registerTask('test', [ + 'karma:unit' + ]); + grunt.registerTask('e2e', [ + 'copy:assets', + 'copy:components', + 'copy:shared', + 'copy:filesPng', + 'copy:filesIco', + 'copy:filesJs', + 'copy:filesJson', + 'instrument', + 'connect', + 'shell:updateSelenium', + 'shell:startSelenium', + 'wait:default', + 'protractor_coverage', + 'makeReport', + 'shell:deleteFiles' + ]); } diff --git a/testapi/opnfv_testapi/ui/app.js b/testapi/opnfv_testapi/ui/app.js index 28e5810..2a34838 100644 --- a/testapi/opnfv_testapi/ui/app.js +++ b/testapi/opnfv_testapi/ui/app.js @@ -79,6 +79,11 @@ templateUrl: 'testapi-ui/components/projects/project/project.html', controller: 'ProjectController as ctrl' }). + state('testCase', { + url: '/projects/:project_name/:name', + templateUrl: 'testapi-ui/components/projects/project/testCases/testCase/testCase.html', + controller: 'TestCaseController as ctrl' + }). state('results', { url: '/results', templateUrl: 'testapi-ui/components/results/results.html', @@ -124,14 +129,14 @@ .run(setup); setup.$inject = [ - '$http', '$rootScope', '$window', '$state', 'testapiApiUrl' + '$http', '$rootScope', '$window', '$state', 'testapiApiUrl', "authenticate" ]; /** * Set up the app with injections into $rootscope. This is mainly for auth * functions. */ - function setup($http, $rootScope, $window, $state, testapiApiUrl) { + function setup($http, $rootScope, $window, $state, testapiApiUrl, authenticate) { $rootScope.auth = {}; $rootScope.auth.doSignIn = doSignIn; @@ -165,7 +170,11 @@ success(function (data) { $rootScope.auth.currentUser = data; $rootScope.auth.isAuthenticated = true; - $rootScope.auth.projectNames = $rootScope.auth.doSubmitterCheck(data.groups); + if(authenticate){ + $rootScope.auth.projectNames = $rootScope.auth.doSubmitterCheck(data.groups); + }else{ + $rootScope.auth.projectNames = ["anonymous"] + } }). error(function () { $rootScope.auth.currentUser = null; diff --git a/testapi/opnfv_testapi/ui/components/projects/modals/projectModal.html b/testapi/opnfv_testapi/ui/components/projects/modals/projectModal.html new file mode 100644 index 0000000..ca00f80 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/projects/modals/projectModal.html @@ -0,0 +1,39 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{ProjectModalCtrl.data.text}}</legend> + <div class="row"> + <div ng-repeat="require in ProjectModalCtrl.createRequirements" style="margin-left:15px"> + <div class="update-project"> + <label for="cpid" class="control-label col-sm-2">{{require.label|capitalize}}: </label> + <div class="col-sm-9"> + <a ng-if="require.type == 'select'"> + <select class="form-control" dynamic-model="'ProjectModalCtrl.project.' + require.label" ng-options="option for option in require.selects"></select> + </a> + <a ng-if="require.type == 'text'"> + <input type="text" class="form-control" dynamic-model="'ProjectModalCtrl.project.' + require.label"/> + </a> + <a ng-if="require.type == 'textarea'"> + <textarea rows="2" class="form-control" cols="50" dynamic-model="'ProjectModalCtrl.project.' + require.label"> + </textarea> + </a> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <div ng-show="ProjectModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{ProjectModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="ProjectModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="ProjectModalCtrl.cancel()">Cancel</button> + </div> +</div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/projects/project/project.html b/testapi/opnfv_testapi/ui/components/projects/project/project.html index 9d46364..2921bd9 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/project.html +++ b/testapi/opnfv_testapi/ui/components/projects/project/project.html @@ -1,25 +1,56 @@ -<div ng-show="ctrl.data" class="projects-table" style="margin-top:24px; margin-left:8px;"> - <table class="table"> - <tbody> - <tr> <td class="col-md-3">Name</td> <td class="col-md-9">{{ctrl.data.name}}</td> </tr> - <tr> <td>Description</td> <td>{{ctrl.data.description}}</td> </tr> - <tr> <td>Creation date</td> <td>{{ctrl.data.creation_date}}</td> </tr> - </tbody> - </table> -</div> - +<legend>Project</legend> <div class="row" style="margin-bottom:24px;"></div> -<div class="project-create col-md-3" style="margin-top:10px" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && - auth.isAuthenticated) }"> - <button type="submit" class="btn btn-primary" ng-click="ctrl.openDeleteModal()">Delete Project</button> - <button type="submit" class="btn btn-primary" ng-click="ctrl.openUpdateModal()">Update Project</button> + +<div class="tabbable tabs-below" ng-init="selectedTab = 1;"> + <ul class="nav nav-tabs nav-justified" style="width:30%"> + <li ng-class="{active: selectedTab == 1}"> + <a ng-click="selectedTab = 1;">Overview</a> + </li> + <li ng-class="{active: selectedTab == 2}"> + <a ng-click="selectedTab = 2;">Test Cases</a> + </li> + </ul> + + <div class="tab-content" ng-show="selectedTab == 1"> + <div style="padding-right:0px; margin-top:20px" > + <div class="table-responsive"> + <table class="table" ng-data="ctrl.data.pods"> + <tbody> + <tr style="padding:9px"> + <td class="podsTableTd">Id :</td> + <td class="podsTableLeftTd">{{ctrl.data._id}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Name :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.name}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Owner :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.owner}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Created at :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data['creation_date']}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Description :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.description}}</td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="tab-content" ng-show="selectedTab == 2"> + <div ng-include src="'testapi-ui/components/projects/project/testCases/testCases.html'"></div> + </div> </div> -<div ng-show="ctrl.showError" class="alert alert-danger col-md-9" role="alert"> +<div ng-show="ctrl.showError" class="alert alert-danger col-md-9" role="alert" style="margin-top:0px"> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <span class="sr-only">Error:</span> {{ctrl.error}} </div> -<div ng-show="ctrl.showSuccess" class="alert alert-success col-md-9" role="alert"> +<div ng-show="ctrl.showSuccess" class="alert alert-success col-md-9" role="alert" style="margin-top:0px"> <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> Update Success -</div>
\ No newline at end of file +</div> diff --git a/testapi/opnfv_testapi/ui/components/projects/project/projectController.js b/testapi/opnfv_testapi/ui/components/projects/project/projectController.js index 8f4bd20..78b805d 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/projectController.js +++ b/testapi/opnfv_testapi/ui/components/projects/project/projectController.js @@ -36,88 +36,6 @@ ctrl.url = testapiApiUrl + '/projects/' + ctrl.name; ctrl.loadDetails = loadDetails; - ctrl.deleteProject = deleteProject; - ctrl.openDeleteModal = openDeleteModal; - ctrl.openUpdateModal = openUpdateModal; - ctrl.updateProject = updateProject; - - - /** - * This will contact the TestAPI to update an existing project. - */ - function updateProject(name,description) { - ctrl.showError = false; - ctrl.showSuccess = false; - if(ctrl.name != ""){ - var projects_url = ctrl.url; - var body = { - name: name, - description: description - }; - ctrl.projectsRequest = - $http.put(projects_url, body).success(function (data){ - ctrl.showSuccess = true ; - }) - .error(function (data) { - ctrl.showError = true; - ctrl.error = 'Error updating the existing Project from server: ' + angular.toJson(data); - }); - ctrl.name = ""; - ctrl.description=""; - } - else{ - ctrl.showError = true; - ctrl.error = 'Name is missing.' - } - } - - /** - * This will contact the TestAPI to delete an existing project. - */ - function deleteProject() { - ctrl.showError = false; - ctrl.showSuccess = false; - ctrl.projectsRequest = - $http.delete(ctrl.url).success(function (data) { - $state.go('projects', {}, {reload: true}); - ctrl.showSuccess = true ; - - }).error(function (error) { - ctrl.showError = true; - ctrl.error = - 'Error deleting project from server: ' + - angular.toJson(error); - }); - } - - /** - * This will open the modal that will show the delete confirm - * message - */ - function openDeleteModal() { - confirmModal("Delete",ctrl.deleteProject); - } - - /** - * This will open the modal that will show the update - * view - */ - function openUpdateModal(){ - $uibModal.open({ - templateUrl: 'testapi-ui/components/projects/project/updateModal.html', - controller: 'ModalInstanceCtrl as updateModal', - size: 'md', - resolve: { - data: function () { - return { - text: "Update", - successHandler: ctrl.updateProject, - project: ctrl.data - }; - } - } - }); - } /** * This will contact the TestAPI to get a listing of declared projects. @@ -127,57 +45,12 @@ ctrl.projectsRequest = $http.get(ctrl.url).success(function (data) { ctrl.data = data; - }).error(function (error) { + }).catch(function (error) { ctrl.data = null; ctrl.showError = true; - ctrl.error = - 'Error retrieving projects from server: ' + - angular.toJson(error); + ctrl.error = error.statusText }); } ctrl.loadDetails(); } - - - /** - * TestAPI Modal instance Controller - * This controller is for the update modal where a user can update - * the project information. - */ - angular.module('testapiApp').controller('ModalInstanceCtrl', ModalInstanceCtrl); - ModalInstanceCtrl.$inject = ['$scope', '$uibModalInstance', 'data']; - function ModalInstanceCtrl($scope, $uibModalInstance, data) { - var ctrl = this; - ctrl.confirm = confirm; - ctrl.cancel = cancel; - ctrl.data = angular.copy(data); - - ctrl.createRequirements = [ - {label: 'name', type: 'text', required: true}, - {label: 'description', type: 'textarea', required: false} - ]; - - ctrl.name = ctrl.data.project.name; - ctrl.description = ctrl.data.project.description; - - /** - * Initiate confirmation and call the success handler with the - * inputs. - */ - function confirm() { - $uibModalInstance.close(); - if (angular.isDefined(ctrl.data.successHandler)) { - ctrl.data.successHandler(ctrl.name,ctrl.description); - } - } - - /** - * Close the confirm modal without initiating changes. - */ - function cancel() { - $uibModalInstance.dismiss('cancel'); - } - } - - })(); diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/modals/testCaseModal.html b/testapi/opnfv_testapi/ui/components/projects/project/testCases/modals/testCaseModal.html new file mode 100644 index 0000000..d1c9cb5 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/modals/testCaseModal.html @@ -0,0 +1,39 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{TestCaseModalCtrl.data.text}}</legend> + <div class="row"> + <div ng-repeat="require in TestCaseModalCtrl.createRequirements" style="margin-left:15px"> + <div class="update-project"> + <label for="cpid" class="control-label col-sm-3">{{require.label.replace("_"," ")|capitalize}}: </label> + <div class="col-sm-8"> + <a ng-if="require.type == 'select'"> + <select class="form-control" dynamic-model="'TestCaseModalCtrl.testcase.' + require.label" ng-options="option for option in require.selects"></select> + </a> + <a ng-if="require.type == 'text'"> + <input type="text" class="form-control" dynamic-model="'TestCaseModalCtrl.testcase.' + require.label"/> + </a> + <a ng-if="require.type == 'textarea'"> + <textarea rows="2" class="form-control" cols="50" dynamic-model="'TestCaseModalCtrl.testcase.' + require.label"> + </textarea> + </a> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <div ng-show="TestCaseModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{TestCaseModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="TestCaseModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="TestCaseModalCtrl.cancel()">Cancel</button> + </div> +</div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html new file mode 100644 index 0000000..70c026a --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html @@ -0,0 +1,80 @@ +<legend>Test Case</legend> +<div style="padding-right:0px"> + <div class="table-responsive"> + <table class="table" ng-data="ctrl.data"> + <tbody> + <tr style="padding:9px"> + <td class="podsTableTd">Id :</td> + <td class="podsTableLeftTd">{{ctrl.data._id}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Name :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.name}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Project :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.project_name}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Tier :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.tier}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Blocking :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.blocking}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">CI Loop :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.ci_loop}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Tags :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.tags}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Version :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.version}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Created at :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data['creation_date']}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Dependencies :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.dependencies}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Trust :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.trust}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Criteria :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.criteria}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Catalog Description :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.catalog_description}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">URL :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.url}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Run :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.run}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Description :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.description}}</td> + </tr> + </tbody> + </table> + </div> +</div> +<div class="row" style="margin-bottom:24px;"></div> +<div ng-show="ctrl.showError" class="alert alert-danger col-md-8" role="alert" style="margin-top:0px"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{ctrl.error}} +</div> +<div class="row" style="margin-bottom:24px;"></div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js new file mode 100644 index 0000000..a38b633 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js @@ -0,0 +1,57 @@ +/* + * 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('TestCaseController', TestCaseController); + + TestCaseController.$inject = [ + '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', + 'confirmModal' + ]; + + /** + * TestAPI Project Controller + * This controller is for the '/projects' page where a user can browse + * through projects declared in TestAPI. + */ + function TestCaseController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal) { + var ctrl = this; + ctrl.name = $state.params['name']; + ctrl.projectName = $state.params['project_name']; + ctrl.url = testapiApiUrl + '/projects/' + ctrl.projectName + "/cases/" + ctrl.name; + + ctrl.loadDetails = loadDetails; + + /** + * This will contact the TestAPI to get a listing of declared projects. + */ + function loadDetails() { + ctrl.showError = false; + ctrl.projectsRequest = + $http.get(ctrl.url).success(function (data) { + ctrl.data = data; + }).catch(function (error) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = error.statusText + }); + } + ctrl.loadDetails(); + } +})(); diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html new file mode 100644 index 0000000..34656f3 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html @@ -0,0 +1,71 @@ +<div ng-controller="TestCasesController as testCasesCtrl" class="col-md-12"> +<div class="row podsTable" style="vertical-align:middle"> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated) }" > + <button type="button" class="btn btn-danger" ng-click="testCasesCtrl.openBatchDeleteModal()"> + <i class="fa fa-minus"></i> Delete</button> + </div> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated) }"> + <button type="button" class="btn btn-success" ng-click="testCasesCtrl.openCreateModal()"> + <i class="fa fa-plus"></i> Create</button> + </div> +</div> +<div class='clo-md-12'> + <div ng-show="testCasesCtrl.showError" class="alert alert-danger" role="alert"> + <span class="pull-right"> {{testCasesCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <div ng-show="testCasesCtrl.showSuccess" class="alert alert-success" role="alert"> + <span class="pull-right"> {{testCasesCtrl.successMessage}}</span> + <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span> + </div> +</div> +<div class='clo-md-12' style="padding-right:0px"> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="testCasesCtrl.data.testcases"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 1%;">Bulk Select</th> + <th style="width: 19%;">Name</th> + <th style="width: 20%;">Tier</th> + <th style="width: 20%;">Blocking</th> + <th style="width: 20%;">CI Loop</th> + <th style="width: 20%;">Operations</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="(index, testcase) in testCasesCtrl.data.testcases" style="padding:9px"> + <td> + <div class="text-center"> + <input type="checkbox" value="{{project.name}}" ng-model="testCasesCtrl.checkBox[index]" > + </div> + </td> + <td> + <a class="text-info" ng-click="testCasesCtrl.viewTestCase(testcase.name, testcase.project_name)"> + {{testcase.name}} + </a> + </td> + <td>{{ ctrl.data.tier}}</td> + <td>{{ctrl.data.blocking}}</td> + <td>{{ctrl.data.ci_loop}}</td> + <td> + <span class="podsTable-col"> + <a class="text-warning" ng-click="testCasesCtrl.openUpdateTestModal(testcase.name)" title="Edit" ng-class="{'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated)}" > + <i class="fa fa-pencil-square-o"></i></a> + <a class="text-danger" ng-click="testCasesCtrl.openDeleteTestModal(testcase.name)" title="Delete" ng-class="{'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated)}"> + <i class="fa fa-trash-o"></i></a> + <a class="text-info" ng-click="testCasesCtrl.viewTestCase(testcase.name, testcase.project_name)"><i class="fa fa-eye"></i></a> + </span> + </td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> +</div> +</div> diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js new file mode 100644 index 0000000..0045284 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js @@ -0,0 +1,278 @@ +/* + * 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('TestCasesController', TestCasesController); + + TestCasesController.$inject = [ + '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', + 'confirmModal' + ]; + + /** + * TestAPI Test cases Controller + * This controller is for the tescases page where a user can browse + * through testcases declared in TestAPI and perform the CRUD operations + * in them. + */ + function TestCasesController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal) { + var ctrl = this; + ctrl.loadDetails = loadDetails; + ctrl.name = $state.params['name']; + ctrl.requestUrl = testapiApiUrl + '/projects/' + ctrl.name +'/cases'; + + ctrl.createTestCase = createTestCase; + ctrl.openCreateModal = openCreateModal; + ctrl.deleteTestCase = deleteTestCase; + ctrl.openDeleteTestModal = openDeleteTestModal; + ctrl.updateTestCase = updateTestCase; + ctrl.openUpdateTestModal = openUpdateTestModal; + ctrl.batchDelete = batchDelete; + ctrl.openBatchDeleteModal = openBatchDeleteModal; + ctrl.viewTestCase = viewTestCase; + + ctrl.checkBox = []; + ctrl.checkBoxList = []; + + /** + * This will contact the TestAPI to create a new test case. + */ + function createTestCase(name, testcase) { + ctrl.showError = false; + ctrl.showSuccess = false; + if(testcase.name != "" && testcase.name!=null){ + var testCase_url = ctrl.requestUrl; + ctrl.testCasesRequest = + $http.post(testCase_url, testcase).success(function (data){ + ctrl.showSuccess = true ; + ctrl.successMessage = "Testcase is successfully created." + loadDetails(); + }) + .catch(function (data) { + ctrl.showError = true; + ctrl.error = data.statusText; + }); + } + else{ + ctrl.showError = true; + ctrl.error = 'Name is missing.' + } + } + + function viewTestCase(name, project_name){ + $state.go('testCase', {'name':name, 'project_name':project_name}, {reload: true}); + } + + /** + * This will open the modal that will show the batch delete confirm + * message + */ + function openBatchDeleteModal() { + confirmModal("Delete",ctrl.batchDelete); + } + + /** + * This will delete list of test cases. + */ + function batchDelete(){ + var index; + var checkedBox = []; + ctrl.checkBox.forEach(function(testcase, index){ + if(!ctrl.showError){ + if(testcase){ + ctrl.deleteTestCase(ctrl.data.testcases[index].name); + } + } + }); + ctrl.checkBox = [] + } + /** + * This will contact the TestAPI to update an existing test case. + */ + function updateTestCase(name, testCase) { + ctrl.showError = false; + ctrl.showSuccess = false; + if(testCase.name != ""){ + var testCase_url = ctrl.requestUrl + '/' + name; + ctrl.testCasesRequest = + $http.put(testCase_url, testCase).success(function (data){ + ctrl.showSuccess = true ; + ctrl.successMessage = "Test case is successfully updated" + loadDetails(); + }) + .catch(function (data) { + ctrl.showError = true; + ctrl.error = data.statusText; + }); + } + else{ + ctrl.showError = true; + ctrl.error = 'Name is missing.' + } + } + + /** + * This will contact the TestAPI to delete an existing test case. + */ + function deleteTestCase(name) { + ctrl.showError = false; + ctrl.showSuccess = false; + ctrl.testCasesRequest = + $http.delete(ctrl.requestUrl+"/"+name).success(function (data) { + loadDetails(); + ctrl.showSuccess = true ; + ctrl.successMessage = "Test case is successfully deleted" + }).catch(function (error) { + ctrl.showError = true; + ctrl.error = data.statusText; + }); + } + + /** + * This will open the modal that will show the delete confirm + * message + */ + function openDeleteTestModal(name) { + confirmModal("Delete", ctrl.deleteTestCase, name); + } + + /** + * This will open the modal that will show the Create + * view + */ + function openCreateModal(name){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/projects/project/testCases/modals/testCaseModal.html', + controller: 'TestCaseModalCtrl as TestCaseModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Create", + successHandler: ctrl.createTestCase + }; + } + } + }); + } + + /** + * This will open the modal that will show the update + * view + */ + function openUpdateTestModal(name){ + var testcase; + var index; + for(index in ctrl.data.testcases){ + if(ctrl.data.testcases[index].name==name){ + testcase = ctrl.data.testcases[index] + continue + } + } + $uibModal.open({ + templateUrl: 'testapi-ui/components/projects/project/testCases/modals/testCaseModal.html', + controller: 'TestCaseModalCtrl as TestCaseModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Update", + successHandler: ctrl.updateTestCase, + testCase: testcase + }; + } + } + }); + } + + /** + * This will contact the TestAPI to get a listing of declared test cases. + */ + function loadDetails() { + ctrl.testCasesReguest = + $http.get(ctrl.requestUrl).success(function (data) { + ctrl.data = data; + }).catch(function (error) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = error.statusText; + }); + } + ctrl.loadDetails(); + } + + + /** + * TestAPI Modal instance Controller + * This controller is for the modal where a user can create + * test case or update the test case information. + */ + angular.module('testapiApp').controller('TestCaseModalCtrl', TestCaseModalCtrl); + TestCaseModalCtrl.$inject = ['$scope', '$uibModalInstance', 'data']; + function TestCaseModalCtrl($scope, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + + ctrl.createRequirements = [ + {label: 'name', type: 'text', required: true}, + {label: 'description', type: 'textarea', required: false}, + {label: 'version', type: 'text', required: false}, + {label: 'tier', type: 'text', required: false}, + {label: 'tags', type: 'text', required: false}, + {label: 'run', type: 'text', required: false}, + {label: 'dependencies', type: 'text', required: false}, + {label: 'trust', type: 'text', required: false}, + {label: 'url', type: 'text', required: false}, + {label: 'blocking', type: 'text', required: false}, + {label: 'criteria', type: 'text', required: false}, + {label: 'domains', type: 'text', required: false}, + {label: 'catalog_description', type: 'text', required: false} + ]; + + ctrl.testcase = {name : null, description : null,version : null, tier : null, tags : null, + run : null, dependencies : null, trust : null, url : null, blocking : null, + criteria : null, domains : null, catalog_description : null}; + + if(ctrl.data.text=="Update"){ + ctrl.testcase = ctrl.data.testCase + delete ctrl.testcase._id; + ctrl.name = ctrl.data.testCase.name + } + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + $uibModalInstance.close(); + if (angular.isDefined(ctrl.data.successHandler)) { + ctrl.data.successHandler(ctrl.name, ctrl.testcase); + } + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } +})(); diff --git a/testapi/opnfv_testapi/ui/components/projects/project/updateModal.html b/testapi/opnfv_testapi/ui/components/projects/project/updateModal.html deleted file mode 100644 index ab8d64e..0000000 --- a/testapi/opnfv_testapi/ui/components/projects/project/updateModal.html +++ /dev/null @@ -1,26 +0,0 @@ -<div class="modal-header"><h3 class="modal-title">Confirm</h3></div> -<div class="modal-body"> - <div class="form-group"> - <h4>Update</h4> - <div class="row"> - <div ng-repeat="require in updateModal.createRequirements"> - <div class="update-project" style="margin-left:24px;"> - <p class="input-group"> - <label for="cpid">{{require.label|capitalize}}: </label> - <a ng-if="require.type == 'text'"> - <input type="text" dynamic-model="'updateModal.' + require.label"/> - </a> - <a ng-if="require.type == 'textarea'"> - <textarea rows="2" cols="50" value={{require.vaule}} dynamic-model="'updateModal.' + require.label"> - </textarea> - </a> - </p> - </div> - </div> - </div> - </div> -</div> -<div class="modal-footer"> - <button class="btn btn-primary" ng-click="updateModal.confirm()">Ok</button> - <button class="btn btn-default" ng-click="updateModal.cancel()">Cancel</button> -</div> diff --git a/testapi/opnfv_testapi/ui/components/projects/projects.html b/testapi/opnfv_testapi/ui/components/projects/projects.html index 28c08b1..e8bb947 100644 --- a/testapi/opnfv_testapi/ui/components/projects/projects.html +++ b/testapi/opnfv_testapi/ui/components/projects/projects.html @@ -1,76 +1,72 @@ <h3>Projects</h3> <div class="row" style="margin-bottom:24px;"></div> -<div class="project-create" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && - auth.isAuthenticated) }"> - <h4>Create</h4> - <div class="row"> - <div ng-repeat="require in ctrl.createRequirements"> - <div class="create-project" style="margin-left:24px;"> - <p class="input-group"> - <label for="cpid">{{require.label|capitalize}}: </label> - <a ng-if="require.type == 'text'"> - <input type="text" dynamic-model="'ctrl.' + require.label"/> - </a> - <a ng-if="require.type == 'textarea'"> - <textarea rows="2" cols="50" dynamic-model="'ctrl.' + require.label"> - </textarea> - </a> - </p> - </div> - </div> - <div class="col-md-1 col-sm-1 col-xs-1 " style="margin-top:15px;"> - <button type="submit" class="btn btn-primary" ng-click="ctrl.create()">Create</button> - </div> - <div class="col-md-11 col-sm-11 col-xs-11"> - <div ng-show="ctrl.showCreateError" 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> - <div ng-show="ctrl.showCreateSuccess" class="alert alert-success" role="alert"> - <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> - Create Success - </div> - </div> +<div class="row podsTable" style="vertical-align:middle"> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated) }" > + <button type="button" class="btn btn-danger" ng-click="ctrl.openBatchDeleteModal()"> + <i class="fa fa-minus"></i> Delete</button> + </div> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated) }"> + <button type="button" class="btn btn-success" ng-click="ctrl.openCreateModal()"> + <i class="fa fa-plus"></i> Create</button> + </div> + <div class="col-sm-1 pull-right"> + <button type="button" class="btn btn-success" ng-click="ctrl.listProjects()"> + <i class="fa fa-search"></i> Filter</button> + </div> + <div class="col-sm-3 pull-right"> + <span style="margin-top:6px">Search: </span> + <input type="text" class="form-control search" ng-Model="ctrl.filterText" style="width:80%;" placeholder="Search By Name"> </div> </div> - - -<div class="project-filters"> - <h4>Filters</h4> - <div class="row"> - <div class="col-md-3"> - <label for="cpid">Name</label> - <input type="text" class="form-control" - ng-model="ctrl.filterName"/> - </div> - <div class="col-md-1" style="margin-top:24px;"> - <button type="submit" class="btn btn-primary" ng-click="ctrl.update()">Filter</button> - </div> - <div class="col-md-1" style="margin-top:24px;"> - <button type="submit" class="btn btn-primary btn-danger" ng-click="ctrl.clearFilters()">Clear</button> - </div> - <div class="col-md-7" style="margin-top:10px;"> - <div ng-show="ctrl.showUpdateError" 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> - </div> +<div class='clo-md-12'> + <div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> + <span class="pull-right"> {{ctrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <div ng-show="ctrl.showCreateSuccess" class="alert alert-success" role="alert"> + <span class="pull-right"> {{ctrl.success}}</span> + <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span> </div> </div> - -<div ng-show="ctrl.data" class="projects-table" style="margin-top:24px; margin-left:8px;"> - <table ng-data="ctrl.data.projects" ng-show="ctrl.data" class="table table-striped table-hover"> +<div class='clo-md-12' style="padding-right:0px"> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="ctrl.data.projects"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 1%;">Bulk Select</th> + <th style="width: 19%;">Name</th> + <th style="width: 70%;">Description</th> + <th style="width: 10%;">Operations</th> + </tr> + </thead> <tbody> - <tr ng-repeat-start="(index, project) in ctrl.data.projects"> - <td> - <a ui-sref='project({name: project.name})'>{{project.name}}</a> - </td> - </tr> - <tr ng-repeat-end=> - </tr> + <tr ng-repeat-start="(index, project) in ctrl.data.projects" style="padding:9px"> + <td> + <div class="text-center"> + <input type="checkbox" value="{{project.name}}" ng-model="ctrl.checkBox[index]" > + </div> + </td> + <td>{{project.name}}</td> + <td>{{project.description}}</td> + <td> + <span class="podsTable-col"> + <a class="text-warning" ng-click="ctrl.openUpdateModal(project.name)" title="Edit" ng-class="{'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated)}" > + <i class="fa fa-pencil-square-o"></i></a> + <a class="text-danger" ng-click="ctrl.openDeleteModal(project.name)" title="Delete" ng-class="{'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated)}"> + <i class="fa fa-trash-o"></i></a> + <a class="text-info" ng-click="ctrl.viewProject(project.name)"><i class="fa fa-eye"></i></a> + </span> + </td> + </tr> + <tr ng-repeat-end=> + </tr> </tbody> - </table> + </table> + </div> </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/projects/projectsController.js b/testapi/opnfv_testapi/ui/components/projects/projectsController.js index 468407b..38764ea 100644 --- a/testapi/opnfv_testapi/ui/components/projects/projectsController.js +++ b/testapi/opnfv_testapi/ui/components/projects/projectsController.js @@ -20,7 +20,8 @@ .controller('ProjectsController', ProjectsController); ProjectsController.$inject = [ - '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert' + '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl', + 'raiseAlert', 'confirmModal' ]; /** @@ -28,80 +29,248 @@ * This controller is for the '/projects' page where a user can browse * through projects declared in TestAPI. */ - function ProjectsController($scope, $http, $filter, $state, testapiApiUrl, - raiseAlert) { + function ProjectsController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal) { var ctrl = this; ctrl.url = testapiApiUrl + '/projects'; + ctrl.create = create; + ctrl.listProjects = listProjects; + ctrl.openCreateModal = openCreateModal; + ctrl.viewProject = viewProject; + ctrl.openUpdateModal = openUpdateModal; ctrl.update = update; - ctrl.clearFilters = clearFilters; - - ctrl.createRequirements = [ - {label: 'name', type: 'text', required: true}, - {label: 'description', type: 'textarea', required: false} - ]; + ctrl.openDeleteModal = openDeleteModal; + ctrl.openBatchDeleteModal = openBatchDeleteModal; + ctrl.projectDelete = projectDelete; + ctrl.batchDelete = batchDelete; + ctrl.checkBox = []; + ctrl.checkBoxList = []; ctrl.name = ''; ctrl.details = ''; - ctrl.filterName=''; + ctrl.filterText=''; + /** * This will contact the TestAPI to create a new project. */ - function create() { - ctrl.showCreateError = false; + function create(project) { + ctrl.showError = false; ctrl.showCreateSuccess = false; - if(ctrl.name != ""){ - var projects_url = ctrl.url; - var body = { - name: ctrl.name, - description: ctrl.description - }; - ctrl.projectsRequest = - $http.post(projects_url, body).success(function (data){ - ctrl.showCreateSuccess = true ; - ctrl.update(); - }).catch(function (data) { - ctrl.showCreateError = true; - ctrl.error = data.statusText; - }); - ctrl.name = ""; - ctrl.description=""; - } - else{ - ctrl.showCreateError = true; - ctrl.error = 'Name is missing.' + var projects_url = ctrl.url; + var body = { + name: project.name, + description: project.description + }; + ctrl.projectsRequest = + $http.post(projects_url, body).success(function (data){ + ctrl.showCreateSuccess = true ; + ctrl.success = "Project is successfully created." + ctrl.listProjects(); + }).catch(function (data) { + ctrl.showError = true; + ctrl.error = data.statusText; + }); + } + + /** + * This will open the modal that will show the create + * project view + */ + function openCreateModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/projects/modals/projectModal.html', + controller: 'ProjectModalCtrl as ProjectModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Create Project", + successHandler: ctrl.create + }; + } + } + }); + } + + /** + * This will open the modal that will show the update + * project view + */ + function openUpdateModal(name){ + var project; + var index; + for(index in ctrl.data.projects){ + if(ctrl.data.projects[index].name==name){ + project = ctrl.data.projects[index] + continue + } } + $uibModal.open({ + templateUrl: 'testapi-ui/components/projects/modals/projectModal.html', + controller: 'ProjectModalCtrl as ProjectModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Update Project", + successHandler: ctrl.update, + project : project + }; + } + } + }); + } + + /** + * This will contact the TestAPI to update an existing test case. + */ + function update(name, project) { + ctrl.showError = false; + ctrl.showSuccess = false; + var projectUrl = ctrl.url + '/' + name; + ctrl.testCasesRequest = + $http.put(projectUrl, project).success(function (data){ + ctrl.showSuccess = true ; + ctrl.success = "Project is successfully updated." + listProjects(); + }) + .catch(function (data) { + ctrl.showError = true; + ctrl.error = data.statusText; + }); } /** * This will contact the TestAPI to get a listing of declared projects. */ - function update() { - ctrl.showUpdateError = false; + function listProjects() { + ctrl.showError = false; var content_url = ctrl.url + '?'; - var name = ctrl.filterName; - if(name){ + var filterText = ctrl.filterText; + if(filterText != ''){ content_url = content_url + 'name=' + - name; + filterText; } ctrl.resultsRequest = $http.get(content_url).success(function (data) { ctrl.data = data; }).catch(function (data) { ctrl.data = null; - ctrl.showUpdateError = true; + ctrl.showError = true; ctrl.error = data.statusText; }); } + function viewProject(name){ + $state.go('project', {'name':name}, {reload: true}); + } + + /** + * This will contact the TestAPI to delete a project for given + * name. + */ + function projectDelete(projectName){ + var projectUrl = ctrl.url + "/" + projectName + $http.delete(projectUrl).success(function(){ + ctrl.showSuccess = true ; + ctrl.success = "Projects is successfully deleted" + ctrl.listProjects(); + }).catch(function (data) { + ctrl.showError = true; + ctrl.showSuccess = false; + ctrl.error = data.statusText; + }); + } + /** - * This function will clear all filters and update the projects - * listing. + * This will delete list of projects. */ - function clearFilters() { - ctrl.filterName = null; - ctrl.showUpdateError = false; - ctrl.update(); + function batchDelete(){ + var index; + var checkedBox = []; + ctrl.checkBox.forEach(function(project, index){ + if(!ctrl.showError){ + if(project){ + projectDelete(ctrl.data.projects[index].name); + } + } + }); + ctrl.checkBox = [] + } + + /** + * This will open the modal that will show the batch delete confirm + * message + */ + function openBatchDeleteModal() { + confirmModal("Delete",ctrl.batchDelete); + } + + /** + * This will open the modal that will show the delete confirm + * message + */ + function openDeleteModal(name) { + confirmModal("Delete", ctrl.projectDelete, name); + } + + ctrl.listProjects(); + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('ProjectModalCtrl', ProjectModalCtrl); + ProjectModalCtrl.$inject = ['$scope', '$uibModalInstance', 'data']; + function ProjectModalCtrl($scope, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.createRequirements = [ + {label: 'name', type: 'text', required: true}, + {label: 'description', type: 'textarea', required: false} + ]; + ctrl.project = { + name : '', + description : '' + } + if(ctrl.data.project){ + ctrl.projectName = ctrl.data.project.name + ctrl.project = ctrl.data.project + delete ctrl.project._id; + } + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + if (angular.isDefined(ctrl.data.successHandler)) { + if(ctrl.project.name != ""){ + $uibModalInstance.close(); + if(ctrl.data.project){ + ctrl.data.successHandler(ctrl.projectName, ctrl.project); + }else{ + ctrl.data.successHandler(ctrl.project); + } + }else{ + ctrl.showCreateError = true; + ctrl.error = 'Name is missing.' + } + } + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); } } + })(); diff --git a/testapi/opnfv_testapi/ui/components/results/results.html b/testapi/opnfv_testapi/ui/components/results/results.html index 2ae5339..0e7b8d5 100644 --- a/testapi/opnfv_testapi/ui/components/results/results.html +++ b/testapi/opnfv_testapi/ui/components/results/results.html @@ -18,15 +18,28 @@ </div> </form> <div class="row" style="margin-bottom:24px;"></div> -<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"> +<div class="result-filters" style="border-top: none;"> + <div class="row podTable" style="vertical-align:middle"> + <div class="col-sm-1 pull-right"> + <button type="button" class="btn btn-danger" ng-click="ctrl.clearFilters()"> + <i class="fa fa-search"></i> Clear + </button> + </div> + <div class="col-sm-1 pull-right"> + <button type="button" class="btn btn-success" ng-click="ctrl.filterList()"> + <i class="fa fa-search"></i> Filter</button> + </div> + <div class="col-sm-2 pull-right" ng-class="{'hidden': ctrl.filter=='start_date' || ctrl.filter=='end_date'}"> + <span style="margin-top:6px">Search: </span> + <input type="text" class="form-control search" style="display:inline;width:105px;padding-left:6px;" + ng-Model="ctrl.filterText" placeholder="Search String"> + </div> + <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='start_date'}"> + <span style="margin-top:6px">Start Date: </span> + <p class="input-group" style="width:48%;display:inline-flex;"> <input type="text" class="form-control" uib-datepicker-popup="{{ctrl.format}}" - ng-model="ctrl.startDate" is-open="ctrl.startOpen" + ng-model="ctrl.filterText" is-open="ctrl.startOpen" close-text="Close" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'startOpen')"> @@ -35,12 +48,12 @@ </span> </p> </div> - <div class="col-md-3"> - <label for="cpid">End Date</label> - <p class="input-group"> + <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='end_date'}"> + <span style="margin-top:6px">End Date: </span> + <p class="input-group" style="width:48%;display:inline-flex;"> <input type="text" class="form-control" uib-datepicker-popup="{{ctrl.format}}" - ng-model="ctrl.endDate" is-open="ctrl.endOpen" + ng-model="ctrl.filterText" is-open="ctrl.endOpen" close-text="Close" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'endOpen')"> @@ -49,9 +62,29 @@ </span> </p> </div> - <div class="col-md-3" style="margin-top:24px;"> - <button type="submit" class="btn btn-primary" ng-click="ctrl.update()">Filter</button> - <button type="submit" class="btn btn-primary btn-danger" ng-click="ctrl.clearFilters()">Clear</button> + <div class="col-md-2 row pull-right" style="width: 20%;"> + <span style="margin-top:6px">Filter: </span> + <select ng-model="ctrl.filter" class="form-control" style="display:inline; width:150px;"> + <option value="pod" ng-disabled="ctrl.testFilter('pod')" >Pod Name</option> + <option value="project" ng-disabled="ctrl.testFilter('project')" >Project Name</option> + <option value="case" ng-disabled="ctrl.testFilter('case')">Case Name</option> + <option value="installer" ng-disabled="ctrl.testFilter('installer')">Installer</option> + <option value="version" ng-disabled="ctrl.testFilter('version')">Version</option> + <option value="scenario" ng-disabled="ctrl.testFilter('scenario')">Scenario</option> + <option value="build_tag" ng-disabled="ctrl.testFilter('build_tag')">Build Tag</option> + <option value="criteria" ng-disabled="ctrl.testFilter('criteria')">Criteria</option> + <option value="start_date" ng-disabled="ctrl.testFilter('start_date')">Start Date</option> + <option value="end_date" ng-disabled="ctrl.testFilter('end_date')">End Date</option> + </select> + </div> + + <div class='filter-box'> + <div class='filter-tag' ng-repeat="(key, tag) in ctrl.tagArray"> + {{key}} : {{tag}} + <div class='delete-tag' ng-click='ctrl.deleteTag(key)'> + × + </div> + </div> </div> </div> </div> @@ -103,7 +136,7 @@ boundary-links="true" rotate="false" num-pages="ctrl.numPages" - ng-change="ctrl.update()"> + ng-change="ctrl.filterList()"> </uib-pagination> </div> </div> diff --git a/testapi/opnfv_testapi/ui/components/results/resultsController.js b/testapi/opnfv_testapi/ui/components/results/resultsController.js index cc6cc0b..e9b4443 100644 --- a/testapi/opnfv_testapi/ui/components/results/resultsController.js +++ b/testapi/opnfv_testapi/ui/components/results/resultsController.js @@ -51,7 +51,6 @@ var ctrl = this; ctrl.uploadFile=uploadFile; - ctrl.update = update; ctrl.open = open; ctrl.clearFilters = clearFilters; ctrl.associateMeta = associateMeta; @@ -60,6 +59,11 @@ ctrl.associateProductVersion = associateProductVersion; ctrl.getProductVersions = getProductVersions; ctrl.prepVersionEdit = prepVersionEdit; + ctrl.deleteTag = deleteTag; + ctrl.filterList= filterList; + ctrl.testFilter = testFilter + + ctrl.tagArray = {} /** Mappings of Interop WG components to marketing program names. */ ctrl.targetMappings = { @@ -104,8 +108,7 @@ $state.go('home'); } - ctrl.pageHeader = ctrl.isUserResults ? - 'Private test results' : 'Community test results'; + ctrl.pageHeader = "Test Results" ctrl.pageParagraph = ctrl.isUserResults ? 'Your most recently uploaded test results are listed here.' : @@ -118,10 +121,24 @@ if (ctrl.isUserResults) { ctrl.authRequest = $scope.auth.doSignCheck() - .then(ctrl.update); + .then(ctrl.filterList); // ctrl.getUserProducts(); } else { - ctrl.update(); + ctrl.filterList(); + } + + function deleteTag(index){ + delete ctrl.tagArray[index]; + ctrl.filterList(); + } + + function testFilter(text){ + for (var filter in ctrl.tagArray){ + if(text==filter){ + return true; + } + } + return false; } @@ -138,7 +155,7 @@ .success(function(data){ var id = data.href.substr(data.href.lastIndexOf('/')+1); ctrl.uploadState = "Upload succeed. Result id is " + id; - ctrl.update(); + ctrl.filterList(); }) .error(function(data, status){ @@ -159,37 +176,50 @@ * This will contact the TestAPI API to get a listing of test run * results. */ - function update() { + function filterList(){ + if(ctrl.filter && ctrl.filterText!=""){ + ctrl.tagArray[ctrl.filter] = ctrl.filterText; + } ctrl.showError = false; - // Construct the API URL based on user-specified filters. var content_url = testapiApiUrl + '/results' + '?page=' + ctrl.currentPage; - var start = $filter('date')(ctrl.startDate, 'yyyy-MM-dd'); - if (start) { - content_url = - content_url + '&from=' + start + ' 00:00:00'; - } - var end = $filter('date')(ctrl.endDate, 'yyyy-MM-dd'); - if (end) { - content_url = content_url + '&to=' + end + ' 23:59:59'; - } - if (ctrl.isUserResults) { - content_url = content_url + '&signed'; + for(var key in ctrl.tagArray){ + if(key=="start_date"){ + var start = $filter('date')(ctrl.tagArray[key], 'yyyy-MM-dd'); + if (start) { + content_url = + content_url + '&from=' + start + ' 00:00:00'; + } + } + else if(key=="end_date"){ + var end = $filter('date')(ctrl.tagArray[key], 'yyyy-MM-dd'); + if (end) { + content_url = content_url + '&to=' + end + ' 23:59:59'; + } + } + else{ + content_url = content_url + "&" + key + "=" + ctrl.tagArray[key] + } + if (ctrl.isUserResults) { + content_url = content_url + '&signed'; + } } ctrl.resultsRequest = - $http.get(content_url).success(function (data) { - ctrl.data = data; - ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage; - ctrl.currentPage = ctrl.data.pagination.current_page; - }).error(function (error) { - ctrl.data = null; - ctrl.totalItems = 0; - ctrl.showError = true; - ctrl.error = - 'Error retrieving results listing from server: ' + - angular.toJson(error); - }); + $http.get(content_url).success(function (data) { + ctrl.data = data; + ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage; + ctrl.currentPage = ctrl.data.pagination.current_page; + }).error(function (error) { + ctrl.data = null; + ctrl.totalItems = 0; + ctrl.showError = true; + ctrl.error = + 'Error retrieving results listing from server: ' + + angular.toJson(error); + }); + ctrl.filterText = '' } + ctrl.filterList(); /** * This is called when the date filter calendar is opened. It @@ -209,9 +239,9 @@ * listing. */ function clearFilters() { - ctrl.startDate = null; - ctrl.endDate = null; - ctrl.update(); + ctrl.tagArray = {} + ctrl.filter = undefined + ctrl.filterList(); } /** diff --git a/testapi/opnfv_testapi/ui/config.json b/testapi/opnfv_testapi/ui/config.json index 5d48c7b..26dfd08 100644 --- a/testapi/opnfv_testapi/ui/config.json +++ b/testapi/opnfv_testapi/ui/config.json @@ -1 +1,2 @@ -{"testapiApiUrl": "http://localhost:8000/api/v1"} +{"testapiApiUrl": "http://localhost:8000/api/v1", +"authenticate": true} diff --git a/testapi/opnfv_testapi/ui/index.html b/testapi/opnfv_testapi/ui/index.html index 3191858..98f1ed8 100644 --- a/testapi/opnfv_testapi/ui/index.html +++ b/testapi/opnfv_testapi/ui/index.html @@ -51,6 +51,8 @@ <script src="testapi-ui/components/profile/profileController.js"></script> <script src="testapi-ui/components/auth-failure/authFailureController.js"></script> <script src="testapi-ui/components/logout/logoutController.js"></script> + <script src="testapi-ui/components/projects/project/testCases/testCasesController.js"></script> + <script src="testapi-ui/components/projects/project/testCases/testCase/testCaseController.js"></script> <!-- Filters --> <script src="testapi-ui/shared/filters.js"></script> diff --git a/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js b/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js index fc0bfe6..5e79775 100644 --- a/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js +++ b/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js @@ -46,7 +46,6 @@ ctrl.cancel = cancel; ctrl.data = angular.copy(data); - console.log(ctrl.data) /** * Initiate confirmation and call the success handler with the * input text. |