Update and Delete functionalities for projects 27/47327/3
authorthuva4 <tharma.thuva@gmail.com>
Thu, 16 Nov 2017 09:21:29 +0000 (14:51 +0530)
committerthuva4 <tharma.thuva@gmail.com>
Thu, 16 Nov 2017 11:01:55 +0000 (16:31 +0530)
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:
testapi/3rd_party/static/testapi-ui/Gruntfile.js
testapi/3rd_party/static/testapi-ui/app.js
testapi/3rd_party/static/testapi-ui/index.html
testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html
testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js
testapi/opnfv_testapi/common/check.py
testapi/opnfv_testapi/handlers/base_handlers.py
testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js
testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js
testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js [new file with mode: 0644]
testapi/opnfv_testapi/tests/unit/handlers/test_base.py
testapi/opnfv_testapi/tests/unit/handlers/test_project.py
testapi/opnfv_testapi/ui/projects/project/project.html [new file with mode: 0644]
testapi/opnfv_testapi/ui/projects/project/projectController.js [new file with mode: 0644]
testapi/opnfv_testapi/ui/projects/project/updateModal.html [new file with mode: 0644]
testapi/opnfv_testapi/ui/projects/projects.html
testapi/opnfv_testapi/ui/projects/projectsController.js

index f82269e..13f484f 100644 (file)
@@ -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']
                        }
                    },
index 0b35162..dbb56a6 100644 (file)
                 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',
index 45162dc..ac29aca 100644 (file)
@@ -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>
index 82478a5..e5397e0 100644 (file)
@@ -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>
index 76c74df..aba205e 100644 (file)
@@ -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: {
index 432a6c1..1155d24 100644 (file)
@@ -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())
index df7f520..a2fdb19 100644 (file)
@@ -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)
index 8cf7467..cb1d95d 100644 (file)
@@ -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'));
index 921625d..475e037 100644 (file)
 '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 (file)
index 0000000..64a5aeb
--- /dev/null
@@ -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);
+        });
+       });
+})
index eb147cc..4e5c0d9 100644 (file)
@@ -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):
index 2873ab0..9bc0e86 100644 (file)
@@ -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 (file)
index 0000000..9d46364
--- /dev/null
@@ -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 (file)
index 0000000..8f4bd20
--- /dev/null
@@ -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 (file)
index 0000000..ab8d64e
--- /dev/null
@@ -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>
index 62a968b..55f8683 100644 (file)
@@ -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">
                 </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
index d2640b6..16002f6 100644 (file)
@@ -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;
                 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();
     }
 })();