summaryrefslogtreecommitdiffstats
path: root/testapi
diff options
context:
space:
mode:
Diffstat (limited to 'testapi')
-rw-r--r--testapi/3rd_party/static/testapi-ui/assets/css/style.css49
-rw-r--r--testapi/docs/developer/devguide/images/CAS-sequence.jpgbin0 -> 11785 bytes
-rw-r--r--testapi/docs/developer/devguide/web-portal.rst114
-rw-r--r--testapi/opnfv_testapi/common/check.py12
-rw-r--r--testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js76
-rw-r--r--testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js2
-rw-r--r--testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js36
-rw-r--r--testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js54
-rw-r--r--testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js4
-rw-r--r--testapi/opnfv_testapi/ui/Gruntfile.js16
-rw-r--r--testapi/opnfv_testapi/ui/components/pods/pods.html16
-rw-r--r--testapi/opnfv_testapi/ui/components/pods/podsController.js39
-rw-r--r--testapi/opnfv_testapi/ui/components/projects/projects.html16
-rw-r--r--testapi/opnfv_testapi/ui/components/projects/projectsController.js60
-rw-r--r--testapi/opnfv_testapi/ui/package.json1
-rw-r--r--testapi/testapi-client/etc/client.creds3
-rw-r--r--testapi/testapi-client/setup.cfg4
-rw-r--r--testapi/testapi-client/testapiclient/cli/pods.py5
-rw-r--r--testapi/testapi-client/testapiclient/cli/results.py100
-rw-r--r--testapi/testapi-client/testapiclient/client/__init__.py0
-rw-r--r--testapi/testapi-client/testapiclient/client/base.py23
-rw-r--r--testapi/testapi-client/testapiclient/client/pods.py11
-rw-r--r--testapi/testapi-client/testapiclient/models/__init__.py0
-rw-r--r--testapi/testapi-client/testapiclient/models/pods.py6
-rw-r--r--testapi/testapi-client/testapiclient/tests/unit/test_results.py98
-rw-r--r--testapi/testapi-client/testapiclient/utils/clientmanager.py6
26 files changed, 627 insertions, 124 deletions
diff --git a/testapi/3rd_party/static/testapi-ui/assets/css/style.css b/testapi/3rd_party/static/testapi-ui/assets/css/style.css
index fb21399..222911c 100644
--- a/testapi/3rd_party/static/testapi-ui/assets/css/style.css
+++ b/testapi/3rd_party/static/testapi-ui/assets/css/style.css
@@ -292,4 +292,53 @@ json-tree .key {
json-tree .leaf-value{
word-break: normal!important;
+}
+
+#toast {
+ visibility: hidden;
+ min-width: 250px;
+ margin-left: -125px;
+ color: #fff;
+ text-align: center;
+ border-radius: 10px;
+ padding: 16px;
+ position: fixed;
+ z-index: 1;
+ left: 50%;
+ bottom: 30px;
+}
+
+#toast.error{
+ background-color: #B03838;
+}
+
+#toast.success{
+ background-color: #1A911E;
+}
+
+#toast.show {
+ visibility: visible;
+
+ -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
+ animation: fadein 0.5s, fadeout 0.5s 2.5s;
+}
+
+@-webkit-keyframes fadein {
+ from {bottom: 0; opacity: 0;}
+ to {bottom: 30px; opacity: 1;}
+}
+
+@keyframes fadein {
+ from {bottom: 0; opacity: 0;}
+ to {bottom: 30px; opacity: 1;}
+}
+
+@-webkit-keyframes fadeout {
+ from {bottom: 30px; opacity: 1;}
+ to {bottom: 0; opacity: 0;}
+}
+
+@keyframes fadeout {
+ from {bottom: 30px; opacity: 1;}
+ to {bottom: 0; opacity: 0;}
} \ No newline at end of file
diff --git a/testapi/docs/developer/devguide/images/CAS-sequence.jpg b/testapi/docs/developer/devguide/images/CAS-sequence.jpg
new file mode 100644
index 0000000..a624871
--- /dev/null
+++ b/testapi/docs/developer/devguide/images/CAS-sequence.jpg
Binary files differ
diff --git a/testapi/docs/developer/devguide/web-portal.rst b/testapi/docs/developer/devguide/web-portal.rst
index 62b2f17..8c4bc6c 100644
--- a/testapi/docs/developer/devguide/web-portal.rst
+++ b/testapi/docs/developer/devguide/web-portal.rst
@@ -6,5 +6,115 @@
Web portal
==========
-.. toctree::
- :maxdepth: 2
+**Web-portal of OPNFV Testapi**:
+
+This project aims to provide the web interface for the Testapi framework. It uses the Restful APIs
+of the testapi framework to provide front-end functionalities.
+
+If you are interested in how TestAPI looks like, please visit OPNFV's official `TestAPI Server`__
+
+.. __: http://testresults.opnfv.org/test
+
+Pre-requsites
+=============
+
+In the web portal, we are using AngularJS(1.3.15) as the frontend framework with Bootstrap(v3) CSS.
+
+Running locally
+===============
+
+Installation
+^^^^^^^^^^^^
+
+Web portal will be installed with the testapi framework. No extra installation.
+
+.. code-block:: shell
+
+ python setup.py install
+
+Start Server
+^^^^^^^^^^^^
+
+.. code-block:: shell
+
+ *opnfv-testapi [--config-file <config.ini>]*
+
+If --config-file is provided, the specified configuration file will be employed
+when starting the server, or else /etc/opnfv_testapi/config.ini will be utilized
+by default.
+
+After executing the command successfully, a TestAPI server will be started on
+port 8000, to visit web portal, please access http://hostname:8000
+
+Test
+===============
+
+There are few external requirements for the testing.
+They are
+
+1. npm : node package manager
+ you can get the installation package for nodejs from the official `website`__
+
+ .. __: https://nodejs.org/en/
+
+2. grunt cli : Automation tool
+
+.. code-block:: shell
+
+ npm install -g grunt-cli
+
+After installing global dependencies, you have to install the required local node modules.
+
+.. code-block:: shell
+
+ npm install
+
+**Running tests**
+
+.. code-block:: shell
+
+ grunt e2e
+
+Authentication
+==============
+
+The web portal is using Linux identity server as the Central Authentication Service. The following
+diagram will explain the authentication process.
+
+.. image:: /images/CAS-sequence.jpg
+ :width: 600
+ :alt: Workflow of the Athentication
+
+When a new user initially logs into an application they won't have established a
+session with the application. Instead of displaying a login form asking for the username and
+password, the application (via the CAS Client) will redirect the browser to the linux foundation
+login page. Linux foundation identity server then authenticates the user. If the authentication
+fails, the Linux foundation login page is displayed again with an error message. So until
+authentication succeeds, the user will not be returned to the application.
+
+Authorization
+=============
+
+TestAPI has 3 level authorization layer. They are
+
+**Public**
+
+The public can view the resources(pod, project, testcase, result, deploy result, scenario).
+They do not have the access to create, delete or modify the resources.
+
+**User - Contributors**
+
+Contributors level user can view all the resources(pod, project, testcase, result, deploy result,
+scenario). They can create/delete/modify pod and scenario.
+
+They do not have the access to create project or testcase.
+
+**User - Submitter**
+
+Submitter level user can view all the resources(pod, project, testcase, result, deploy result,
+scenario). They can create/delete/modify pod and scenario.
+
+If user want to create/modify/delete a project or testcase then user has to be in the Submitter
+group for that specific project.
+
+Currently, we can't create/modify/delete results or deploy results from the web portal. \ No newline at end of file
diff --git a/testapi/opnfv_testapi/common/check.py b/testapi/opnfv_testapi/common/check.py
index 09a9270..fdc527f 100644
--- a/testapi/opnfv_testapi/common/check.py
+++ b/testapi/opnfv_testapi/common/check.py
@@ -65,12 +65,14 @@ def is_reource_tied(method):
}
if self.table in tied_maps:
if method.__name__ == '_update':
- if 'name' not in self.json_args:
- ret = yield gen.coroutine(method)(self, *args, **kwargs)
- raise gen.Return(ret)
+ if 'name' in self.json_args:
+ if self.json_args['name'] == kwargs.get('query')['name']:
+ ret = yield gen.coroutine(method)(
+ self, *args, **kwargs)
+ raise gen.Return(ret)
query_data[tied_maps[self.table][1]] = kwargs.get('query')['name']
- data = yield dbapi.db_find_one(tied_maps[self.table][0],
- query_data)
+ data = yield dbapi.db_find_one(
+ tied_maps[self.table][0], query_data)
if data:
raises.Unauthorized(message.tied_with_resource())
ret = yield gen.coroutine(method)(self, *args, **kwargs)
diff --git a/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js
index e287f29..6b9a2bf 100644
--- a/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js
+++ b/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js
@@ -250,7 +250,7 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var buttonAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[2]/button'))
+ var buttonAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[2]/button'))
expect(buttonAdd.isDisplayed()).toBe(true);
});
@@ -258,9 +258,9 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[3]/button'))
+ var installerDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[3]/button'))
expect(installerDelete.isDisplayed()).toBe(true);
});
@@ -268,11 +268,11 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
installerShow.click();
- var versionAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[2]/button'))
+ var versionAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[2]/button'))
versionAdd.click()
expect(versionAdd.isDisplayed()).toBe(true);
});
@@ -281,15 +281,15 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
installerShow.click();
- var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
+ var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
versionsShow.click();
- var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
versionShow.click()
- var versionDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody[1]/tr[1]/td[3]/button'))
+ var versionDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody[1]/tr[1]/td[3]/button'))
expect(versionDelete.isDisplayed()).toBe(true);
});
@@ -297,15 +297,15 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
installerShow.click();
- var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
+ var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
versionsShow.click();
- var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
versionShow.click()
- var projectAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[2]/button'))
+ var projectAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[2]/button'))
projectAdd.click()
expect(projectAdd.isDisplayed()).toBe(true);
});
@@ -314,17 +314,17 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
installerShow.click();
- var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
+ var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
versionsShow.click();
- var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
versionShow.click()
- var projectsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a'))
+ var projectsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a'))
projectsShow.click();
- var projectDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[1]/td[3]/button'))
+ var projectDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[1]/td[3]/button'))
projectDelete.click()
expect(projectDelete.isDisplayed()).toBe(true);
});
@@ -333,24 +333,24 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
installerShow.click();
- var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
+ var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
versionsShow.click();
- var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
versionShow.click()
- var projectsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a'))
+ var projectsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a'))
projectsShow.click();
- var projectShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var projectShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
projectShow.click();
- var customsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/a/p'))
+ var customsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/a/p'))
customsShow.click();
var row = element.all(by.repeater('(indexCU, custom) in project.customs')).first();
var cells = row.all(by.tagName('td'));
expect(cells.get(0).getText()).toContain("dvs");
- var buttonAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/button'))
+ var buttonAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/button'))
buttonAdd.click()
expect(buttonAdd.isDisplayed()).toBe(true);
});
@@ -359,24 +359,24 @@ describe('testing the scenarios page for anonymous user', function () {
browser.get(baseURL+"#/scenarios/test-scenario");
var EC = browser.ExpectedConditions;
browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000);
- var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[1]/a/p'))
+ var installersShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p'))
installersShow.click();
- var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
installerShow.click();
- var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
+ var versionsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p'))
versionsShow.click();
- var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var versionShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
versionShow.click()
- var projectsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a'))
+ var projectsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a'))
projectsShow.click();
- var projectShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
+ var projectShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a'))
projectShow.click();
- var customsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/a/p'))
+ var customsShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/a/p'))
customsShow.click();
var row = element.all(by.repeater('(indexCU, custom) in project.customs')).first();
var cells = row.all(by.tagName('td'));
expect(cells.get(0).getText()).toContain("dvs");
- var buttonDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[4]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/table/tbody/tr[1]/td[2]/button'))
+ var buttonDelete = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[3]/div/table/tbody[1]/tr[4]/td[2]/table/tbody/tr[1]/td[2]/button'))
buttonDelete.click()
expect(buttonDelete.isDisplayed()).toBe(true);
});
diff --git a/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js
index e00243b..40f60e4 100644
--- a/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js
+++ b/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js
@@ -383,7 +383,7 @@ describe('testing the result page for user', function () {
buttonClear.click();
var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
var cells = row.all(by.tagName('td'));
- expect(cells.get(0).getText()).toContain("3c9f8d62");
+ expect(cells.get(0).getText()).toContain("3c9f8d63");
});
it('view the deploy results ', function () {
diff --git a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js
index 16b219d..ac1f954 100644
--- a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js
+++ b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js
@@ -78,7 +78,7 @@ describe('testing the Pods page for anonymous user', function () {
it('Sort the results by mode', function () {
browser.get(baseURL+'#/pods');
- var sortMode = element(by.xpath('//*[@id="ng-app"]/body/div/div[6]/div/table/thead/tr/th[4]/a[2]/span'))
+ var sortMode = element(by.xpath('//*[@id="ng-app"]/body/div/div[5]/div/table/thead/tr/th[4]/a[2]/span'))
sortMode.click();
var row = element.all(by.repeater('(index, pod) in ctrl.data.pods')).first();
var cells = row.all(by.tagName('td'));
@@ -87,7 +87,7 @@ describe('testing the Pods page for anonymous user', function () {
it('Sort the results by role', function () {
browser.get(baseURL+'#/pods');
- var sortRole = element(by.xpath('//*[@id="ng-app"]/body/div/div[6]/div/table/thead/tr/th[3]/a[2]/span'))
+ var sortRole = element(by.xpath('//*[@id="ng-app"]/body/div/div[5]/div/table/thead/tr/th[3]/a[2]/span'))
sortRole.click();
var row = element.all(by.repeater('(index, pod) in ctrl.data.pods')).first();
var cells = row.all(by.tagName('td'));
@@ -262,8 +262,11 @@ describe('testing the Pods page for authorized user', function () {
.isDisplayed()).toBe(true);
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.cssContainingText(".alert","Delete Success"))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Delete Success"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Delete the pods ', function () {
@@ -272,8 +275,11 @@ describe('testing the Pods page for authorized user', function () {
deleteOperation.click();
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.cssContainingText(".alert","Delete Success"))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Delete Success"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Create the pod', function () {
@@ -286,8 +292,11 @@ describe('testing the Pods page for authorized user', function () {
name.sendKeys('test1');
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.cssContainingText(".alert","Create Success"))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Create Success"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Showing error when creating with a empty name ', function () {
@@ -299,8 +308,11 @@ describe('testing the Pods page for authorized user', function () {
browser.wait(EC.visibilityOf(name), 5000);
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click()
- expect(element(by.cssContainingText(".alert","Name is missing."))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".error.show","Name is missing."))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('cancel the delete confimation modal of the pod ', function () {
@@ -356,8 +368,11 @@ describe('testing the Pods page for authorized user', function () {
deleteOperation.click();
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.css(".alert.alert-danger"))
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error.show"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('view the test case ', function () {
@@ -394,8 +409,13 @@ describe('testing the Pods page for authorized user', function () {
},
]);
browser.get(baseURL+"#/pods");
- expect(element(by.css(".alert.alert-danger"))
+ var EC = browser.ExpectedConditions;
+ browser.wait(EC.urlContains(baseURL+ '/#/pods'), 5000);
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error.show"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
}); \ 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
index 1117ef5..da86389 100644
--- a/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js
+++ b/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js
@@ -324,8 +324,12 @@ describe('testing the Project Link for user who is in submitter group', function
name.sendKeys('testproject');
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.cssContainingText(".alert","Project is successfully created."))
+
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Project is successfully created."))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Show error if user doesnt have permission to Create the Project', function () {
@@ -340,7 +344,10 @@ describe('testing the Project Link for user who is in submitter group', function
description.sendKeys('demoDescription');
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.css(".alert.alert-danger")).isDisplayed()).toBe(true);
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error")).isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Showing error when creating with a empty name ', function () {
@@ -352,8 +359,11 @@ describe('testing the Project Link for user who is in submitter group', function
browser.wait(EC.visibilityOf(name), 5000);
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
+ browser.ignoreSynchronization = true;
expect(element(by.cssContainingText(".alert","Name is missing."))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Show error when user click the create button with an already existing name', function () {
@@ -368,7 +378,10 @@ describe('testing the Project Link for user who is in submitter group', function
description.sendKeys('demoDescription');
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.css(".alert.alert-danger")).isDisplayed()).toBe(true);
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error")).isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('cancel the delete confimation modal of the project ', function () {
@@ -388,8 +401,11 @@ describe('testing the Project Link for user who is in submitter group', function
.isDisplayed()).toBe(true);
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.cssContainingText(".alert","Projects is successfully deleted"))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Projects is successfully deleted"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it(' Show error if user doesnt has permission to delete the projects ', function () {
@@ -442,7 +458,10 @@ describe('testing the Project Link for user who is in submitter group', function
deleteOperation.click();
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.css(".alert.alert-danger")).isDisplayed()).toBe(true);
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error")).isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('cancel the Edit modal of the Project ', function () {
@@ -468,8 +487,12 @@ describe('testing the Project Link for user who is in submitter group', function
name.sendKeys('test1');
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click()
- expect(element(by.cssContainingText(".alert","Project is successfully updated."))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Project is successfully updated."))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
+
});
it('Show error if user doesnt has permission to edit the projects ', function () {
@@ -526,7 +549,10 @@ describe('testing the Project Link for user who is in submitter group', function
name.sendKeys('test1');
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click()
- expect(element(by.css(".alert.alert-danger")).isDisplayed()).toBe(true);
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error")).isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
it('Batch Delete the projects ', function () {
@@ -537,8 +563,12 @@ describe('testing the Project Link for user who is in submitter group', function
buttonDelete.click();
var buttonOK = element(by.buttonText('Ok'));
buttonOK.click();
- expect(element(by.cssContainingText(".alert","Projects is successfully deleted"))
+ browser.ignoreSynchronization = true;
+ expect(element(by.cssContainingText(".success.show","Projects is successfully deleted"))
.isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
+
});
it('If backend is not responding then show error when user click the create button',function(){
@@ -570,8 +600,10 @@ describe('testing the Project Link for user who is in submitter group', function
name.sendKeys('testproject');
details.sendKeys('demoDescription');
var buttonOK = element(by.buttonText('Ok'));
- buttonOK.click().then(function(){
- expect(element(by.css(".alert.alert-danger")).isDisplayed()).toBe(true);
- });
+ buttonOK.click()
+ browser.ignoreSynchronization = true;
+ expect(element(by.css(".error")).isDisplayed()).toBe(true);
+ browser.sleep(500);
+ browser.ignoreSynchronization = false;
});
})
diff --git a/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js
index 925e82c..48690a1 100644
--- a/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js
+++ b/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js
@@ -198,8 +198,8 @@ describe('testing the result page for anonymous user', function () {
browser.wait(EC.urlContains(baseURL+ '#/results'), 10000);
var resultLink = element(by.linkText('0e2643f4')).click();
browser.wait(EC.urlContains(baseURL+ '#/result/5a45170bbb2092000e2643f4'), 10000);
- expect(element(by.cssContainingText(".key.col-md-1","failures")).isDisplayed()).toBe(true);
- expect(element(by.cssContainingText(".leaf-value.col-md-11","0")).isDisplayed()).toBe(true);
+ expect(element(by.cssContainingText(".key.col-md-2","failures")).isDisplayed()).toBe(true);
+ expect(element(by.cssContainingText(".leaf-value.col-md-10","0")).isDisplayed()).toBe(true);
});
it('Should show the results in results page related to the filters', function () {
diff --git a/testapi/opnfv_testapi/ui/Gruntfile.js b/testapi/opnfv_testapi/ui/Gruntfile.js
index 805ad9f..ab59475 100644
--- a/testapi/opnfv_testapi/ui/Gruntfile.js
+++ b/testapi/opnfv_testapi/ui/Gruntfile.js
@@ -6,7 +6,22 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-wait');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-connect');
+ grunt.loadNpmTasks('grunt-convert');
grunt.initConfig({
+ convert: {
+ options: {
+ explicitArray: false,
+ },
+ json2xml: {
+ options: {
+ xml: {
+ header: true
+ }
+ },
+ src: ['../tests/UI/coverage/coverage.json'],
+ dest: '../tests/UI/coverage/coverage.xml'
+ }
+ },
connect: {
server: {
options: {
@@ -159,6 +174,7 @@ module.exports = function (grunt) {
'wait:default',
'protractor_coverage',
'makeReport',
+ 'convert',
'shell:deleteFiles'
]);
}
diff --git a/testapi/opnfv_testapi/ui/components/pods/pods.html b/testapi/opnfv_testapi/ui/components/pods/pods.html
index 8e66a9c..b5dadf5 100644
--- a/testapi/opnfv_testapi/ui/components/pods/pods.html
+++ b/testapi/opnfv_testapi/ui/components/pods/pods.html
@@ -19,22 +19,18 @@
<div class="col-sm-1 pull-right">
<button type="button" class="btn btn-success" ng-click="ctrl.listPods()">
<i class="fa fa-search"></i> Filter</button>
+ <div ng-class="{'show': ctrl.showError}" id="toast" class="error">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true" ></span>
+ {{ctrl.error}}</div>
+ <div ng-class="{'show': ctrl.showSuccess}" id="toast" class="success">
+ <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
+ {{ctrl.success}}</div>
</div>
<div class="col-sm-3 pull-right">
<span style="margin-top:6px">Search:&nbsp;&nbsp;</span>
<input type="text" class="form-control search" ng-enter="ctrl.listPods()" ng-Model="ctrl.filterText" placeholder="Search String">
</div>
</div>
-<div class="col-md-12">
- <div ng-show="ctrl.showError" class="col-md-12 alert alert-danger" role="alert">
- <span class="pull-right">&nbsp;{{ctrl.error}}</span>
- <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span>
- </div>
- <div ng-show="ctrl.showSuccess" class="col-md-12 alert alert-success" role="alert">
- <span class="pull-right">&nbsp;{{ctrl.success}}</span>
- <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span>
- </div>
-</div>
<div class="col-md-12" style="padding-right:0px">
<div class="table-responsive">
<table class="table table-bordered table-hover" ng-data="ctrl.data.pods">
diff --git a/testapi/opnfv_testapi/ui/components/pods/podsController.js b/testapi/opnfv_testapi/ui/components/pods/podsController.js
index c50fa5a..3b6ab97 100644
--- a/testapi/opnfv_testapi/ui/components/pods/podsController.js
+++ b/testapi/opnfv_testapi/ui/components/pods/podsController.js
@@ -21,7 +21,7 @@
PodsController.$inject = [
'$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert',
- 'confirmModal', 'keepState', 'sortService'
+ 'confirmModal', 'keepState', 'sortService', '$timeout'
];
/**
@@ -30,7 +30,7 @@
* through pods declared in TestAPI.
*/
function PodsController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl,
- raiseAlert, confirmModal, keepState, sortService) {
+ raiseAlert, confirmModal, keepState, sortService, $timeout) {
var ctrl = this;
ctrl.url = testapiApiUrl + '/pods';
ctrl.checkBox = []
@@ -48,6 +48,18 @@
ctrl.batchDelete = batchDelete;
ctrl.viewPod = viewPod
ctrl.sortBy = sortBy
+ ctrl.toastError = toastError
+ ctrl.toastSuccess = toastSuccess
+
+ function toastError() {
+ ctrl.showError = true
+ $timeout(function(){ ctrl.showError = false;}, 3000);
+ }
+
+ function toastSuccess() {
+ ctrl.showSuccess = true
+ $timeout(function(){ ctrl.showSuccess = false;}, 3000);
+ }
function sortBy(field){
ctrl.data.pods = sortService.sortFunction(ctrl.data.pods, field , ctrl.sorting[field] )
@@ -82,19 +94,21 @@
role: pod.role,
details: pod.details
};
- ctrl.podsRequest =
- $http.post(pods_url, body).success(function (data) {
- ctrl.showSuccess = true ;
+ ctrl.podsRequest = $http.post(pods_url, body)
+
+ ctrl.podsRequest.success(function (data) {
ctrl.success = "Create Success"
+ ctrl.toastSuccess()
ctrl.listPods();
}).catch(function (data) {
- ctrl.showError = true;
ctrl.error = data.statusText;
+ ctrl.toastError()
});
+ return ctrl.podsRequest
}
else{
- ctrl.showError = true;
ctrl.error = 'Name is missing.'
+ ctrl.toastError()
}
}
@@ -124,8 +138,8 @@
}
}).catch(function (data) {
ctrl.data = null;
- ctrl.showError = true;
ctrl.error = data.statusText;
+ ctrl.toastError()
});
}
@@ -139,12 +153,12 @@
function podDelete(podName){
var pods_url = ctrl.url + "/" + podName
$http.delete(pods_url).success(function(){
- ctrl.showSuccess = true ;
ctrl.success = "Delete Success"
+ ctrl.toastSuccess()
ctrl.listPods();
}).catch(function (data) {
- ctrl.showError = true;
ctrl.error = data.statusText;
+ ctrl.toastError()
});
}
@@ -247,9 +261,10 @@
* inputs.
*/
function confirm() {
- $uibModalInstance.close();
if (angular.isDefined(ctrl.data.successHandler)) {
- ctrl.data.successHandler(ctrl.pod);
+ ctrl.data.successHandler(ctrl.pod).success( function(data){
+ $uibModalInstance.close();
+ })
}
}
diff --git a/testapi/opnfv_testapi/ui/components/projects/projects.html b/testapi/opnfv_testapi/ui/components/projects/projects.html
index 5d514d1..3098aa2 100644
--- a/testapi/opnfv_testapi/ui/components/projects/projects.html
+++ b/testapi/opnfv_testapi/ui/components/projects/projects.html
@@ -21,16 +21,12 @@
<input type="text" class="form-control search" ng-enter="ctrl.listProjects()" ng-Model="ctrl.filterText" style="width:80%;" placeholder="Search By Name">
</div>
</div>
-<div class='clo-md-12'>
- <div ng-show="ctrl.showError" class="alert alert-danger" role="alert">
- <span class="pull-right">&nbsp;{{ctrl.error}}</span>
- <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span>
- </div>
- <div ng-show="ctrl.showSuccess" class="alert alert-success" role="alert">
- <span class="pull-right">&nbsp;{{ctrl.success}}</span>
- <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span>
- </div>
-</div>
+<div ng-class="{'show': ctrl.showError}" id="toast" class="error">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true" ></span>
+ {{ctrl.error}}</div>
+<div ng-class="{'show': ctrl.showSuccess}" id="toast" class="success">
+ <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
+ {{ctrl.success}}</div>
<div class='clo-md-12' style="padding-right:0px">
<div class="table-responsive">
<table class="table table-bordered table-hover" ng-data="ctrl.data.projects">
diff --git a/testapi/opnfv_testapi/ui/components/projects/projectsController.js b/testapi/opnfv_testapi/ui/components/projects/projectsController.js
index 4f0af0f..42100bd 100644
--- a/testapi/opnfv_testapi/ui/components/projects/projectsController.js
+++ b/testapi/opnfv_testapi/ui/components/projects/projectsController.js
@@ -21,7 +21,7 @@
ProjectsController.$inject = [
'$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl',
- 'raiseAlert', 'confirmModal', 'authenticate', 'keepState', 'sortService'
+ 'raiseAlert', 'confirmModal', 'authenticate', 'keepState', 'sortService', '$timeout'
];
/**
@@ -30,7 +30,7 @@
* through projects declared in TestAPI.
*/
function ProjectsController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl,
- raiseAlert, confirmModal, authenticate, keepState, sortService) {
+ raiseAlert, confirmModal, authenticate, keepState, sortService, $timeout) {
var ctrl = this;
ctrl.url = testapiApiUrl + '/projects';
@@ -51,27 +51,41 @@
ctrl.name = '';
ctrl.details = '';
ctrl.ascending = false;
+ ctrl.toastError = toastError
+ ctrl.toastSuccess = toastSuccess
+
+ function toastError() {
+ ctrl.showError = true
+ $timeout(function(){ ctrl.showError = false;}, 7000);
+ }
+
+ function toastSuccess() {
+ ctrl.showSuccess = true
+ $timeout(function(){ ctrl.showSuccess = false;}, 7000);
+ }
/**
* This will contact the TestAPI to create a new project.
*/
function create(project) {
- ctrl.showError = false;
- ctrl.showSuccess = false;
var projects_url = ctrl.url;
var body = {
name: project.name,
description: project.description
};
- ctrl.projectsRequest =
- $http.post(projects_url, body).success(function (data){
- ctrl.showSuccess = true ;
- ctrl.success = "Project is successfully created."
+ ctrl.projectsRequest = $http.post(projects_url, body)
+ ctrl.projectsRequest.success(function (data){
+ ctrl.success = "Project is successfully created.";
ctrl.listProjects();
+ ctrl.toastSuccess();
+ ctrl.request = true;
}).catch(function (data) {
- ctrl.showError = true;
ctrl.error = data.statusText;
+ ctrl.toastError();
+ ctrl.request = false;
});
+
+ return ctrl.projectsRequest
}
function sortByName(){
@@ -135,16 +149,17 @@
ctrl.showError = false;
ctrl.showSuccess = false;
var projectUrl = ctrl.url + '/' + name;
- ctrl.testCasesRequest =
- $http.put(projectUrl, project).success(function (data){
- ctrl.showSuccess = true ;
+ ctrl.projectRequest = $http.put(projectUrl, project)
+ ctrl.projectRequest.success(function (data){
ctrl.success = "Project is successfully updated."
- listProjects();
+ ctrl.listProjects();
+ ctrl.toastSuccess();
})
.catch(function (data) {
- ctrl.showError = true;
ctrl.error = data.statusText;
+ ctrl.toastError();
});
+ return ctrl.projectRequest
}
/**
@@ -174,8 +189,8 @@
}
}).catch(function (data) {
ctrl.data = null;
- ctrl.showError = true;
ctrl.error = data.statusText;
+ ctrl.toastError();
});
}
@@ -190,13 +205,12 @@
function projectDelete(projectName){
var projectUrl = ctrl.url + "/" + projectName
$http.delete(projectUrl).success(function(){
- ctrl.showSuccess = true ;
ctrl.success = "Projects is successfully deleted"
+ ctrl.toastSuccess();
ctrl.listProjects();
}).catch(function (data) {
- ctrl.showError = true;
- ctrl.showSuccess = false;
ctrl.error = data.statusText;
+ ctrl.toastError();
});
}
@@ -277,11 +291,15 @@
function confirm() {
if (angular.isDefined(ctrl.data.successHandler)) {
if(ctrl.project.name != ""){
- $uibModalInstance.close();
+ var success = false;
if(ctrl.data.project){
- ctrl.data.successHandler(ctrl.projectName, ctrl.project);
+ ctrl.data.successHandler(ctrl.projectName, ctrl.project).success(function (data){
+ $uibModalInstance.close();
+ })
}else{
- ctrl.data.successHandler(ctrl.project);
+ ctrl.data.successHandler(ctrl.project).success(function (data){
+ $uibModalInstance.close();
+ })
}
}else{
ctrl.showCreateError = true;
diff --git a/testapi/opnfv_testapi/ui/package.json b/testapi/opnfv_testapi/ui/package.json
index dc99239..2d4eb6e 100644
--- a/testapi/opnfv_testapi/ui/package.json
+++ b/testapi/opnfv_testapi/ui/package.json
@@ -3,6 +3,7 @@
"grunt": "~1.0.1",
"grunt-contrib-connect": "^1.0.2",
"grunt-contrib-copy": "^1.0.0",
+ "grunt-convert": "^0.1.12",
"grunt-karma": "~2.0.0",
"grunt-protractor-coverage": "^0.2.18",
"grunt-protractor-runner": "~5.0.0",
diff --git a/testapi/testapi-client/etc/client.creds b/testapi/testapi-client/etc/client.creds
index a082047..ee2a0ab 100644
--- a/testapi/testapi-client/etc/client.creds
+++ b/testapi/testapi-client/etc/client.creds
@@ -1,3 +1,4 @@
export testapi_url=http://localhost:8000/api/v1
export testapi_cas_signin_return=/auth/signin_return
-export testapi_cas_auth_url=https://identity.linuxfoundation.org/user/login?destination=cas/login%3Fservice%3D \ No newline at end of file
+export testapi_cas_auth_url=https://identity.linuxfoundation.org/user/login?destination=cas/login%3Fservice%3D
+export testapi_token=changeme \ No newline at end of file
diff --git a/testapi/testapi-client/setup.cfg b/testapi/testapi-client/setup.cfg
index b89fba4..ee1ba54 100644
--- a/testapi/testapi-client/setup.cfg
+++ b/testapi/testapi-client/setup.cfg
@@ -64,6 +64,10 @@ testapi =
deployresult get = testapiclient.cli.deployresults:DeployresultGet
deployresult getone = testapiclient.cli.deployresults:DeployresultGetOne
+ result create = testapiclient.cli.results:ResultCreate
+ result get = testapiclient.cli.results:ResultGet
+ result getone = testapiclient.cli.results:ResultGetOne
+
[egg_info]
tag_build =
tag_date = 0
diff --git a/testapi/testapi-client/testapiclient/cli/pods.py b/testapi/testapi-client/testapiclient/cli/pods.py
index 8d0970b..df63737 100644
--- a/testapi/testapi-client/testapiclient/cli/pods.py
+++ b/testapi/testapi-client/testapiclient/cli/pods.py
@@ -1,5 +1,6 @@
import json
+from testapiclient.client import pods
from testapiclient.utils import command
from testapiclient.utils import urlparse
@@ -67,8 +68,8 @@ class PodCreate(command.ShowOne):
return parser
def take_action(self, parsed_args):
- return self.format_output(
- self.app.client_manager.post(pods_url(), parsed_args.pod))
+ client = pods.PodsClient(client_manager=self.app.client_manager)
+ return self.format_output(client.create(parsed_args.pod))
class PodDelete(command.Command):
diff --git a/testapi/testapi-client/testapiclient/cli/results.py b/testapi/testapi-client/testapiclient/cli/results.py
new file mode 100644
index 0000000..56ea71f
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/cli/results.py
@@ -0,0 +1,100 @@
+import json
+
+from testapiclient.utils import command
+from testapiclient.utils import urlparse
+
+
+def results_url():
+ return urlparse.resource_join('results')
+
+
+def result_url(parsed_args):
+ return urlparse.path_join(results_url(), parsed_args.result_id)
+
+
+class ResultGet(command.Lister):
+
+ def get_parser(self, prog_name):
+ parser = super(ResultGet, self).get_parser(prog_name)
+ parser.add_argument('-case',
+ help='Search results using tesetcase')
+ parser.add_argument('-build-tag',
+ help='Search results using build tag')
+ parser.add_argument('-from',
+ help='Search results using from date')
+ parser.add_argument('-last',
+ help='Search results using last date')
+ parser.add_argument('-scenario',
+ help='Search results using scenario')
+ parser.add_argument('-period',
+ help='Search results using period')
+ parser.add_argument('-project',
+ help='Search results using project')
+ parser.add_argument('-to',
+ help='Search results using to')
+ parser.add_argument('---version',
+ help='Search results using version')
+ parser.add_argument('-criteria',
+ help='Search results using version')
+ parser.add_argument('-installer',
+ help='Search results using installer')
+ parser.add_argument('-pod',
+ help='Search results using pod')
+ parser.add_argument('-page',
+ help='Search results using page')
+ return parser
+
+ def take_action(self, parsed_args):
+ columns = (
+ '_id',
+ 'pod_name',
+ 'project_name',
+ 'case_name',
+ 'installer',
+ 'version',
+ 'scenario',
+ 'criteria',
+ 'start_date'
+ )
+ data = self.app.client_manager.get(
+ urlparse.query_by(results_url(),
+ ['case', 'build_tag', 'from', 'last',
+ 'scenario', 'period', 'project',
+ 'to', 'version',
+ 'criteria', 'installer', 'pod', 'page'],
+ parsed_args))
+ return self.format_output(columns, data.get('results', []))
+
+
+class ResultGetOne(command.ShowOne):
+
+ def get_parser(self, prog_name):
+ parser = super(ResultGetOne, self).get_parser(prog_name)
+ parser.add_argument('result_id',
+ help='Search result by result id')
+ return parser
+
+ def take_action(self, parsed_args):
+ return self.format_output(
+ self.app.client_manager.get(result_url(parsed_args)))
+
+
+class ResultCreate(command.ShowOne):
+
+ def get_parser(self, prog_name):
+ parser = super(ResultCreate, self).get_parser(prog_name)
+ parser.add_argument('result',
+ type=json.loads,
+ help='Result create request format:\n'
+ '\'{"project_name" : "","scenario" : "",'
+ '"stop_date" : "", "case_name" : "",'
+ '"build_tag" : "", "version" : "",'
+ '"pod_name" : "" , "criteria" : "",'
+ '"installer" : "", "start_date" : "",'
+ '"details" : ""}\'')
+ return parser
+
+ def take_action(self, parsed_args):
+ return self.format_output(
+ self.app.client_manager.post(
+ results_url(), parsed_args.result))
diff --git a/testapi/testapi-client/testapiclient/client/__init__.py b/testapi/testapi-client/testapiclient/client/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/client/__init__.py
diff --git a/testapi/testapi-client/testapiclient/client/base.py b/testapi/testapi-client/testapiclient/client/base.py
new file mode 100644
index 0000000..c45c9b7
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/client/base.py
@@ -0,0 +1,23 @@
+from testapiclient.utils import clientmanager
+from testapiclient.utils import urlparse
+
+
+class AuthOption(object):
+ def __init__(self, user=None, password=None):
+ self.u = user
+ self.p = password
+
+
+class Client(object):
+
+ resource = ''
+
+ def __init__(self, user=None, password=None, client_manager=None):
+ self.url = urlparse.resource_join(self.resource)
+ if client_manager:
+ self.clientmanager = client_manager
+ else:
+ self.clientmanager = clientmanager.ClientManager(
+ AuthOption(user, password))
+ if self.clientmanager.auth_required:
+ self.clientmanager.auth()
diff --git a/testapi/testapi-client/testapiclient/client/pods.py b/testapi/testapi-client/testapiclient/client/pods.py
new file mode 100644
index 0000000..4254da7
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/client/pods.py
@@ -0,0 +1,11 @@
+from testapiclient.client import base
+
+
+class PodsClient(base.Client):
+ resource = 'pods'
+
+ def __init__(self, **kwargs):
+ super(PodsClient, self).__init__(**kwargs)
+
+ def create(self, pod_req):
+ return self.clientmanager.post(self.url, pod_req)
diff --git a/testapi/testapi-client/testapiclient/models/__init__.py b/testapi/testapi-client/testapiclient/models/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/models/__init__.py
diff --git a/testapi/testapi-client/testapiclient/models/pods.py b/testapi/testapi-client/testapiclient/models/pods.py
new file mode 100644
index 0000000..27ea311
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/models/pods.py
@@ -0,0 +1,6 @@
+class PodCreateRequest(object):
+ def __init__(self, name='', mode='', details='', role=""):
+ self.name = name
+ self.mode = mode
+ self.details = details
+ self.role = role
diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_results.py b/testapi/testapi-client/testapiclient/tests/unit/test_results.py
new file mode 100644
index 0000000..83bcc9f
--- /dev/null
+++ b/testapi/testapi-client/testapiclient/tests/unit/test_results.py
@@ -0,0 +1,98 @@
+import json
+
+from mock import mock
+from six.moves.urllib import parse
+import testtools
+
+from testapiclient.cli import results
+from testapiclient.tests.unit import fakes
+from testapiclient.tests.unit import utils
+from testapiclient.utils import clientmanager
+
+
+class ResultTest(utils.TestCommand):
+ def setUp(self):
+ super(ResultTest, self).setUp()
+ self.base_url = parse.urljoin(self.api_url, 'results')
+ self.result_json = {
+ 'project_name': 'functest',
+ 'scenario': 'test-scenario',
+ 'stop_date': '2018-04-09 13:44:53',
+ 'case_name': 'test-case',
+ 'build_tag': 'test-build',
+ 'version': 'test-version',
+ 'pod_name': 'test-pod',
+ 'criteria': 'test-criteria',
+ 'installer': 'test-installer',
+ 'start_date': '2018-04-09 13:44:53',
+ 'details': 'test-details'
+ }
+ self.result_string = json.dumps(self.result_json)
+
+
+class ResultGetTest(ResultTest):
+
+ def setUp(self):
+ super(ResultGetTest, self).setUp()
+ self.results_rsp = {'results': [self.result_json]}
+
+ def test_get(self):
+ self.get_mock.return_value = fakes.FakeResponse(data=self.results_rsp)
+ result_get = results.ResultGet(self.app, mock.Mock())
+ args = ['-case', 'dfs']
+ verifies = [('case', 'dfs')]
+ parsed_args = self.check_parser(result_get, args, verifies)
+ result_get.take_action(parsed_args)
+ self.get_mock.assert_called_once_with(
+ self.base_url + '?case=dfs',
+ headers=clientmanager.ClientManager.headers)
+
+ def test_get_all(self):
+ self.get_mock.return_value = fakes.FakeResponse(data=self.results_rsp)
+ result_get = results.ResultGet(self.app, mock.Mock())
+ args = []
+ verifies = []
+ parsed_args = self.check_parser(result_get, args, verifies)
+ result_get.take_action(parsed_args)
+ self.get_mock.assert_called_once_with(
+ self.base_url,
+ headers=clientmanager.ClientManager.headers)
+
+ def test_get_one(self):
+ self.get_mock.return_value = fakes.FakeResponse(data=self.result_json)
+ result_get_one = results.ResultGetOne(self.app, mock.Mock())
+ args = ['def']
+ verifies = [('result_id', 'def')]
+ parsed_args = self.check_parser(result_get_one, args, verifies)
+ result_get_one.take_action(parsed_args)
+ self.get_mock.assert_called_once_with(
+ self.base_url + '/def',
+ headers=clientmanager.ClientManager.headers)
+
+
+class ResultCreateTest(ResultTest):
+
+ def setUp(self):
+ super(ResultCreateTest, self).setUp()
+
+ def test_create_success(self):
+ succ_rsp = {
+ 'href': '{}/{}'.format(self.base_url,
+ self.result_json.get('project_name'))
+ }
+ self.post_mock.return_value = fakes.FakeResponse(data=succ_rsp)
+ result_create = results.ResultCreate(self.app, mock.Mock())
+ args = [self.result_string]
+ verifies = [('result', self.result_json)]
+ parsed_args = self.check_parser(result_create, args, verifies)
+ result_create.take_action(parsed_args)
+ self.post_mock.assert_called_once()
+
+ def test_create_failure(self):
+ with testtools.ExpectedException(Exception, 'Create failed: Error'):
+ self.post_mock.return_value = utils.FAKE_FAILURE
+ result_create = results.ResultCreate(self.app, mock.Mock())
+ args = [self.result_string]
+ verifies = [('result', self.result_json)]
+ parsed_args = self.check_parser(result_create, args, verifies)
+ result_create.take_action(parsed_args)
diff --git a/testapi/testapi-client/testapiclient/utils/clientmanager.py b/testapi/testapi-client/testapiclient/utils/clientmanager.py
index 8858819..cbfd723 100644
--- a/testapi/testapi-client/testapiclient/utils/clientmanager.py
+++ b/testapi/testapi-client/testapiclient/utils/clientmanager.py
@@ -10,7 +10,9 @@ LOG = logging.getLogger(__name__)
class ClientManager(object):
- headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
+ headers = {
+ 'Content-type': 'application/json',
+ 'Accept': 'text/plain'}
def __init__(self, cli_options=None):
self.cli_options = cli_options
@@ -49,6 +51,8 @@ class ClientManager(object):
headers=self.headers))
def post(self, url, data):
+ if 'results' in url or 'deployresults' in url:
+ self.headers['X-Auth-Token'] = os.environ.get('testapi_token')
return self._parse_response('Create',
self._request('post', url,
data=json.dumps(data),