Implemented the create function for the projects.
Wrote the e2e tests for the create function.
Change-Id: Iceac650573ca31b6246350c4d60033b42e0ffb0f
Signed-off-by: thuva4 <tharma.thuva@gmail.com>
dest: 'testapi-ui/assets',
},
components: {
+ expand: true,
+ cwd: '../../../opnfv_testapi/ui',
+ src: '**',
+ dest: 'components',
+ },
+ copyComponents: {
expand: true,
cwd: 'components',
src: '**',
async: true
}
},
+ deleteFiles: {
+ command: 'rm -r testapi-ui && rm -r components',
+ options: {
+ async: false
+ }
+ },
options: {
stdout: false,
stderr: false
instrument: {
files: ['components/**/*.js'],
options: {
- lazy: false,
- basePath: "./testapi-ui/"
+ lazy: false,
+ basePath: "./testapi-ui/"
}
},
karma: {
noColor: false,
coverageDir: '../../../opnfv_testapi/tests/UI/coverage',
args: {
- specs: ['../../../opnfv_testapi/tests/UI/e2e/podsControllerSpec.js']
+ specs: ['../../../opnfv_testapi/tests/UI/e2e/podsControllerSpec.js',
+ '../../../opnfv_testapi/tests/UI/e2e/projectControllerSpec.js']
}
},
local: {
options: {
print: 'detail'
}
- },
- protractor: {
- e2e: {
- options: {
- args: {
- specs: ['../../../opnfv_testapi/tests/UI/e2e/podsControllerSpec.js']
- },
- configFile: '../../../opnfv_testapi/tests/UI/protractor-conf.js',
- keepAlive: true
- }
- }
- }
+ }
});
grunt.registerTask('test', [
'karma:unit'
grunt.registerTask('e2e', [
'copy:assets',
'copy:components',
+ 'copy:copyComponents',
'copy:shared',
'copy:filesPng',
'copy:filesIco',
'shell:startSelenium',
'wait:default',
'protractor_coverage',
- 'makeReport'
- // 'protractor'
+ 'makeReport',
+ 'shell:deleteFiles'
+
]);
}
templateUrl: 'testapi-ui/components/pods/pods.html',
controller: 'PodsController as ctrl'
}).
+ state('projects', {
+ url: '/projects',
+ templateUrl: 'testapi-ui/components/projects/projects.html',
+ controller: 'ProjectsController as ctrl'
+ }).
state('communityResults', {
url: '/community_results',
templateUrl: 'testapi-ui/components/results/results.html',
$rootScope.auth.doSignIn = doSignIn;
$rootScope.auth.doSignOut = doSignOut;
$rootScope.auth.doSignCheck = doSignCheck;
+ $rootScope.auth.doSubmitterCheck = doSubmitterCheck;
var sign_in_url = testapiApiUrl + '/auth/signin';
var sign_out_url = testapiApiUrl + '/auth/signout';
function doSignOut() {
$rootScope.auth.currentUser = null;
$rootScope.auth.isAuthenticated = false;
+ $rootScope.auth.projectNames = [];
$window.location.href = sign_out_url;
}
success(function (data) {
$rootScope.auth.currentUser = data;
$rootScope.auth.isAuthenticated = true;
+ $rootScope.auth.projectNames = $rootScope.auth.doSubmitterCheck(data.groups);
}).
error(function () {
$rootScope.auth.currentUser = null;
$rootScope.auth.isAuthenticated = false;
+ $rootScope.auth.projectNames = [];
});
}
+ function doSubmitterCheck(groups){
+ var projectNames = []
+ for(var index=0;index<groups.length; index++){
+ if(groups[index].indexOf('-submitters')>=0){
+ projectNames.push(groups[index].split('-')[2])
+ }
+ }
+ return projectNames;
+ }
+
$rootScope.auth.doSignCheck();
}
<script src="testapi-ui/components/profile/profileController.js"></script>
<script src="testapi-ui/components/auth-failure/authFailureController.js"></script>
<script src="testapi-ui/components/logout/logoutController.js"></script>
+ <script src="testapi-ui/components/projects/projectsController.js"></script>
<!-- Filters -->
<script src="testapi-ui/shared/filters.js"></script>
<li ng-class="{ active: header.isActive('/about')}"><a ui-sref="about">About</a></li>
<li ng-class="{ active: header.isActive('/pods')}"><a ui-sref="pods">Pods</a></li>
<li ng-class="{ active: header.isActive('/community_results')}"><a ui-sref="communityResults">Community Results</a></li>
+ <li ng-class="{ active: header.isActive('/projects')}"><a ui-sref="projects">Projects</a></li>
<!--
<li ng-class="{ active: header.isCatalogActive('public')}" class="dropdown" uib-dropdown>
<a role="button" class="dropdown-toggle" uib-dropdown-toggle>
def is_authorized(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
- if CONF.api_authenticate and self.table in ['pods']:
+ if CONF.api_authenticate and self.table in ['pods', 'projects']:
testapi_id = self.get_secure_cookie(constants.TESTAPI_ID)
if not testapi_id:
raises.Unauthorized(message.not_login())
if not user_info:
raises.Unauthorized(message.not_lfid())
kwargs['owner'] = testapi_id
+ if self.table in ['projects']:
+ query = kwargs.get('query')
+ query_data = query()
+ group = "opnfv-gerrit-" + query_data['name'] + "-submitters"
+ if group not in user_info['groups']:
+ raises.Unauthorized(message.no_permission())
ret = yield gen.coroutine(method)(self, *args, **kwargs)
raise gen.Return(ret)
return wrapper
def must_int(name):
return '{} must be int'.format(name)
+
+
+def no_permission():
+ return 'You do not have permission to perform this action'
@web.asynchronous
@gen.coroutine
- @check.is_authorized
@check.valid_token
@check.no_body
@check.miss_fields
+ @check.is_authorized
@check.values_check
@check.carriers_exist
@check.new_not_exists
},
response: {
data: {
- pods: [{role: "community-ci", name: "test", owner: "testUser", details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", creation_date: "2017-10-25 11:58:25.926168"}]
+ pods: [{role: "community-ci", name: "test", owner: "testUser",
+ details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed",
+ creation_date: "2017-10-25 11:58:25.926168"}]
}
}
}]);
mock.teardown();
var buttonFilter = element(by.buttonText('Filter'));
buttonFilter.click().then(function(){
- expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')).isDisplayed()).toBe(true);
+ expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope'))
+ .isDisplayed()).toBe(true);
});
});
},
response: {
data: {
- "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", "user": "testUser", "groups": ["opnfv-testapi-users"], "email": "testuser@test.com"
+ "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed",
+ "user": "testUser", "groups": ["opnfv-testapi-users",
+ "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com"
}
}
}
details.sendKeys('DemoDetails');
var buttonCreate = element(by.buttonText('Create'));
buttonCreate.click().then(function(){
- expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')).isDisplayed()).toBe(false);
+ expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope'))
+ .isDisplayed()).toBe(false);
});
});
},
response: {
data: {
- "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", "user": "testUser", "groups": ["opnfv-testapi-users"], "email": "testuser@test.com"
+ "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed",
+ "user": "testUser", "groups": ["opnfv-testapi-users"],
+ "email": "testuser@test.com"
}
}
}
details.sendKeys('DemoDetails');
var buttonCreate = element(by.buttonText('Create'));
buttonCreate.click().then(function(){
- expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')).isDisplayed()).toBe(true);
+ expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope'))
+ .isDisplayed()).toBe(true);
});
- })
+ });
});
\ No newline at end of file
--- /dev/null
+'use strict';
+
+var mock = require('protractor-http-mock');
+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);
+ });
+
+ 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();
+ });
+
+});
+
+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"
+ }
+ }
+ }
+ ]);
+ });
+
+ 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'
+ }
+ }
+ ]);
+ });
+
+ 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.ng-binding.ng-scope")).isDisplayed()).toBe(true);
+ });
+ });
+})
from opnfv_testapi.models import base_models
from opnfv_testapi.models import pod_models
+from opnfv_testapi.models import project_models
from opnfv_testapi.tests.unit import fake_pymongo
_id=str(ObjectId()),
owner='ValidUser',
create_date=str(datetime.now()))
+ self.project_e = project_models.Project(
+ name='functest',
+ description='functest test',
+ _id=str(ObjectId()),
+ create_date=str(datetime.now()))
+
self.req_d = None
self.req_e = None
self.addCleanup(self._clear)
'groups': [
'opnfv-testapi-users',
'opnfv-gerrit-functest-submitters',
+ 'opnfv-gerrit-qtip-submitters',
'opnfv-gerrit-qtip-contributors']
})
+##############################################################################
+# Copyright (c) 2016 ZTE Corporation
+# feng.xiaowei@zte.com.cn
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
import httplib
import unittest
class TestProjectBase(base.TestBase):
def setUp(self):
super(TestProjectBase, self).setUp()
- self.req_d = project_models.ProjectCreateRequest('vping',
- 'vping-ssh test')
- self.req_e = project_models.ProjectCreateRequest('doctor',
- 'doctor test')
+ self.req_d = project_models.ProjectCreateRequest('qtip',
+ 'qtip-ssh test')
+ self.req_e = project_models.ProjectCreateRequest('functest',
+ 'functest test')
self.get_res = project_models.Project
self.list_res = project_models.Projects
self.update_res = project_models.Project
class TestProjectCreate(TestProjectBase):
+
+ @executor.create(httplib.BAD_REQUEST, message.not_login())
+ def test_notlogin(self):
+ return self.req_d
+
+ @executor.mock_valid_lfid()
@executor.create(httplib.BAD_REQUEST, message.no_body())
def test_withoutBody(self):
return None
+ @executor.mock_valid_lfid()
@executor.create(httplib.BAD_REQUEST, message.missing('name'))
def test_emptyName(self):
return project_models.ProjectCreateRequest('')
+ @executor.mock_valid_lfid()
@executor.create(httplib.BAD_REQUEST, message.missing('name'))
def test_noneName(self):
return project_models.ProjectCreateRequest(None)
+ @executor.mock_valid_lfid()
@executor.create(httplib.OK, 'assert_create_body')
def test_success(self):
return self.req_d
+ @executor.mock_valid_lfid()
@executor.create(httplib.FORBIDDEN, message.exist_base)
def test_alreadyExist(self):
self.create_d()
class TestProjectGet(TestProjectBase):
+
+ @executor.mock_valid_lfid()
def setUp(self):
super(TestProjectGet, self).setUp()
self.create_d()
class TestProjectUpdate(TestProjectBase):
+ @executor.mock_valid_lfid()
def setUp(self):
super(TestProjectUpdate, self).setUp()
_, d_body = self.create_d()
class TestProjectDelete(TestProjectBase):
+ @executor.mock_valid_lfid()
def setUp(self):
super(TestProjectDelete, self).setUp()
self.create_d()
import unittest
from opnfv_testapi.common import message
-from opnfv_testapi.models import project_models
from opnfv_testapi.models import result_models
from opnfv_testapi.models import testcase_models
from opnfv_testapi.tests.unit import executor
self.list_res = result_models.TestResults
self.update_res = result_models.TestResult
self.basePath = '/api/v1/results'
- self.req_project = project_models.ProjectCreateRequest(
- self.project,
- 'vping test')
+ fake_pymongo.projects.insert(self.project_e.format())
self.req_testcase = testcase_models.TestcaseCreateRequest(
self.case,
'/cases/vping',
'vping-ssh test')
fake_pymongo.pods.insert(self.pod_d.format())
- self.create_help('/api/v1/projects', self.req_project)
self.create_help('/api/v1/projects/%s/cases',
self.req_testcase,
self.project)
import unittest
from opnfv_testapi.common import message
-from opnfv_testapi.models import project_models
from opnfv_testapi.models import testcase_models
from opnfv_testapi.tests.unit import executor
+from opnfv_testapi.tests.unit import fake_pymongo
from opnfv_testapi.tests.unit.handlers import test_base as base
class TestCaseBase(base.TestBase):
def setUp(self):
super(TestCaseBase, self).setUp()
+ self.project = 'functest'
self.req_d = testcase_models.TestcaseCreateRequest('vping_1',
'/cases/vping_1',
'vping-ssh test')
self.list_res = testcase_models.Testcases
self.update_res = testcase_models.Testcase
self.basePath = '/api/v1/projects/%s/cases'
- self.create_project()
+ fake_pymongo.projects.insert(self.project_e.format())
def assert_body(self, case, req=None):
if not req:
self.assertIsNotNone(new._id)
self.assertIsNotNone(new.creation_date)
- def create_project(self):
- req_p = project_models.ProjectCreateRequest('functest',
- 'vping-ssh test')
- self.create_help('/api/v1/projects', req_p)
- self.project = req_p.name
-
def create_d(self):
return super(TestCaseBase, self).create_d(self.project)
--- /dev/null
+<h3>Projects</h3>
+<p> </p>
+
+<div class="row" style="margin-bottom:24px;"></div>
+<div class="project-create" ng-class="{ 'hidden': ! (auth.projectNames.length>0) }">
+ <h4>Create</h4>
+ <div class="row">
+ <div ng-repeat="require in ctrl.createRequirements">
+ <div class="create-project" style="margin-left:24px;">
+ <p class="input-group">
+ <label for="cpid">{{require.label|capitalize}}: </label>
+ <a ng-if="require.type == 'text'">
+ <input type="text" dynamic-model="'ctrl.' + require.label"/>
+ </a>
+ <a ng-if="require.type == 'textarea'">
+ <textarea rows="2" cols="50" dynamic-model="'ctrl.' + require.label">
+ </textarea>
+ </a>
+ </p>
+ </div>
+ </div>
+
+ <div class="col-md-3" style="margin-top:12px; margin-left:8px;">
+ <button type="submit" class="btn btn-primary" ng-click="ctrl.create()">Create</button>
+ </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>
\ No newline at end of file
--- /dev/null
+/*
+ * 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('ProjectsController', ProjectsController);
+
+ ProjectsController.$inject = [
+ '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert'
+ ];
+
+ /**
+ * TestAPI Project Controller
+ * This controller is for the '/projects' page where a user can browse
+ * through projects declared in TestAPI.
+ */
+ function ProjectsController($scope, $http, $filter, $state, testapiApiUrl,
+ raiseAlert) {
+ var ctrl = this;
+ ctrl.url = testapiApiUrl + '/projects';
+ ctrl.create = create;
+
+ ctrl.createRequirements = [
+ {label: 'name', type: 'text', required: true},
+ {label: 'description', type: 'textarea', required: false}
+ ];
+
+ ctrl.name = '';
+ ctrl.details = '';
+
+ /**
+ * This will contact the TestAPI to create a new project.
+ */
+ function create() {
+ ctrl.showError = false;
+ ctrl.showSuccess = false;
+ if(ctrl.name != ""){
+ var projects_url = ctrl.url;
+ var body = {
+ name: ctrl.name,
+ description: ctrl.description
+ };
+ ctrl.projectsRequest =
+ $http.post(projects_url, body).success(function (data){
+ ctrl.showSuccess = true ;
+ })
+ .error(function (data) {
+ ctrl.showError = true;
+ ctrl.error = 'Error creating the new Project from server:' + angular.toJson(data);
+ });
+ ctrl.name = "";
+ ctrl.description="";
+ }
+ else{
+ ctrl.showError = true;
+ ctrl.error = 'Name is missing.'
+ }
+ }
+ }
+})();