diff options
author | thuva4 <tharma.thuva@gmail.com> | 2017-11-16 14:51:29 +0530 |
---|---|---|
committer | thuva4 <tharma.thuva@gmail.com> | 2017-11-16 16:31:55 +0530 |
commit | 141e16fcbdcacc02ff30d861bf76082b51d4c287 (patch) | |
tree | af8f398f254d9e1ba27b392b9e36957a75af77b9 | |
parent | 265e10a036b545d9d4e15bebef17e38e4b013af3 (diff) |
Update and Delete functionalities for projects
Implemented the update and delete functions for the
projects and wrote the e2e tests for the both
functions.
Change-Id: I917dd9503f145b0dde61dd9970bd855f9711335e
Signed-off-by: thuva4 <tharma.thuva@gmail.com>
17 files changed, 883 insertions, 172 deletions
diff --git a/testapi/3rd_party/static/testapi-ui/Gruntfile.js b/testapi/3rd_party/static/testapi-ui/Gruntfile.js index f82269e..13f484f 100644 --- a/testapi/3rd_party/static/testapi-ui/Gruntfile.js +++ b/testapi/3rd_party/static/testapi-ui/Gruntfile.js @@ -14,7 +14,9 @@ module.exports = function (grunt) { base: './', middleware: function(connect, options, middlewares) { middlewares.unshift(function(req, res, next) { - if (req.method.toUpperCase() == 'POST') req.method='GET'; + if (req.method.toUpperCase() == 'POST' || req.method.toUpperCase() == "PUT"){ + req.method='GET'; + } return next(); }); return middlewares; @@ -118,6 +120,7 @@ module.exports = function (grunt) { coverageDir: '../../../opnfv_testapi/tests/UI/coverage', args: { specs: ['../../../opnfv_testapi/tests/UI/e2e/podsControllerSpec.js', + '../../../opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js', '../../../opnfv_testapi/tests/UI/e2e/projectControllerSpec.js'] } }, diff --git a/testapi/3rd_party/static/testapi-ui/app.js b/testapi/3rd_party/static/testapi-ui/app.js index 0b35162..dbb56a6 100644 --- a/testapi/3rd_party/static/testapi-ui/app.js +++ b/testapi/3rd_party/static/testapi-ui/app.js @@ -69,6 +69,11 @@ templateUrl: 'testapi-ui/components/projects/projects.html', controller: 'ProjectsController as ctrl' }). + state('project', { + url: '/projects/:name', + templateUrl: 'testapi-ui/components/projects/project/project.html', + controller: 'ProjectController as ctrl' + }). state('communityResults', { url: '/community_results', templateUrl: 'testapi-ui/components/results/results.html', diff --git a/testapi/3rd_party/static/testapi-ui/index.html b/testapi/3rd_party/static/testapi-ui/index.html index 45162dc..ac29aca 100644 --- a/testapi/3rd_party/static/testapi-ui/index.html +++ b/testapi/3rd_party/static/testapi-ui/index.html @@ -47,6 +47,7 @@ <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/projectsController.js"></script> + <script src="testapi-ui/components/projects/project/projectController.js"></script> <!-- Filters --> <script src="testapi-ui/shared/filters.js"></script> diff --git a/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html b/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html index 82478a5..e5397e0 100644 --- a/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html +++ b/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html @@ -1,11 +1,21 @@ <div class="modal-header"><h3 class="modal-title">Confirm</h3></div> <div class="modal-body"> + <div class="confirm" ng-class="{ 'hidden': confirmModal.data.text=='Delete' }"> <div class="form-group"> <label for="confirmText">{{confirmModal.data.text}}:</label> <textarea type="text" class="form-control" rows="5" ng-model="confirmModal.inputText" id="confirmText"> </textarea> </div> + </div> + <div class="Delete" ng-class="{ 'hidden': confirmModal.data.text!='Delete' }"> + <div class="form-group"> + <label for="confirmText"> You are about to delete.</label> + <br> + Do you want to proceed? + </div> + </div> + </div> <div class="modal-footer"> <button class="btn btn-primary" ng-click="confirmModal.confirm()">Ok</button> diff --git a/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js b/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js index 76c74df..aba205e 100644 --- a/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js +++ b/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js @@ -13,7 +13,7 @@ function confirmModal($uibModal) { return function(text, successHandler) { $uibModal.open({ - templateUrl: '/shared/alerts/confirmModal.html', + templateUrl: '/testapi-ui/shared/alerts/confirmModal.html', controller: 'CustomConfirmModalController as confirmModal', size: 'md', resolve: { diff --git a/testapi/opnfv_testapi/common/check.py b/testapi/opnfv_testapi/common/check.py index 432a6c1..1155d24 100644 --- a/testapi/opnfv_testapi/common/check.py +++ b/testapi/opnfv_testapi/common/check.py @@ -28,10 +28,17 @@ def is_authorized(method): user_info = yield dbapi.db_find_one('users', {'user': testapi_id}) if not user_info: raises.Unauthorized(message.not_lfid()) - kwargs['owner'] = testapi_id + if "owner" in kwargs: + kwargs['owner'] = testapi_id if self.table in ['projects']: query = kwargs.get('query') - query_data = query() + if type(query) is not dict: + query_data = query() + else: + if self.json_args is None: + query_data = query + else: + query_data = self.json_args group = "opnfv-gerrit-" + query_data['name'] + "-submitters" if group not in user_info['groups']: raises.Unauthorized(message.no_permission()) diff --git a/testapi/opnfv_testapi/handlers/base_handlers.py b/testapi/opnfv_testapi/handlers/base_handlers.py index df7f520..a2fdb19 100644 --- a/testapi/opnfv_testapi/handlers/base_handlers.py +++ b/testapi/opnfv_testapi/handlers/base_handlers.py @@ -78,10 +78,10 @@ class GenericApiHandler(web.RequestHandler): @check.valid_token @check.no_body @check.miss_fields + @check.new_not_exists @check.is_authorized @check.values_check @check.carriers_exist - @check.new_not_exists def _create(self, **kwargs): """ :param miss_checks: [miss1, miss2] @@ -179,6 +179,7 @@ class GenericApiHandler(web.RequestHandler): @web.asynchronous @gen.coroutine @check.not_exist + @check.is_authorized def _delete(self, data, query=None): yield dbapi.db_delete(self.table, query) self.finish_request() @@ -188,6 +189,7 @@ class GenericApiHandler(web.RequestHandler): @check.no_body @check.not_exist @check.updated_one_not_exist + @check.is_authorized def _update(self, data, query=None, **kwargs): data = self.table_cls.from_dict(data) update_req = self._update_requests(data) diff --git a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js index 8cf7467..cb1d95d 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js @@ -20,6 +20,10 @@ describe('testing the Pods page for anonymous user', function () { }]); }); + afterEach(function(){ + mock.teardown(); + }); + it( 'should navigate to pods link ', function() { browser.get(baseURL); var podslink = element(by.linkText('Pods')).click(); @@ -128,6 +132,10 @@ describe('testing the Pods page for authorized user', function () { ]); }); + afterEach(function(){ + mock.teardown(); + }); + it('create button is visible for authorized user', function () { browser.get(baseURL + '/#/pods'); var buttonCreate = element(by.buttonText('Create')); diff --git a/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js index 921625d..475e037 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js @@ -1,224 +1,354 @@ 'use strict'; var mock = require('protractor-http-mock'); -var baseURL = "http://localhost:8000" +var baseURL = "http://localhost:8000/#/" describe('testing the Project Link for anonymous user', function () { - - it( 'should not show the Project Link for anonymous user', function() { - mock.teardown(); - browser.get(baseURL); - var projectslink = element(by.linkText('Projects')); - expect(projectslink.isPresent()).toBe(true); + beforeEach(function(){ + mock([ + { + 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" + } + } + } + ]); }); - it( 'navigate anonymous user to project page', function() { - browser.get(baseURL+'#/projects'); + afterEach(function(){ + mock.teardown(); + }); + + it( 'navigate to the project page', function() { + browser.get(baseURL+"projects/testproject"); var EC = browser.ExpectedConditions; - browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + browser.wait(EC.urlContains(baseURL+ 'projects/testproject'), 10000); + }); + + it('show the project details for anonymous user ', function(){ + var table = $$('.projects-table.ng-scope tr'); + var projectDetailsLable = ['Name','Description','Creation date'] + var projectDetails = ['testproject', 'dsfsd','2017-11-15 14:30:31.200259'] + table.each(function(row,index) { + var rowElems = row.$$('td'); + expect(rowElems.count()).toBe(2); + expect(rowElems.get(0).getText()).toMatch(projectDetailsLable[index]); + expect(rowElems.get(1).getText()).toMatch(projectDetails[index]); + }); }); - it('create button is not visible for anonymous user ', function () { - browser.get(baseURL+'#/projects'); - var buttonCreate = element(by.buttonText('Create')); - expect(buttonCreate.isDisplayed()).toBeFalsy(); + it('should not show the update & delete button', function(){ + var buttonUpdate = element(by.buttonText('Update Project')); + var buttonDelete = element(by.buttonText('Delete Project')); + expect(buttonUpdate.isDisplayed()).toBeFalsy(); + expect(buttonDelete.isDisplayed()).toBeFalsy(); }); }); -describe('testing the Project Link for user who is not in submitter group', function () { - beforeEach(function(){ - mock([ - { - request: { - path: '/api/v1/profile', + +describe('testing the Project Link for authorized user(not a submitter)', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/projects/testproject', method: 'GET' - }, - response: { - data: { - "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", - "user": "testUser", "groups": ["opnfv-testapi-users"], - "email": "testuser@test.com" - } + }, + response: { + data: { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" } } - ]); - }); - - it( 'should show the Project Link for user', function() { - browser.get(baseURL); - var projectslink = element(by.linkText('Projects')); - expect(projectslink.isPresent()).toBe(true); - }); - - it( 'should navigate the user to the Project page', function() { - browser.get(baseURL); - var projectslink = element(by.linkText('Projects')).click(); - var EC = browser.ExpectedConditions; - browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); - }); - - it('create button is not visible for user', function () { - browser.get(baseURL+'#/projects'); - var buttonCreate = element(by.buttonText('Create')); - expect(buttonCreate.isDisplayed()).toBeFalsy(); - }); -}) - -describe('testing the Project Link for user who is in submitter group', function () { + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'navigate to the project page', function() { + browser.get(baseURL+"projects/testproject"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ 'projects/testproject'), 10000); + }); + + it('show the project details for user ', function(){ + var table = $$('.projects-table.ng-scope tr'); + var projectDetailsLable = ['Name','Description','Creation date'] + var projectDetails = ['testproject', 'dsfsd','2017-11-15 14:30:31.200259'] + table.each(function(row,index) { + var rowElems = row.$$('td'); + expect(rowElems.count()).toBe(2); + expect(rowElems.get(0).getText()).toMatch(projectDetailsLable[index]); + expect(rowElems.get(1).getText()).toMatch(projectDetails[index]); + }); + }); + + it('should not show the update & delete button', function(){ + var buttonUpdate = element(by.buttonText('Update Project')); + var buttonDelete = element(by.buttonText('Delete Project')); + expect(buttonUpdate.isDisplayed()).toBeFalsy(); + expect(buttonDelete.isDisplayed()).toBeFalsy(); + }); + +}); + +describe('testing the Project Link for authorized user(a submitter)', function () { beforeEach(function(){ mock([ { request: { - path: '/api/v1/profile', + path: '/api/v1/projects/testproject', method: 'GET' }, response: { data: { - "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", - "user": "testUser", "groups": ["opnfv-testapi-users", - "opnfv-gerrit-testProject1-submitters", - "opnfv-gerrit-testProject2-submitters" ], - "email": "testuser@test.com" + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" } } }, { request: { - path: '/api/v1/projects', - method: 'POST' + path: '/api/v1/projects/testproject1', + method: 'GET' }, response: { data: { - href: baseURL+"/api/v1/projects/testProject1" + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject1" } } }, { request: { path: '/api/v1/projects', - method: 'POST', + 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: 'DELETE' + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/projects/testproject1', + method: 'DELETE' + }, + response: { + status : 403 + } + }, + { + request: { + path: '/api/v1/projects/testproject', + method: 'PUT', data: { name: 'testProject2', description : 'demoDescription', } }, response: { - status : 403 + status : 200, + data : { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject2" + } } }, { request: { - path: '/api/v1/projects', - method: 'POST', + path: '/api/v1/projects/testproject', + method: 'PUT', data: { - name: 'testProject3', + name: 'testProject1', description : 'demoDescription', } }, response: { - status : 403, - data : 'You do not have permission to perform this action' + status : 403 } - } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-testProject-submitters", + "opnfv-gerrit-testProject2-submitters" ], + "email": "testuser@test.com" + } + } + }, ]); }); - it( 'should show the Project Link for user', function() { - browser.get(baseURL); - var projectslink = element(by.linkText('Projects')); - expect(projectslink.isPresent()).toBe(true); - }); + afterEach(function(){ + mock.teardown(); + }); - it( 'should navigate the user to the Project page', function() { - browser.get(baseURL); - var projectslink = element(by.linkText('Projects')).click(); + it( 'navigate to the project page', function() { + browser.get(baseURL+"projects/testproject"); var EC = browser.ExpectedConditions; - browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + browser.wait(EC.urlContains(baseURL+ 'projects/testproject'), 10000); }); - it('create button is visible for user', function () { - browser.get(baseURL+'#/projects'); - var buttonCreate = element(by.buttonText('Create')); - expect(buttonCreate.isDisplayed()).toBe(true); + it('show the project details for user ', function(){ + var table = $$('.projects-table.ng-scope tr'); + var projectDetailsLable = ['Name','Description','Creation date'] + var projectDetails = ['testproject', 'dsfsd','2017-11-15 14:30:31.200259'] + table.each(function(row,index) { + var rowElems = row.$$('td'); + expect(rowElems.count()).toBe(2); + expect(rowElems.get(0).getText()).toMatch(projectDetailsLable[index]); + expect(rowElems.get(1).getText()).toMatch(projectDetails[index]); + }); }); - it('Show error when user click the create button with a empty name', function () { - browser.get(baseURL+ '/#/projects'); - var description = element(by.model('ctrl.description')); - description.sendKeys('DemoDescription'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click(); - expect(element(by.cssContainingText(".alert","Name is missing.")) - .isDisplayed()).toBe(true); - }); + it('should show the update & delete button', function(){ + var buttonUpdate = element(by.buttonText('Update Project')); + var buttonDelete = element(by.buttonText('Delete Project')); + expect(buttonUpdate.isDisplayed()).toBe(true); + expect(buttonDelete.isDisplayed()).toBe(true); + }); - it('Show error when user click the create button with an already existing name', function () { - browser.get(baseURL+ '/#/projects'); - var name = element(by.model('ctrl.name')); - var details = element(by.model('ctrl.description')); - name.sendKeys('testProject2'); - details.sendKeys('demoDescription'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click(); - expect(element(by.cssContainingText(".alert", - "Error creating the new Project from server:undefined")) - .isDisplayed()).toBe(true); + it('show the update modal when user clicks the update button', function(){ + browser.get(baseURL+"projects/testproject"); + var buttonDelete = element(by.buttonText('Update Project')).click(); + var EC = protractor.ExpectedConditions; + var elm = element(by.css(".modal-body")); + browser.wait(EC.textToBePresentInElement(elm, "Update"), 5000); + expect(elm.isDisplayed()).toBe(true); + var buttonCancel = element(by.buttonText('Cancel')).click(); + expect(elm.isPresent()).toEqual(false); }); - it('Show error when user try to create a project which he is not belonged to ', function () { - browser.get(baseURL+ '/#/projects'); - var name = element(by.model('ctrl.name')); - var details = element(by.model('ctrl.description')); - name.sendKeys('testProject3'); - details.sendKeys('demoDescription'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click(); + it('send a update request to server and show success when we click ok', function(){ + browser.get(baseURL+"projects/testproject"); + var buttonUpdate = element(by.buttonText('Update Project')).click(); + var EC = protractor.ExpectedConditions; + var elm = element(by.css(".modal-body")); + browser.wait(EC.textToBePresentInElement(elm, "Update"), 5000); + expect(elm.isDisplayed()).toBe(true); + var name = element(by.model('updateModal.name')); + var description = element(by.model('updateModal.description')); + name.click().clear().sendKeys('testProject2'); + description.click().clear().sendKeys('demoDescription'); + var buttonOk = element(by.buttonText('Ok')).click(); + expect(element(by.cssContainingText(".alert.alert-success", + "Update Success")) + .isDisplayed()).toBe(true); + }); + + it('show error when server send a error response when we click ok', function(){ + browser.get(baseURL+"projects/testproject"); + var buttonUpdate = element(by.buttonText('Update Project')).click(); + var EC = protractor.ExpectedConditions; + var elm = element(by.css(".modal-body")); + browser.wait(EC.textToBePresentInElement(elm, "Update"), 5000); + expect(elm.isDisplayed()).toBe(true); + var name = element(by.model('updateModal.name')); + var description = element(by.model('updateModal.description')); + name.click().clear().sendKeys('testProject1'); + description.click().clear().sendKeys('demoDescription'); + var buttonOk = element(by.buttonText('Ok')).click(); expect(element(by.cssContainingText(".alert", - 'Error creating the new Project from server:"You do not have permission to perform this action"')).isDisplayed()) - .toBe(true); + "Error updating the existing Project from server: undefined")) + .isDisplayed()).toBe(true); }); - it('Do not show error if input is acceptable', function () { - var name = element(by.model('ctrl.name')); - var details = element(by.model('ctrl.description')); - name.sendKeys('testProject1'); - details.sendKeys('demoDescription'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click().then(function(){ - expect(element(by.cssContainingText(".alert", - "Create Success")) - .isDisplayed()).toBe(true); - }); + it('show the confirm modal when user clicks the delete button', function(){ + var buttonDelete = element(by.buttonText('Delete Project')).click(); + var EC = protractor.ExpectedConditions; + var elm = element(by.css(".modal-body")); + browser.wait(EC.textToBePresentInElement(elm, "You are about to delete."), 5000); + expect(elm.isDisplayed()).toBe(true); + var buttonCancel = element(by.buttonText('Cancel')).click(); + expect(elm.isPresent()).toEqual(false); }); - it('If backend is not responding then show error when user click the create button',function(){ - mock.teardown(); - mock([ - { - request: { - path: '/api/v1/profile', - method: 'GET' - }, - response: { - data: { - "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", - "user": "testUser", "groups": ["opnfv-testapi-users", - "opnfv-gerrit-testProject1-submitters", - "opnfv-gerrit-testProject2-submitters" ], - "email": "testuser@test.com" - } - } - } - ]); - browser.get(baseURL+ '/#/projects'); - var name = element(by.model('ctrl.name')); - var details = element(by.model('ctrl.description')); - name.sendKeys('testProject1'); - details.sendKeys('demoDescription'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click().then(function(){ - expect(element(by.css(".alert.alert-danger.ng-binding.ng-scope")).isDisplayed()).toBe(true); - }); - }); -}) + it('send a delete request to server when we click ok', function(){ + var buttonDelete = element(by.buttonText('Delete Project')).click(); + var EC = protractor.ExpectedConditions; + var elm = element(by.css(".modal-body")); + browser.wait(EC.textToBePresentInElement(elm, "You are about to delete."), 5000); + expect(elm.isDisplayed()).toBe(true); + var buttonCancel = element(by.buttonText('Ok')).click(); + browser.wait(EC.urlContains(baseURL+ 'projects'), 10000); + }); + + it('show the error message when we click ok', function(){ + browser.get(baseURL+"projects/testproject1"); + var buttonDelete = element(by.buttonText('Delete Project')).click(); + var EC = protractor.ExpectedConditions; + var elm = element(by.css(".modal-body")); + browser.wait(EC.textToBePresentInElement(elm, "You are about to delete."), 5000); + expect(elm.isDisplayed()).toBe(true); + var buttonCancel = element(by.buttonText('Ok')).click(); + // browser.wait(EC.urlContains(baseURL+ 'projects'), 10000); + expect(element(by.cssContainingText(".alert", + "Error deleting project from server: undefined")) + .isDisplayed()).toBe(true); + // browser.pause(); + }); + +});
\ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js new file mode 100644 index 0000000..64a5aeb --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js @@ -0,0 +1,271 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000" + +describe('testing the Projects Link 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" + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the Projects Link for anonymous user', function() { + mock.teardown(); + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')); + expect(projectslink.isPresent()).toBe(true); + }); + + it( 'navigate anonymous user to project page', function() { + browser.get(baseURL+'#/projects'); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + }); + + it('create button is not visible for anonymous user ', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + + it('Show projects list when user comes to the projects page', function () { + var firstBookName = element(by.repeater('(index, project) in ctrl.data.projects'). + row(0).column('{{project.name}}')); + expect(firstBookName).toBeDefined(); + }); + + it('redirect to project page when user clicks a project',function(){ + var projectlink = element(by.linkText('testproject')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects/testproject'), 10000); + }); + +}); + +describe('testing the Project Link 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" + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the Project Link for user', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')); + expect(projectslink.isPresent()).toBe(true); + }); + + it( 'should navigate the user to the Project page', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + }); + + it('create button is not visible for user', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); +}) + +describe('testing the Project Link 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-testProject1-submitters", + "opnfv-gerrit-testProject2-submitters" ], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/projects', + method: 'POST' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject1" + } + } + }, + { + request: { + path: '/api/v1/projects', + method: 'POST', + data: { + name: 'testProject2', + description : 'demoDescription', + } + }, + response: { + status : 403 + } + }, + { + request: { + path: '/api/v1/projects', + method: 'POST', + data: { + name: 'testProject3', + description : 'demoDescription', + } + }, + response: { + status : 403, + data : 'You do not have permission to perform this action' + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the Project Link for user', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')); + expect(projectslink.isPresent()).toBe(true); + }); + + it( 'should navigate the user to the Project page', function() { + browser.get(baseURL); + var projectslink = element(by.linkText('Projects')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/projects'), 10000); + }); + + it('create button is visible for user', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('Show error when user click the create button with a empty name', function () { + browser.get(baseURL+ '/#/projects'); + var description = element(by.model('ctrl.description')); + description.sendKeys('DemoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + expect(element(by.cssContainingText(".alert","Name is missing.")) + .isDisplayed()).toBe(true); + }); + + it('Show error when user click the create button with an already existing name', function () { + browser.get(baseURL+ '/#/projects'); + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject2'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + expect(element(by.cssContainingText(".alert", + "Error creating the new Project from server:undefined")) + .isDisplayed()).toBe(true); + }); + + it('Show error when user try to create a project which he is not belonged to ', function () { + browser.get(baseURL+ '/#/projects'); + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject3'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + expect(element(by.cssContainingText(".alert", + 'Error creating the new Project from server:"You do not have permission to perform this action"')).isDisplayed()) + .toBe(true); + }); + + it('Do not show error if input is acceptable', function () { + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject1'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click().then(function(){ + expect(element(by.cssContainingText(".alert", + "Create Success")) + .isDisplayed()).toBe(true); + }); + }); + + it('If backend is not responding then show error when user click the create button',function(){ + mock.teardown(); + mock([ + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-testProject1-submitters", + "opnfv-gerrit-testProject2-submitters" ], + "email": "testuser@test.com" + } + } + } + ]); + browser.get(baseURL+ '/#/projects'); + var name = element(by.model('ctrl.name')); + var details = element(by.model('ctrl.description')); + name.sendKeys('testProject1'); + details.sendKeys('demoDescription'); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click().then(function(){ + expect(element(by.css(".alert.alert-danger")).isDisplayed()).toBe(true); + }); + }); +}) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_base.py b/testapi/opnfv_testapi/tests/unit/handlers/test_base.py index eb147cc..4e5c0d9 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_base.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_base.py @@ -61,7 +61,8 @@ class TestBase(testing.AsyncHTTPTestCase): 'opnfv-testapi-users', 'opnfv-gerrit-functest-submitters', 'opnfv-gerrit-qtip-submitters', - 'opnfv-gerrit-qtip-contributors'] + 'opnfv-gerrit-qtip-contributors', + 'opnfv-gerrit-apex-submitters'] }) def tearDown(self): diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_project.py b/testapi/opnfv_testapi/tests/unit/handlers/test_project.py index 2873ab0..9bc0e86 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_project.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_project.py @@ -110,21 +110,25 @@ class TestProjectUpdate(TestProjectBase): def test_withoutBody(self): return None, 'noBody' + @executor.mock_valid_lfid() @executor.update(httplib.NOT_FOUND, message.not_found_base) def test_notFound(self): return self.req_e, 'notFound' + @executor.mock_valid_lfid() @executor.update(httplib.FORBIDDEN, message.exist_base) def test_newNameExist(self): return self.req_e, self.req_d.name + @executor.mock_valid_lfid() @executor.update(httplib.FORBIDDEN, message.no_update()) def test_noUpdate(self): return self.req_d, self.req_d.name + @executor.mock_valid_lfid() @executor.update(httplib.OK, '_assert_update') def test_success(self): - req = project_models.ProjectUpdateRequest('newName', 'new description') + req = project_models.ProjectUpdateRequest('apex', 'apex test') return req, self.req_d.name def _assert_update(self, req, body): @@ -145,6 +149,7 @@ class TestProjectDelete(TestProjectBase): def test_notFound(self): return 'notFound' + @executor.mock_valid_lfid() @executor.delete(httplib.OK, '_assert_delete') def test_success(self): return self.req_d.name diff --git a/testapi/opnfv_testapi/ui/projects/project/project.html b/testapi/opnfv_testapi/ui/projects/project/project.html new file mode 100644 index 0000000..9d46364 --- /dev/null +++ b/testapi/opnfv_testapi/ui/projects/project/project.html @@ -0,0 +1,25 @@ +<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> + +<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> +<div ng-show="ctrl.showError" class="alert alert-danger col-md-9" 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.showSuccess" class="alert alert-success col-md-9" role="alert"> + <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> + Update Success +</div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/projects/project/projectController.js b/testapi/opnfv_testapi/ui/projects/project/projectController.js new file mode 100644 index 0000000..8f4bd20 --- /dev/null +++ b/testapi/opnfv_testapi/ui/projects/project/projectController.js @@ -0,0 +1,183 @@ +/* + * 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('ProjectController', ProjectController); + + ProjectController.$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 ProjectController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal) { + var ctrl = this; + ctrl.name = $state.params['name']; + 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. + */ + function loadDetails() { + ctrl.showError = false; + ctrl.projectsRequest = + $http.get(ctrl.url).success(function (data) { + ctrl.data = data; + }).error(function (error) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = + 'Error retrieving projects from server: ' + + angular.toJson(error); + }); + } + 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/projects/project/updateModal.html b/testapi/opnfv_testapi/ui/projects/project/updateModal.html new file mode 100644 index 0000000..ab8d64e --- /dev/null +++ b/testapi/opnfv_testapi/ui/projects/project/updateModal.html @@ -0,0 +1,26 @@ +<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/projects/projects.html b/testapi/opnfv_testapi/ui/projects/projects.html index 62a968b..55f8683 100644 --- a/testapi/opnfv_testapi/ui/projects/projects.html +++ b/testapi/opnfv_testapi/ui/projects/projects.html @@ -1,8 +1,8 @@ <h3>Projects</h3> -<p> </p> <div class="row" style="margin-bottom:24px;"></div> -<div class="project-create" ng-class="{ 'hidden': ! (auth.projectNames.length>0) }"> +<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"> @@ -19,20 +19,34 @@ </p> </div> </div> - - <div class="col-md-3" style="margin-top:12px; margin-left:8px;"> - <button type="submit" class="btn btn-primary" ng-click="ctrl.create()">Create</button> + <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.showError" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{ctrl.error}} + </div> + <div ng-show="ctrl.showSuccess" class="alert alert-success" role="alert"> + <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> + Create Success + </div> </div> </div> </div> -<div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> - <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> - <span class="sr-only">Error:</span> - {{ctrl.error}} -</div> -<div ng-show="ctrl.showSuccess" class="alert alert-success" role="alert"> - <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> - Create Success +<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"> + <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> + </tbody> + </table> </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/projects/projectsController.js b/testapi/opnfv_testapi/ui/projects/projectsController.js index d2640b6..16002f6 100644 --- a/testapi/opnfv_testapi/ui/projects/projectsController.js +++ b/testapi/opnfv_testapi/ui/projects/projectsController.js @@ -33,6 +33,7 @@ var ctrl = this; ctrl.url = testapiApiUrl + '/projects'; ctrl.create = create; + ctrl.update = update; ctrl.createRequirements = [ {label: 'name', type: 'text', required: true}, @@ -57,6 +58,7 @@ ctrl.projectsRequest = $http.post(projects_url, body).success(function (data){ ctrl.showSuccess = true ; + ctrl.update(); }) .error(function (data) { ctrl.showError = true; @@ -70,5 +72,23 @@ ctrl.error = 'Name is missing.' } } + + /** + * This will contact the TestAPI to get a listing of declared projects. + */ + function update() { + ctrl.showError = false; + ctrl.projectsRequest = + $http.get(ctrl.url).success(function (data) { + ctrl.data = data; + }).error(function (error) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = + 'Error retrieving projects from server: ' + + angular.toJson(error); + }); + } + ctrl.update(); } })(); |