diff options
205 files changed, 14683 insertions, 2328 deletions
@@ -25,3 +25,5 @@ nosetests.xml .tox *.retry job_output/ +.vscode +testapi/testapi-client/pip-selfcheck.json diff --git a/INFO.yaml b/INFO.yaml new file mode 100644 index 0000000..c283eca --- /dev/null +++ b/INFO.yaml @@ -0,0 +1,47 @@ +--- +subproject: 'Test Results Collecting and Analyzing (TestResults)' +project_category: 'Integration & Testing' +lifecycle_state: 'Incubation' +subproject_lead: &opnfv_testresults_ptl + name: 'SerenaFeng' + email: 'feng.xiaowei@zte.com.cn' + id: 'serenafeng' + company: 'ZTE' + timezone: 'China' +primary_contact: *opnfv_testresults_ptl +issue_tracking: + type: 'jira' + url: 'https://jira.opnfv.org/projects/RELENG' + key: 'RELENG' +mailing_list: + type: 'mailman2' + url: 'opnfv-tech-discuss@lists.opnfv.org' + tag: '[testresults]' +meetings: + - type: 'appearin' + url: 'https://appear.in/serenafeng' + repeats: 'weekly' + day: 'Thursday' + time: '13:30 UTC' +committers: + - <<: *opnfv_testresults_ptl + - name: 'Fatih Degirmenci' + company: 'Ericsson' + email: 'fatih.degirmenci@ericsson.com' + id: 'fdegir' + timezone: 'Europe/Stockholm' + - name: 'Morgan Richomme' + email: 'morgan.richomme@orange.com' + company: 'Orange' + id: 'mrichomme' + timezone: 'Europe/Paris' + - name: 'Jose Lausuch' + company: 'SUSE' + email: 'jalausuch@suse.com' + id: 'jose.lausuch' + timezone: 'Europe/Madrid' + - name: 'Jack Chan' + company: 'Huawei' + email: 'chenjiankun1@huawei.com' + id: 'chenjiankun' + timezone: 'China/Shanghai' diff --git a/ci/htmlize/htmlize.py b/ci/htmlize/htmlize.py index da6a6cf..135d401 100644 --- a/ci/htmlize/htmlize.py +++ b/ci/htmlize/htmlize.py @@ -15,9 +15,9 @@ def main(args): # Merging two specs api_response = requests.get(args.api_declaration_url) - api_response = json.loads(api_response.content) + api_response = api_response.json() resource_response = requests.get(args.resource_listing_url) - resource_response = json.loads(resource_response.content) + resource_response = resource_response.json() resource_response['models'] = api_response['models'] resource_response['apis'] = api_response['apis'] @@ -41,7 +41,7 @@ if __name__ == '__main__': type=str, required=False, default=('http://testresults.opnfv.org' - '/test/swagger/resources.json'), + '/test/swagger/models.json'), help='Resource Listing Spec File') parser.add_argument('-au', '--api-declaration-url', type=str, diff --git a/ci/htmlize/push-doc-artifact.sh b/ci/htmlize/push-doc-artifacts.sh index e6432d3..e6432d3 100644 --- a/ci/htmlize/push-doc-artifact.sh +++ b/ci/htmlize/push-doc-artifacts.sh diff --git a/ci/mongodb_backup.py b/ci/mongodb_backup.py new file mode 100644 index 0000000..263232a --- /dev/null +++ b/ci/mongodb_backup.py @@ -0,0 +1,82 @@ +############################################################################## +# 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 argparse +import os +import subprocess +import urlparse + +import datetime + + +def get_abspath(path): + assert os.path.isdir(path), 'Path %s can\'t be found.' % path + return os.path.abspath(path) + + +def parse_mongodb_url(url): + url = urlparse.urlparse(url) + assert url.scheme == 'mongodb', 'URL must be a MongoDB URL' + return url + + +def url_parse(url): + url = parse_mongodb_url(url) + return url.username, url.password, url.hostname, url.port + + +def execute(cmd, args): + execute_output = subprocess.check_output(cmd) + print(execute_output) + + +def do_backup(args): + db = args.db + out = get_abspath(args.output_dir) + now = datetime.datetime.now() + out = os.path.join(out, '%s__%s' % (db, now.strftime('%Y_%m_%d_%H%M%S'))) + cmd = ['mongodump', '-o', '%s' % out] + if db: + cmd.extend(['--db', '%s' % db]) + + (username, password, hostname, port) = url_parse(args.url) + cmd.extend(['--host', '%s' % hostname, '--port', '%s' % port]) + + if username: + cmd.extend(['-u', '%s' % username]) + if password: + cmd.extend(['-p', '%s' % password]) + print('execute: %s' % cmd) + execute(cmd, args) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Backup MongoDBs') + + parser.add_argument('-u', '--url', + type=str, + required=False, + default='mongodb://127.0.0.1:27017/', + help='Mongo DB URL for Backups') + parser.add_argument('-o', '--output_dir', + type=str, + required=False, + default='./', + help='Output directory for the backup.') + + parser.add_argument('-d', '--db', + type=str, + required=False, + default='test_results_collection', + help='database for the backup.') + + args = parser.parse_args() + try: + do_backup(args) + except AssertionError as msg: + print(msg) diff --git a/ci/testapi-backup-mongodb.sh b/ci/testapi-backup-mongodb.sh index e6465ab..9bb5fd9 100644 --- a/ci/testapi-backup-mongodb.sh +++ b/ci/testapi-backup-mongodb.sh @@ -3,7 +3,7 @@ set -e # Run MongoDB backup -python $WORKSPACE/testapi/update/templates/backup_mongodb.py -o $WORKSPACE/ +python $WORKSPACE/ci/mongodb_backup.py -o $WORKSPACE/ # Compressing the dump now=$(date +"%m_%d_%Y_%H_%M_%S") diff --git a/reporting/html/colorado.html b/reporting/3rd_party/html/colorado.html index 58cb009..7a7326e 100644 --- a/reporting/html/colorado.html +++ b/reporting/3rd_party/html/colorado.html @@ -58,7 +58,7 @@ <span class="image">
<img src="img/projectIcon_functest_250x250.png" alt="" />
</span>
- <a href="functest-colorado.html">
+ <a href="colorado/functest/functest.html">
<h2>Functest</h2>
<div class="content">
<p>Functional testing</p>
diff --git a/reporting/html/danube.html b/reporting/3rd_party/html/danube.html index 09de789..ae8a3ac 100644 --- a/reporting/html/danube.html +++ b/reporting/3rd_party/html/danube.html @@ -58,7 +58,7 @@ <span class="image"> <img src="img/projectIcon_functest_250x250.png" alt="" /> </span> - <a href="functest-danube.html"> + <a href="danube/functest/functest.html"> <h2>Functest</h2> <div class="content"> <p>Functional testing</p> @@ -91,7 +91,7 @@ <span class="image"> <img src="img/projectIcon_vsperf_250x250.png" alt="" /> </span> - <a href="danube/vsperf/status-apex.html"> + <a href="danube/vsperf/reporting.html"> <h2>Vsperf</h2> <div class="content"> <p>Virtual switch testing</p> diff --git a/reporting/html/elements.html b/reporting/3rd_party/html/elements.html index 7b9bb4d..7b9bb4d 100644 --- a/reporting/html/elements.html +++ b/reporting/3rd_party/html/elements.html diff --git a/reporting/html/euphrates.html b/reporting/3rd_party/html/euphrates.html index ff7061b..e357edc 100644 --- a/reporting/html/euphrates.html +++ b/reporting/3rd_party/html/euphrates.html @@ -42,7 +42,7 @@ <span class="image"> <img src="img/projectIcon_functest_250x250.png" alt="" /> </span> - <a href="functest-euphrates.html"> + <a href="euphrates/functest/functest.html"> <h2>Functest</h2> <div class="content"> <p>Functional testing</p> @@ -75,7 +75,7 @@ <span class="image"> <img src="img/projectIcon_vsperf_250x250.png" alt="" /> </span> - <a href="euphrates/vsperf/status-apex.html"> + <a href="euphrates/vsperf/reporting.html"> <h2>Vsperf</h2> <div class="content"> <p>Virtual switch testing</p> diff --git a/reporting/html/functest-colorado.html b/reporting/3rd_party/html/fraser.html index 2fc76d1..65b996c 100644 --- a/reporting/html/functest-colorado.html +++ b/reporting/3rd_party/html/fraser.html @@ -1,124 +1,141 @@ -<!DOCTYPE HTML>
-<!--
- Phantom by HTML5 UP
- html5up.net | @ajlkn
- Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
--->
-<html>
- <head>
- <title>Phantom by HTML5 UP</title>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <!--[if lte IE 8]><script src="3rd_party/js/ie/html5shiv.js"></script><![endif]-->
- <link rel="stylesheet" href="3rd_party/css/main.css" />
- <!--[if lte IE 9]><link rel="stylesheet" href="3rd_party/css/ie9.css" /><![endif]-->
- <!--[if lte IE 8]><link rel="stylesheet" href="3rd_party/css/ie8.css" /><![endif]-->
- </head>
- <body>
- <!-- Wrapper -->
- <div id="wrapper">
-
- <!-- Header -->
- <header id="header">
- <div class="inner">
-
- <!-- Logo -->
- <a href="index.html" class="logo">
- <span class="symbol"><img src="img/logo.svg" alt="" /></span><span class="title">Phantom</span>
- </a>
-
- <!-- Nav -->
- <!-- <nav>
- <ul>
- <li><a href="#menu">Menu</a></li>
- </ul>
- </nav>
- --->
- </div>
- </header>
-
- <!-- Menu -->
- <!--- <nav id="menu">
- <h2>Menu</h2>
- <ul>
- <li><a href="index.html">Home</a></li>
- <li><a href="colorado.html">Colorado</a></li>
- <li><a href="danube.html">Danube</a></li>
- </ul>
- </nav>
- --->
- <!-- Main -->
- <div id="main">
- <div class="inner">
- <header>
- <h1>Functest reporting</h1>
- </header>
- <section class="tiles">
- <article class="style5">
- <span class="image">
- <img src="img/pic05.jpg" alt="" />
- </span>
- <a href="colorado/functest/status-apex.html">
- <h2>Status</h2>
- <div class="content">
- <p>Scenario status</p>
- </div>
- </a>
- </article>
- <article class="style2">
- <span class="image">
- <img src="img/pic02.jpg" alt="" />
- </span>
- <a href="colorado/functest/vims-apex.html">
- <h2>vIMS</h2>
- <div class="content">
- <p>Virtual IMS</p>
- </div>
- </a>
- </article>
- <article class="style3">
- <span class="image">
- <img src="img/pic03.jpg" alt="" />
- </span>
- <a href="colorado/functest/tempest-apex.html">
- <h2>Tempest</h2>
- <div class="content">
- <p>Tempest OpenStack suite</p>
- </div>
- </a>
- </article>
- </section>
- </div>
- </div>
-
- <!-- Footer -->
- <footer id="footer">
- <div class="inner">
- <section>
- <h2>OPNFV Testing Working group</h2>
- </section>
- <section>
- <h2>Follow</h2>
- <ul class="icons">
- <li><a href="https://twitter.com/opnfv" class="icon style2 fa-twitter"><span class="label">Twitter</span></a></li>
- <li><a href="http://git.opnfv.org" class="icon style2 fa-github"><span class="label">GitHub</span></a></li>
- <li><a href="mailto:test-wg@list.opnfv.org" class="icon style2 fa-envelope-o"><span class="label">Email</span></a></li>
- </ul>
- </section>
- <ul class="copyright">
- <li>© Untitled. All rights reserved</li><li>Design: <a href="http://html5up.net">HTML5 UP</a></li>
- </ul>
- </div>
- </footer>
-
- </div>
-
- <!-- Scripts -->
- <script src="3rd_party/js/jquery.min.js"></script>
- <script src="3rd_party/js/skel.min.js"></script>
- <script src="3rd_party/js/util.js"></script>
- <!--[if lte IE 8]><script src="3rd_party/js/ie/respond.min.js"></script><![endif]-->
- <script src="3rd_party/js/main.js"></script>
-
- </body>
-</html>
+<!DOCTYPE HTML> +<!-- + Phantom by HTML5 UP + html5up.net | @ajlkn + Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) +--> +<html> + <head> + <title>Phantom by HTML5 UP</title> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <!--[if lte IE 8]><script src="3rd_party/js/ie/html5shiv.js"></script><![endif]--> + <link rel="stylesheet" href="3rd_party/css/main.css" /> + <!--[if lte IE 9]><link rel="stylesheet" href="3rd_party/css/ie9.css" /><![endif]--> + <!--[if lte IE 8]><link rel="stylesheet" href="3rd_party/css/ie8.css" /><![endif]--> + </head> + <body> + <!-- Wrapper --> + <div id="wrapper"> + + <!-- Header --> + <header id="header"> + <div class="inner"> + + <!-- Logo --> + <a href="index.html" class="logo"> + <span class="symbol"><img src="img/logo.svg" alt="" /></span><span class="title">Phantom</span> + </a> + + </div> + </header> + + <!-- Menu --> + <!-- Main --> + <div id="main"> + <div class="inner"> + <header> + <h1>Fraser reporting</h1> + </header> + <section class="tiles"> + <article class="style3"> + <span class="image"> + <img src="img/projectIcon_functest_250x250.png" alt="" /> + </span> + <a href="fraser/functest/functest.html"> + <h2>Functest</h2> + <div class="content"> + <p>Functional testing</p> + </div> + </a> + </article> + <article class="style2"> + <span class="image"> + <img src="img/projectIcon_yardstick_250x250.png" alt="" /> + </span> + <a href="fraser/yardstick/status-apex.html"> + <h2>Yardstick</h2> + <div class="content"> + <p>Qualification and performance testing</p> + </div> + </a> + </article> + <article class="style4"> + <span class="image"> + <img src="img/projectIcon_storperf_250x250.png" alt="" /> + </span> + <a href="fraser/storperf/status-apex.html"> + <h2>Storperf</h2> + <div class="content"> + <p>Storage testing</p> + </div> + </a> + </article> + <article class="style5"> + <span class="image"> + <img src="img/projectIcon_vsperf_250x250.png" alt="" /> + </span> + <a href="fraser/vsperf/reporting.html"> + <h2>Vsperf</h2> + <div class="content"> + <p>Virtual switch testing</p> + </div> + </a> + </article> + <article class="style1"> + <span class="image"> + <img src="img/projectIcon_qtip_250x250.png" alt="" /> + </span> + <a href="fraser/qtip/status-apex.html"> + <h2>Qtip</h2> + <div class="content"> + <p>Benchmark as a service</p> + </div> + </a> + </article> + <article class="style6"> + <span class="image"> + <img src="img/projectIcon_bottlenecks_250x250.png" alt="" /> + </span> + <a href="fraser/bottlenecks/status-apex.html"> + <h2>Bottlenecks</h2> + <div class="content"> + <p>Bottleneck finder</p> + </div> + </a> + </article> + </section> + </div> + </div> + + <!-- Footer --> + <footer id="footer"> + <div class="inner"> + <section> + <h2>OPNFV Testing Working group</h2> + </section> + <section> + <h2>Follow</h2> + <ul class="icons"> + <li><a href="https://twitter.com/opnfv" class="icon style2 fa-twitter"><span class="label">Twitter</span></a></li> + <li><a href="http://git.opnfv.org" class="icon style2 fa-github"><span class="label">GitHub</span></a></li> + <li><a href="mailto:test-wg@list.opnfv.org" class="icon style2 fa-envelope-o"><span class="label">Email</span></a></li> + </ul> + </section> + <ul class="copyright"> + <li>© Untitled. All rights reserved</li><li>Design: <a href="http://html5up.net">HTML5 UP</a></li> + </ul> + </div> + </footer> + + </div> + + <!-- Scripts --> + <script src="3rd_party/js/jquery.min.js"></script> + <script src="3rd_party/js/skel.min.js"></script> + <script src="3rd_party/js/util.js"></script> + <!--[if lte IE 8]><script src="3rd_party/js/ie/respond.min.js"></script><![endif]--> + <script src="3rd_party/js/main.js"></script> + + </body> +</html> diff --git a/reporting/html/functest-euphrates.html b/reporting/3rd_party/html/functest.html index c203e61..d5f78c5 100644 --- a/reporting/html/functest-euphrates.html +++ b/reporting/3rd_party/html/functest.html @@ -9,10 +9,7 @@ <title>Phantom by HTML5 UP</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> - <!--[if lte IE 8]><script src="3rd_party/js/ie/html5shiv.js"></script><![endif]--> - <link rel="stylesheet" href="3rd_party/css/main.css" /> - <!--[if lte IE 9]><link rel="stylesheet" href="3rd_party/css/ie9.css" /><![endif]--> - <!--[if lte IE 8]><link rel="stylesheet" href="3rd_party/css/ie8.css" /><![endif]--> + <link rel="stylesheet" href="../../3rd_party/css/main.css" /> </head> <body> <!-- Wrapper --> @@ -24,7 +21,9 @@ <!-- Logo --> <a href="index.html" class="logo"> - <span class="symbol"><img src="img/logo.svg" alt="" /></span><span class="title">Phantom</span> + <span class="symbol"><img src="../../img/logo.svg" alt="" /> + </span> + <span class="title">Functest</span> </a> </div> @@ -41,9 +40,9 @@ <section class="tiles"> <article class="style5"> <span class="image"> - <img src="img/pic05.jpg" alt="" /> + <img src="../../img/pic05.jpg" alt="" /> </span> - <a href="euphrates/functest/status-apex.html"> + <a href="status-apex.html"> <h2>Status</h2> <div class="content"> <p>Scenario status</p> @@ -52,9 +51,9 @@ </article> <article class="style2"> <span class="image"> - <img src="img/pic02.jpg" alt="" /> + <img src="../../img/pic02.jpg" alt="" /> </span> - <a href="euphrates/functest/vims-apex.html"> + <a href="vims-apex.html"> <h2>vIMS</h2> <div class="content"> <p>Virtual IMS</p> @@ -63,9 +62,9 @@ </article> <article class="style3"> <span class="image"> - <img src="img/pic03.jpg" alt="" /> + <img src="../../img/pic03.jpg" alt="" /> </span> - <a href="euphrates/functest/tempest-apex.html"> + <a href="tempest-apex.html"> <h2>Tempest</h2> <div class="content"> <p>Tempest OpenStack suite</p> @@ -99,11 +98,11 @@ </div> <!-- Scripts --> - <script src="3rd_party/js/jquery.min.js"></script> - <script src="3rd_party/js/skel.min.js"></script> - <script src="3rd_party/js/util.js"></script> + <script src="../../3rd_party/js/jquery.min.js"></script> + <script src="../../3rd_party/js/skel.min.js"></script> + <script src="../../3rd_party/js/util.js"></script> <!--[if lte IE 8]><script src="3rd_party/js/ie/respond.min.js"></script><![endif]--> - <script src="3rd_party/js/main.js"></script> + <script src="../../3rd_party/js/main.js"></script> </body> </html> diff --git a/reporting/html/functest-danube.html b/reporting/3rd_party/html/gambia.html index ac99cb0..d5f6fcd 100644 --- a/reporting/html/functest-danube.html +++ b/reporting/3rd_party/html/gambia.html @@ -27,63 +27,80 @@ <span class="symbol"><img src="img/logo.svg" alt="" /></span><span class="title">Phantom</span> </a> - <!-- Nav --> - <!-- <nav> - <ul> - <li><a href="#menu">Menu</a></li> - </ul> - </nav> - ---> </div> </header> <!-- Menu --> - <!--- <nav id="menu"> - <h2>Menu</h2> - <ul> - <li><a href="index.html">Home</a></li> - <li><a href="colorado.html">Colorado</a></li> - <li><a href="danube.html">Danube</a></li> - </ul> - </nav> - ---> <!-- Main --> <div id="main"> <div class="inner"> <header> - <h1>Functest reporting</h1> + <h1>Gambia reporting</h1> </header> <section class="tiles"> - <article class="style5"> + <article class="style3"> <span class="image"> - <img src="img/pic05.jpg" alt="" /> + <img src="img/projectIcon_functest_250x250.png" alt="" /> </span> - <a href="danube/functest/status-apex.html"> - <h2>Status</h2> + <a href="gambia/functest/functest.html"> + <h2>Functest</h2> <div class="content"> - <p>Scenario status</p> + <p>Functional testing</p> </div> </a> </article> <article class="style2"> <span class="image"> - <img src="img/pic02.jpg" alt="" /> + <img src="img/projectIcon_yardstick_250x250.png" alt="" /> </span> - <a href="danube/functest/vims-apex.html"> - <h2>vIMS</h2> + <a href="gambia/yardstick/status-apex.html"> + <h2>Yardstick</h2> <div class="content"> - <p>Virtual IMS</p> + <p>Qualification and performance testing</p> </div> </a> </article> - <article class="style3"> + <article class="style4"> + <span class="image"> + <img src="img/projectIcon_storperf_250x250.png" alt="" /> + </span> + <a href="gambia/storperf/status-apex.html"> + <h2>Storperf</h2> + <div class="content"> + <p>Storage testing</p> + </div> + </a> + </article> + <article class="style5"> + <span class="image"> + <img src="img/projectIcon_vsperf_250x250.png" alt="" /> + </span> + <a href="gambia/vsperf/reporting.html"> + <h2>Vsperf</h2> + <div class="content"> + <p>Virtual switch testing</p> + </div> + </a> + </article> + <article class="style1"> + <span class="image"> + <img src="img/projectIcon_qtip_250x250.png" alt="" /> + </span> + <a href="gambia/qtip/status-apex.html"> + <h2>Qtip</h2> + <div class="content"> + <p>Benchmark as a service</p> + </div> + </a> + </article> + <article class="style6"> <span class="image"> - <img src="img/pic03.jpg" alt="" /> + <img src="img/projectIcon_bottlenecks_250x250.png" alt="" /> </span> - <a href="danube/functest/tempest-apex.html"> - <h2>Tempest</h2> + <a href="gambia/bottlenecks/status-apex.html"> + <h2>Bottlenecks</h2> <div class="content"> - <p>Tempest OpenStack suite</p> + <p>Bottleneck finder</p> </div> </a> </article> diff --git a/reporting/html/generic.html b/reporting/3rd_party/html/generic.html index 39273da..39273da 100644 --- a/reporting/html/generic.html +++ b/reporting/3rd_party/html/generic.html diff --git a/reporting/html/index.html b/reporting/3rd_party/html/index.html index 2789045..9165250 100644 --- a/reporting/html/index.html +++ b/reporting/3rd_party/html/index.html @@ -43,40 +43,29 @@ <h1>OPNFV Testing group reporting</h1> </header> <section class="tiles"> - <article class="style3"> + <article class="style4"> <span class="image"> - <img src="img/colorado.jpg" alt="" /> + <img src="img/fraser.jpg" alt="" /> </span> - <a href="colorado.html"> - <h2>Colorado</h2> + <a href="fraser.html"> + <h2>Fraser</h2> <div class="content"> - <p>Colorado 1.0 (22/09/2016)</p> + <p>Fraser (ETA 04/2018)</p> </div> </a> </article> - <article class="style2"> - <span class="image"> - <img src="img/danube.jpg" alt="" /> - </span> - <a href="danube.html"> - <h2>Danube</h2> - <div class="content"> - <p>Danube 1.0 (22/03/2017)</p> - </div> - </a> - </article> - <article class="style4"> + <article class="style6"> <span class="image"> - <img src="img/euphrates.jpg" alt="" /> + <img src="img/gambia.jpg" alt="" /> </span> - <a href="euphrates.html"> - <h2>Euphrates</h2> + <a href="gambia.html"> + <h2>Gambia</h2> <div class="content"> - <p>Euphreates (ETA 10/2017)</p> + <p>Gambia (ETA 11/2018)</p> </div> </a> </article> - <article class="style6"> + <article class="style5"> <span class="image"> <img src="img/misc-npc-letterblock-m-800px.png" alt="" /> </span> diff --git a/reporting/html/master.html b/reporting/3rd_party/html/master.html index 7805386..5d4f916 100644 --- a/reporting/html/master.html +++ b/reporting/3rd_party/html/master.html @@ -58,7 +58,7 @@ <span class="image"> <img src="img/projectIcon_functest_250x250.png" alt="" /> </span> - <a href="functest-master.html"> + <a href="master/functest/functest.html"> <h2>Functest</h2> <div class="content"> <p>Functional testing</p> @@ -91,7 +91,7 @@ <span class="image"> <img src="img/projectIcon_vsperf_250x250.png" alt="" /> </span> - <a href="master/vsperf/status-apex.html"> + <a href="master/vsperf/reporting.html"> <h2>Vsperf</h2> <div class="content"> <p>Virtual switch testing</p> diff --git a/reporting/api/conf.py b/reporting/api/conf.py index 5897d4f..d0023e0 100644 --- a/reporting/api/conf.py +++ b/reporting/api/conf.py @@ -1 +1,5 @@ base_url = 'http://testresults.opnfv.org/test/api/v1' + +versions = ['master', 'fraser', 'gambia'] + +period = 10 diff --git a/reporting/api/extension/__init__.py b/reporting/api/extension/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/reporting/api/extension/__init__.py diff --git a/reporting/api/extension/client.py b/reporting/api/extension/client.py new file mode 100644 index 0000000..03371fd --- /dev/null +++ b/reporting/api/extension/client.py @@ -0,0 +1,15 @@ +from tornado.simple_httpclient import SimpleAsyncHTTPClient +from tornado.log import gen_log + + +class NoQueueTimeoutHTTPClient(SimpleAsyncHTTPClient): + def fetch_impl(self, request, callback): + key = object() + + self.queue.append((key, request, callback)) + self.waiting[key] = (request, callback, None) + + self._process_queue() + + if self.queue: + gen_log.debug("max_clients limit reached, request queued.") diff --git a/reporting/api/handlers/scenarios.py b/reporting/api/handlers/scenarios.py new file mode 100644 index 0000000..70447c7 --- /dev/null +++ b/reporting/api/handlers/scenarios.py @@ -0,0 +1,27 @@ +from tornado.web import asynchronous +from tornado.gen import coroutine +from tornado.escape import json_encode + +from api.handlers import BaseHandler +from api.service.scenario import ScenarioTableResult + + +class Result(BaseHandler): + @asynchronous + @coroutine + def get(self): + self._set_header() + + scenario = self.get_argument('scenario', None) + version = self.get_argument('version', 'master') + installer = self.get_argument('installer', None) + iteration = int(self.get_argument('iteration', 10)) + + yield self._get_scenario_data(scenario, version, installer, iteration) + + @coroutine + def _get_scenario_data(self, scenario, version, installer, iteration): + results = ScenarioTableResult(scenario, version, installer, iteration) + result = yield results.get() + self.write(json_encode(result)) + self.finish() diff --git a/reporting/api/server.py b/reporting/api/server.py index e340b01..461e6d5 100644 --- a/reporting/api/server.py +++ b/reporting/api/server.py @@ -12,6 +12,7 @@ from tornado.options import define from tornado.options import options from api.urls import mappings +from api.service.result import ResultCache define("port", default=8000, help="run on the given port", type=int) @@ -20,7 +21,12 @@ def main(): tornado.options.parse_command_line() application = tornado.web.Application(mappings) application.listen(options.port) - tornado.ioloop.IOLoop.current().start() + + tornado.ioloop.PeriodicCallback(ResultCache.update, 1800000).start() + + ioloop = tornado.ioloop.IOLoop.current() + ioloop.call_later(1000, ResultCache.update()) + ioloop.start() if __name__ == "__main__": diff --git a/reporting/api/service/__init__.py b/reporting/api/service/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/reporting/api/service/__init__.py diff --git a/reporting/api/service/result.py b/reporting/api/service/result.py new file mode 100644 index 0000000..fa6553c --- /dev/null +++ b/reporting/api/service/result.py @@ -0,0 +1,90 @@ +import json +import logging +from collections import defaultdict + +from tornado.gen import coroutine +from tornado.gen import Return +from tornado.httpclient import AsyncHTTPClient + +from api import conf as consts +from api.extension.client import NoQueueTimeoutHTTPClient + +LOG = logging.getLogger(__name__) +AsyncHTTPClient.configure(NoQueueTimeoutHTTPClient) + + +class Result(object): + + def __init__(self): + self._url = '{}/results?period={}&version={}&page={}' + self._client = AsyncHTTPClient() + self._result = defaultdict(list) + + @property + @coroutine + def result(self): + if not self._result: + yield self.update_results() + raise Return(self._result) + + @coroutine + def update_results(self): + LOG.info('start update results') + + for version in consts.versions: + yield self._update_version_result(version) + + LOG.info('results update finished') + + @coroutine + def _update_version_result(self, version): + total_page = yield self._get_total_page(version) + + responses = yield self._fetch_results(version, total_page) + + self._update_version_dict(version, responses) + + @coroutine + def _fetch_results(self, version, total_page): + urls = [self._url.format(consts.base_url, consts.period, version, i) + for i in range(1, total_page + 1)] + responses = yield [self._client.fetch(url) for url in urls] + raise Return(responses) + + @coroutine + def _get_total_page(self, version): + url = self._url.format(consts.base_url, consts.period, version, 1) + response = yield self._client.fetch(url) + raise Return(json.loads(response.body)['pagination']['total_pages']) + + def _update_version_dict(self, version, responses): + for response in responses: + results = json.loads(response.body)['results'] + for result in results: + data = {k: v for k, v in result.items() if k != 'details'} + self._result[version].append(data) + + +class ResultCache(Result): + + @classmethod + @coroutine + def update(cls): + cls._check() + + yield cls.cache.update_results() + + @classmethod + @coroutine + def get(cls, version): + cls._check() + + result = yield cls.cache.result + raise Return(result[version]) + + @classmethod + def _check(cls): + try: + cls.cache + except AttributeError: + cls.cache = cls() diff --git a/reporting/api/service/scenario.py b/reporting/api/service/scenario.py new file mode 100644 index 0000000..f9e2a91 --- /dev/null +++ b/reporting/api/service/scenario.py @@ -0,0 +1,133 @@ +import abc +from collections import defaultdict + +import six +from tornado.gen import coroutine +from tornado.gen import Return + +from api.service.result import ResultCache + +PROJECTS = ['fastdatastacks', 'barometer', 'sfc', 'sdnvpn', 'doctor', 'parser'] + + +def _set(key, llist): + return set(x[key] for x in llist) + + +def _filter(key, value, llist): + return filter(lambda x: x[key] == value, llist) + + +class ScenarioTableResult(object): + + def __init__(self, scenario, version, installer, iteration): + self.scenario = scenario + self.version = version + self.installer = installer + self.iteration = iteration + + @coroutine + def get(self): + results = yield ResultCache.get(self.version) + results = self._filter_result(results) + results = self._struct_result(results) + + raise Return(results) + + def _filter_result(self, results): + results = [x for x in results if x['build_tag']] + if self.installer: + results = _filter('installer', self.installer, results) + if self.scenario: + results = _filter('scenario', self.scenario, results) + return results + + def _struct_result(self, results): + + return { + s: self._struct_scenario(_filter('scenario', s, results)) + for s in _set('scenario', results) + } + + def _struct_scenario(self, data): + return sorted([ + HandlerFacade.get_result(b, _filter('build_tag', b, data)) + for b in _set('build_tag', data) + ], key=lambda x: x['date'], reverse=True)[:self.iteration] + + +class HandlerFacade(object): + + @classmethod + def get_result(cls, index, data): + if not data: + return {} + + cls._change_name_to_functest(data) + data = cls._sort_by_start_date(data) + + return { + 'id': index, + 'date': data[0]['start_date'], + 'version': data[0]['version'], + 'installer': data[0]['installer'], + 'projects': cls._get_projects_data(data) + } + + @classmethod + def _sort_by_start_date(cls, data): + return sorted(data, key=lambda x: x['start_date']) + + @classmethod + def _get_projects_data(cls, data): + + return { + p: HANDLER_MAP[p](_filter('project_name', p, data)).struct() + for p in _set('project_name', data) + } + + @classmethod + def _change_name_to_functest(cls, data): + for ele in data: + if ele['project_name'] in PROJECTS: + ele['project_name'] = 'functest' + + +@six.add_metaclass(abc.ABCMeta) +class ProjectHandler(object): + + def __init__(self, data): + self.data = data + + def struct(self): + return self._struct_project_data() + + def _struct_project_data(self): + return { + 'gating': self._gating() + } + + @abc.abstractmethod + def _gating(self): + pass + + +class DefaultHandler(ProjectHandler): + + def _struct_project_data(self): + return {} + + def _gating(self): + pass + + +class FunctestHandler(ProjectHandler): + + def _gating(self): + if all([x['criteria'] == 'PASS' for x in self.data]): + return 'PASS' + else: + return 'FAIL' + + +HANDLER_MAP = defaultdict(lambda: DefaultHandler, functest=FunctestHandler) diff --git a/reporting/api/urls.py b/reporting/api/urls.py index a5228b2..34f61d8 100644 --- a/reporting/api/urls.py +++ b/reporting/api/urls.py @@ -9,6 +9,7 @@ from api.handlers import landing from api.handlers import projects from api.handlers import testcases +from api.handlers import scenarios mappings = [ (r"/landing-page/filters", landing.FiltersHandler), @@ -16,5 +17,7 @@ mappings = [ (r"/projects-page/projects", projects.Projects), (r"/projects/([^/]+)/cases", testcases.TestCases), - (r"/projects/([^/]+)/cases/([^/]+)", testcases.TestCase) + (r"/projects/([^/]+)/cases/([^/]+)", testcases.TestCase), + + (r"/scenarios/results", scenarios.Result) ] diff --git a/reporting/css/default.css b/reporting/css/default.css index e32fa5f..686e886 100644 --- a/reporting/css/default.css +++ b/reporting/css/default.css @@ -26,6 +26,7 @@ width: 100%; background-color: #0095a2 } + .panel-default > .panel-heading h4 { color: white; } @@ -55,7 +56,7 @@ td{ background-color: #0095a2; } -h1 { +h1 { display: block; font-size: 2em; margin-top: 0.67em; @@ -191,4 +192,3 @@ h2 { position:relative; top:1px; } - diff --git a/reporting/docker/reporting.sh b/reporting/docker/reporting.sh index 2cb438d..f3f3d50 100755 --- a/reporting/docker/reporting.sh +++ b/reporting/docker/reporting.sh @@ -3,7 +3,7 @@ export PYTHONPATH="${PYTHONPATH}:./reporting" export CONFIG_REPORTING_YAML=./reporting/reporting.yaml -declare -a versions=(euphrates danube master) +declare -a versions=(gambia fraser master) declare -a projects=(functest storperf yardstick qtip vsperf bottlenecks) project=$1 @@ -21,7 +21,7 @@ done # copy images, js, css, 3rd_party cp -Rf 3rd_party display cp -Rf css display -cp -Rf html/* display +cp -Rf 3rd_party/html/* display cp -Rf img display cp -Rf js display diff --git a/reporting/html/functest-master.html b/reporting/html/functest-master.html deleted file mode 100644 index 4b1f763..0000000 --- a/reporting/html/functest-master.html +++ /dev/null @@ -1,124 +0,0 @@ -<!DOCTYPE HTML> -<!-- - Phantom by HTML5 UP - html5up.net | @ajlkn - Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) ---> -<html> - <head> - <title>Phantom by HTML5 UP</title> - <meta charset="utf-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <!--[if lte IE 8]><script src="3rd_party/js/ie/html5shiv.js"></script><![endif]--> - <link rel="stylesheet" href="3rd_party/css/main.css" /> - <!--[if lte IE 9]><link rel="stylesheet" href="3rd_party/css/ie9.css" /><![endif]--> - <!--[if lte IE 8]><link rel="stylesheet" href="3rd_party/css/ie8.css" /><![endif]--> - </head> - <body> - <!-- Wrapper --> - <div id="wrapper"> - - <!-- Header --> - <header id="header"> - <div class="inner"> - - <!-- Logo --> - <a href="index.html" class="logo"> - <span class="symbol"><img src="img/logo.svg" alt="" /></span><span class="title">Phantom</span> - </a> - - <!-- Nav --> - <!-- <nav> - <ul> - <li><a href="#menu">Menu</a></li> - </ul> - </nav> - ---> - </div> - </header> - - <!-- Menu --> - <!--- <nav id="menu"> - <h2>Menu</h2> - <ul> - <li><a href="index.html">Home</a></li> - <li><a href="colorado.html">Colorado</a></li> - <li><a href="danube.html">Danube</a></li> - </ul> - </nav> - ---> - <!-- Main --> - <div id="main"> - <div class="inner"> - <header> - <h1>Functest reporting</h1> - </header> - <section class="tiles"> - <article class="style5"> - <span class="image"> - <img src="img/pic05.jpg" alt="" /> - </span> - <a href="master/functest/status-apex.html"> - <h2>Status</h2> - <div class="content"> - <p>Scenario status</p> - </div> - </a> - </article> - <article class="style2"> - <span class="image"> - <img src="img/pic02.jpg" alt="" /> - </span> - <a href="master/functest/vims-apex.html"> - <h2>vIMS</h2> - <div class="content"> - <p>Virtual IMS</p> - </div> - </a> - </article> - <article class="style3"> - <span class="image"> - <img src="img/pic03.jpg" alt="" /> - </span> - <a href="master/functest/tempest-apex.html"> - <h2>Tempest</h2> - <div class="content"> - <p>Tempest OpenStack suite</p> - </div> - </a> - </article> - </section> - </div> - </div> - - <!-- Footer --> - <footer id="footer"> - <div class="inner"> - <section> - <h2>OPNFV Testing Working group</h2> - </section> - <section> - <h2>Follow</h2> - <ul class="icons"> - <li><a href="https://twitter.com/opnfv" class="icon style2 fa-twitter"><span class="label">Twitter</span></a></li> - <li><a href="http://git.opnfv.org" class="icon style2 fa-github"><span class="label">GitHub</span></a></li> - <li><a href="mailto:test-wg@list.opnfv.org" class="icon style2 fa-envelope-o"><span class="label">Email</span></a></li> - </ul> - </section> - <ul class="copyright"> - <li>© Untitled. All rights reserved</li><li>Design: <a href="http://html5up.net">HTML5 UP</a></li> - </ul> - </div> - </footer> - - </div> - - <!-- Scripts --> - <script src="3rd_party/js/jquery.min.js"></script> - <script src="3rd_party/js/skel.min.js"></script> - <script src="3rd_party/js/util.js"></script> - <!--[if lte IE 8]><script src="3rd_party/js/ie/respond.min.js"></script><![endif]--> - <script src="3rd_party/js/main.js"></script> - - </body> -</html> diff --git a/reporting/img/fraser.jpg b/reporting/img/fraser.jpg Binary files differnew file mode 100644 index 0000000..8bc6b51 --- /dev/null +++ b/reporting/img/fraser.jpg diff --git a/reporting/img/gambia.jpg b/reporting/img/gambia.jpg Binary files differnew file mode 100644 index 0000000..fd91d11 --- /dev/null +++ b/reporting/img/gambia.jpg diff --git a/reporting/js/gauge.js b/reporting/js/gauge.js index 4cad16c..a9842e5 100644 --- a/reporting/js/gauge.js +++ b/reporting/js/gauge.js @@ -28,7 +28,9 @@ var gauge = function(container) { labelFormat : d3.format(',g'), labelInset : 10, - arcColorFn : d3.interpolateHsl(d3.rgb('#ff0000'), d3.rgb('#00ff00')) + arcColorFn : d3.scale.quantile() + .domain([0, , 0.6, 0.8, 1]) + .range([d3.rgb('#ff0000'),d3.rgb('#ffcc00'),d3.rgb('#00ff00')]) }; diff --git a/reporting/pages/app/index.html b/reporting/pages/app/index.html index 843a623..6ef1d02 100644 --- a/reporting/pages/app/index.html +++ b/reporting/pages/app/index.html @@ -87,6 +87,7 @@ <script src="scripts/controllers/auth.controller.js"></script> <script src="scripts/controllers/admin.controller.js"></script> <script src="scripts/controllers/main.controller.js"></script> + <script src="scripts/controllers/gating.controller.js"></script> <script src="scripts/controllers/testvisual.controller.js"></script> <!-- endbuild --> diff --git a/reporting/pages/app/scripts/config.router.js b/reporting/pages/app/scripts/config.router.js index d3724b7..45a6997 100644 --- a/reporting/pages/app/scripts/config.router.js +++ b/reporting/pages/app/scripts/config.router.js @@ -17,11 +17,11 @@ angular.module('opnfvApp') ]).config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { - $urlRouterProvider.otherwise('/landingpage/table'); + $urlRouterProvider.otherwise('/home/gating'); $stateProvider - .state('landingpage', { - url: "/landingpage", + .state('home', { + url: "/home", controller: 'MainController', templateUrl: "views/main.html", data: { pageTitle: '首页', specialClass: 'landing-page' }, @@ -33,7 +33,7 @@ angular.module('opnfvApp') }] } }) - .state('landingpage.table', { + .state('home.table', { url: "/table", controller: 'TableController', templateUrl: "views/commons/table.html", @@ -47,6 +47,19 @@ angular.module('opnfvApp') }] } }) + .state('home.gating', { + url: "/gating", + controller: 'GatingController', + templateUrl: "views/gating.html", + data: { pageTitle: 'OPNFV Release Gating Page', specialClass: 'landing-page' }, + resolve: { + controller: ['$ocLazyLoad', function($ocLazyLoad) { + return $ocLazyLoad.load([ + + ]) + }] + } + }) .state('select', { url: '/select', templateUrl: "views/testcase.html", diff --git a/reporting/pages/app/scripts/controllers/gating.controller.js b/reporting/pages/app/scripts/controllers/gating.controller.js new file mode 100644 index 0000000..52b381d --- /dev/null +++ b/reporting/pages/app/scripts/controllers/gating.controller.js @@ -0,0 +1,119 @@ +'use strict'; + +angular.module('opnfvApp') + .controller('GatingController', ['$scope', '$state', '$stateParams', 'TableFactory', function($scope, $state, $stateParams, TableFactory) { + + init(); + + function init() { + $scope.goTest = goTest; + $scope.goLogin = goLogin; + + $scope.scenarios = {}; + + $scope.scenarioList = _toSelectList(['all']); + $scope.versionList = _toSelectList(['master', 'fraser', 'gambia']); + $scope.installerList = _toSelectList(['all', 'apex', 'compass', 'daisy', 'fuel', 'joid']); + $scope.iterationList = _toSelectList([10, 20, 30]); + + $scope.selectScenario = 'all'; + $scope.selectVersion = 'master'; + $scope.selectInstaller = 'all'; + $scope.selectIteration = 10; + + $scope.ScenarioConfig = { + create: true, + valueField: 'title', + labelField: 'title', + delimiter: '|', + maxItems: 1, + placeholder: 'Scenario', + onChange: function(value) { + $scope.selectScenario = value; + } + } + + $scope.VersionConfig = { + create: true, + valueField: 'title', + labelField: 'title', + delimiter: '|', + maxItems: 1, + placeholder: 'Version', + onChange: function(value) { + $scope.selectVersion = value; + getScenarioResult(); + } + } + + $scope.InstallerConfig = { + create: true, + valueField: 'title', + labelField: 'title', + delimiter: '|', + maxItems: 1, + placeholder: 'Installer', + onChange: function(value) { + $scope.selectInstaller = value; + getScenarioResult(); + } + } + + $scope.IterationConfig = { + create: true, + valueField: 'title', + labelField: 'title', + delimiter: '|', + maxItems: 1, + placeholder: 'Iteration', + onChange: function(value) { + $scope.selectIteration = value; + getScenarioResult(); + } + } + getScenarioResult(); + } + + function getScenarioResult(){ + _getScenarioResult($scope.selectVersion, $scope.selectInstaller, $scope.selectIteration); + } + + function _getScenarioResult(version, installer, iteration){ + var data = { + 'version': version, + 'iteration': iteration + } + + if(installer != 'all'){ + data['installer'] = installer; + } + + TableFactory.getScenarioResult().get(data).$promise.then(function(resp){ + $scope.scenarios = resp; + _concat($scope.scenarioList, _toSelectList(Object.keys(resp))); + }, function(err){ + }); + } + + function _concat(aList, bList){ + angular.forEach(bList, function(ele){ + aList.push(ele); + }); + } + + function _toSelectList(arr){ + var tempList = []; + angular.forEach(arr, function(ele){ + tempList.push({'title': ele}); + }); + return tempList; + } + + function goTest() { + $state.go("select.selectTestCase"); + } + + function goLogin() { + $state.go("login"); + } + }]); diff --git a/reporting/pages/app/scripts/factory/table.factory.js b/reporting/pages/app/scripts/factory/table.factory.js index e715c5c..a2c5c76 100644 --- a/reporting/pages/app/scripts/factory/table.factory.js +++ b/reporting/pages/app/scripts/factory/table.factory.js @@ -6,7 +6,7 @@ angular.module('opnfvApp') .factory('TableFactory', function($resource, $rootScope, $http) { - var BASE_URL = 'http://testresults.opnfv.org/reporting2'; + var BASE_URL = ' http://testresults.opnfv.org/testing'; $.ajax({ url: 'config.json', async: false, @@ -15,11 +15,19 @@ angular.module('opnfvApp') BASE_URL = response.url; }, error: function (response){ - alert('fail to get api url, using default: http://testresults.opnfv.org/reporting2') + alert('fail to get api url, using default: http://testresults.opnfv.org/testing') } }); return { + getScenarioResult: function() { + return $resource(BASE_URL + '/scenarios/results', {'scenario': '@scenario', 'version': '@version', 'installer': '@installer', 'iteration': '@iteration'}, { + 'get': { + method: 'GET', + + } + }); + }, getFilter: function() { return $resource(BASE_URL + '/landing-page/filters', {}, { 'get': { diff --git a/reporting/pages/app/views/gating.html b/reporting/pages/app/views/gating.html new file mode 100644 index 0000000..0215c87 --- /dev/null +++ b/reporting/pages/app/views/gating.html @@ -0,0 +1,68 @@ +<section class="container-tablesize"> + <div class="row border-bottom white-bg dashboard-header"> + <div class="row"> + + <div class="ibox float-e-margins"> + <div class="ibox-title"> + <h5>OPNFV Release Gating Reporting </h5> + </div> + + <div class="ibox-content row"> + <div class="col-md-2" style="margin-top:5px;margin-right: 5px;"> + <selectize options="scenarioList" ng-model="selectScenario" config="ScenarioConfig"></selectize> + </div> + <div class="col-md-2" style="margin-top:5px;margin-right: 5px;"> + <selectize options="versionList" ng-model="selectVersion" config="VersionConfig"></selectize> + </div> + + <div class="col-md-2" style="margin-top:5px;margin-right: 5px;"> + <selectize options="installerList" ng-model="selectInstaller" config="InstallerConfig"></selectize> + + </div> + + <div class="col-md-2" style="margin-top:5px;margin-right: 5px;"> + <selectize options="iterationList" ng-model="selectIteration" config="IterationConfig"></selectize> + </div> + </div> + + <div class="table-responsive"> + <table class="table table-bordered" id="table"> + <thead class="thead"> + <tr> + <th>Scenario</th> + <th>Date</th> + <th>ID</th> + <th>Version</th> + <th>Installer</th> + <th>Deployment</th> + <th>Functest</th> + <th>Yardstick</th> + </tr> + </thead> + <tbody class="tbody" ng-show="selectScenario == 'all' || scenario == selectScenario" ng-repeat="(scenario,value) in scenarios"> + <tr ng-repeat="record in value"> + <td ng-if="$index == 0">{{ scenario }}</td> + <td ng-if="$index != 0"></td> + <td>{{ record.date }}</td> + <td>{{ record.id }}</td> + <td>{{ record.version }}</td> + <td>{{ record.installer }}</td> + <td>{{ record.projects.deployment.gating }}</td> + <td>{{ record.projects.functest.gating }}</td> + <td>{{ record.projects.yardstick.gating }}</td> + </tr> + </tbody> + </table> + + </div> + + </div> + </div> + </div> + +</section> +<style> +.selectize-input { + width: 200px !important; +} +</style> diff --git a/reporting/reporting/bottlenecks/reporting-status.py b/reporting/reporting/bottlenecks/reporting-status.py index 225227a..8966d06 100644 --- a/reporting/reporting/bottlenecks/reporting-status.py +++ b/reporting/reporting/bottlenecks/reporting-status.py @@ -37,14 +37,10 @@ for version in VERSIONS: # For all the installers for installer in INSTALLERS: # get scenarios results data - if version != 'master': - new_version = "stable/{}".format(version) - else: - new_version = version scenario_results = rp_utils.getScenarios("bottlenecks", "posca_factor_ping", installer, - new_version) + version) LOGGER.info("scenario_results: %s", scenario_results) scenario_stats = rp_utils.getScenarioStats(scenario_results) diff --git a/reporting/reporting/functest/reporting-status.py b/reporting/reporting/functest/reporting-status.py index 592f929..e36aede 100755 --- a/reporting/reporting/functest/reporting-status.py +++ b/reporting/reporting/functest/reporting-status.py @@ -25,8 +25,6 @@ Functest reporting status LOGGER = rp_utils.getLogger("Functest-Status") # Initialization -testValid = [] -otherTestCases = [] reportingDate = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") # init just connection_check to get the list of scenarios @@ -43,8 +41,7 @@ blacklist = rp_utils.get_config('functest.blacklist') log_level = rp_utils.get_config('general.log.log_level') exclude_noha = rp_utils.get_config('functest.exclude_noha') exclude_virtual = rp_utils.get_config('functest.exclude_virtual') - -functest_yaml_config = rp_utils.getFunctestConfig() +tiers_for_scoring = {'healthcheck', 'smoke', 'vnf', 'features'} LOGGER.info("*******************************************") LOGGER.info("* *") @@ -57,38 +54,36 @@ LOGGER.info("* NOHA scenarios excluded: %s *", exclude_noha) LOGGER.info("* *") LOGGER.info("*******************************************") -# Retrieve test cases of Tier 1 (smoke) -config_tiers = functest_yaml_config.get("tiers") +# For all the versions +for version in versions: + testValid = [] + # Retrieve test cases of Tier 1 (smoke) + version_config = "" + if (version != "master" and version != "latest"): + version_config = "?h=stable/" + version + functest_yaml_config = rp_utils.getFunctestConfig(version_config) + config_tiers = functest_yaml_config.get("tiers") + + # we consider Tier 0 (Healthcheck), Tier 1 (smoke),2 (features) + # to validate scenarios + # Tier > 2 are not used to validate scenarios but we display + # the results anyway + # tricky thing for the API as some tests are Functest tests + # other tests are declared directly in the feature projects + for tier in config_tiers: -# we consider Tier 0 (Healthcheck), Tier 1 (smoke),2 (features) -# to validate scenarios -# Tier > 2 are not used to validate scenarios but we display the results anyway -# tricky thing for the API as some tests are Functest tests -# other tests are declared directly in the feature projects -for tier in config_tiers: - if tier['order'] >= 0 and tier['order'] < 2: for case in tier['testcases']: + try: + dependencies = case['dependencies'] + except KeyError: + dependencies = "" if case['case_name'] not in blacklist: testValid.append(tc.TestCase(case['case_name'], "functest", - case['dependencies'])) - elif tier['order'] == 2: - for case in tier['testcases']: - if case['case_name'] not in blacklist: - otherTestCases.append(tc.TestCase(case['case_name'], - case['case_name'], - case['dependencies'])) - elif tier['order'] > 2: - for case in tier['testcases']: - if case['case_name'] not in blacklist: - otherTestCases.append(tc.TestCase(case['case_name'], - "functest", - case['dependencies'])) + dependencies, + tier=tier['name'])) + LOGGER.debug("Functest reporting start") -LOGGER.debug("Functest reporting start") - -# For all the versions -for version in versions: # For all the installers scenario_directory = "./display/" + version + "/functest/" scenario_file_name = scenario_directory + "scenario_history.txt" @@ -155,10 +150,8 @@ for version in versions: # Check if test case is runnable / installer, scenario # for the test case used for Scenario validation try: - # 1) Manage the test cases for the scenario validation - # concretely Tiers 0-3 for test_case in testValid: - test_case.checkRunnable(installer, s, + test_case.checkRunnable(installer, s, architecture, test_case.getConstraints()) LOGGER.debug("testcase %s (%s) is %s", test_case.getDisplayName(), @@ -169,7 +162,8 @@ for version in versions: name = test_case.getName() displayName = test_case.getDisplayName() project = test_case.getProject() - nb_test_runnable_for_this_scenario += 1 + if test_case.getTier() in tiers_for_scoring: + nb_test_runnable_for_this_scenario += 1 LOGGER.info(" Searching results for case %s ", displayName) if "fuel" in installer: @@ -185,53 +179,18 @@ for version in versions: LOGGER.info(" >>>> Test score = " + str(result)) test_case.setCriteria(result) test_case.setIsRunnable(True) - testCases2BeDisplayed.append(tc.TestCase(name, - project, - "", - result, - True, - 1)) - scenario_score = scenario_score + result - - # 2) Manage the test cases for the scenario qualification - # concretely Tiers > 3 - for test_case in otherTestCases: - test_case.checkRunnable(installer, s, - test_case.getConstraints()) - LOGGER.debug("testcase %s (%s) is %s", - test_case.getDisplayName(), - test_case.getName(), - test_case.isRunnable) - time.sleep(1) - if test_case.isRunnable: - name = test_case.getName() - displayName = test_case.getDisplayName() - project = test_case.getProject() - LOGGER.info(" Searching results for case %s ", - displayName) - if "fuel" in installer: - result = rp_utils.getCaseScoreFromBuildTag( - name, - s_result) - else: - result = rp_utils.getCaseScore(name, installer, - s, version) - # at least 1 result for the test - if result > -1: - test_case.setCriteria(result) - test_case.setIsRunnable(True) - testCases2BeDisplayed.append(tc.TestCase( - name, - project, - "", - result, - True, - 4)) - else: - LOGGER.debug("No results found") - - items[s] = testCases2BeDisplayed - except Exception: # pylint: disable=broad-except + testCases2BeDisplayed.append( + tc.TestCase(name, + project, + "", + result, + True, + tier=test_case.getTier())) + if test_case.getTier() in tiers_for_scoring: + scenario_score = scenario_score + result + + items[s] = testCases2BeDisplayed + except KeyError: # pylint: disable=broad-except LOGGER.error("Error installer %s, version %s, scenario %s", installer, version, s) LOGGER.error("No data available: %s", sys.exc_info()[0]) @@ -259,6 +218,12 @@ for version in versions: else: k_score = 2 + # TODO for the scoring we should consider 3 tiers + # - Healthcheck + # - Smoke + # - Vnf + # components + scenario_criteria = nb_test_runnable_for_this_scenario*k_score # score for reporting diff --git a/reporting/reporting/functest/reporting-tempest.py b/reporting/reporting/functest/reporting-tempest.py index d78d9a1..b82f96c 100755 --- a/reporting/reporting/functest/reporting-tempest.py +++ b/reporting/reporting/functest/reporting-tempest.py @@ -83,8 +83,16 @@ for version in rp_utils.get_config('general.versions'): # retrieve results # **************** - nb_tests_run = result['details']['tests'] - nb_tests_failed = result['details']['failures'] + try: + nb_tests_run = result['details']['success_number'] + nb_tests_failed = result['details']['failures_number'] + except KeyError: + try: + nb_tests_run = result['details']['tests'] + nb_tests_failed = result['details']['failures'] + except KeyError: + logger.error("Impossible to retrieve results") + logger.debug("nb_tests_run= %s", nb_tests_run) logger.debug("nb_tests_failed= %s", nb_tests_failed) @@ -140,12 +148,14 @@ for version in rp_utils.get_config('general.versions'): # Error management # **************** try: - errors = result['details']['errors'] - logger.info("errors: %s", errors) - result['errors'] = errors - except Exception: # pylint: disable=broad-except - logger.error("Error field not present (Brahamputra runs?)") - + errors = result['details']['failures'] + except KeyError: + try: + errors = result['details']['errors'] + except KeyError: + logger.error("Error field not present (old runs?)") + logger.info("errors: %s", errors) + result['errors'] = errors templateLoader = jinja2.FileSystemLoader(".") templateEnv = jinja2.Environment(loader=templateLoader, autoescape=True) diff --git a/reporting/reporting/functest/reporting-vims.py b/reporting/reporting/functest/reporting-vims.py index 3b25e91..bc9308c 100755 --- a/reporting/reporting/functest/reporting-vims.py +++ b/reporting/reporting/functest/reporting-vims.py @@ -120,7 +120,7 @@ for version in VERSIONS: LOGGER.debug("vIMS deployment: %ss", res_vnf) LOGGER.debug("VNF testing: %ss", res_test_vnf) LOGGER.debug("VNF testing results: %s", format_result) - except Exception as err: # pylint: disable=broad-except + except KeyError as err: # pylint: disable=broad-except LOGGER.error("Uncomplete data %s", err) LOGGER.debug("----------------------------------------") diff --git a/reporting/reporting/functest/template/index-status-tmpl.html b/reporting/reporting/functest/template/index-status-tmpl.html index 50fc648..48b5a2d 100644 --- a/reporting/reporting/functest/template/index-status-tmpl.html +++ b/reporting/reporting/functest/template/index-status-tmpl.html @@ -144,33 +144,32 @@ $(document).ready(function (){ <span class="panel-header-item"> </span> </div> + {% for tier in ['healthcheck', 'smoke', 'vnf', 'features'] -%} <table class="table"> <tr> + <h2>{{tier}}</h2> {% for test in items[scenario] -%} - <th> - {% if test.getCriteria() > -1 -%} - {{test.getDisplayName() }} + {% if test.getCriteria() > -1 and test.getTier() == tier -%} + <th>{{test.getDisplayName() }}</th> {%- endif %} - {% if test.getTier() > 3 -%} - * - {%- endif %} - </th> - {%- endfor %} + {%- endfor %} </tr> <tr class="tr-weather-weather"> - {% for test in items[scenario] -%} - {% if test.getCriteria() > 2 -%} + {% for test in items[scenario] -%} + {% if test.getCriteria() > 2 and test.getTier() == tier -%} <td><img src="../../img/weather-clear.png"></td> - {%- elif test.getCriteria() > 1 -%} + {%- elif test.getCriteria() > 1 and test.getTier() == tier -%} <td><img src="../../img/weather-few-clouds.png"></td> - {%- elif test.getCriteria() > 0 -%} + {%- elif test.getCriteria() > 0 and test.getTier() == tier -%} <td><img src="../../img/weather-overcast.png"></td> - {%- elif test.getCriteria() > -1 -%} + {%- elif test.getCriteria() > -1 and test.getTier() == tier -%} <td><img src="../../img/weather-storm.png"></td> {%- endif %} {%- endfor %} </tr> </table> + <br><hr> + {%- endfor %} </div> </div> {%- endfor %} diff --git a/reporting/reporting/functest/testCase.py b/reporting/reporting/functest/testCase.py index a182dd4..fba3216 100644 --- a/reporting/reporting/functest/testCase.py +++ b/reporting/reporting/functest/testCase.py @@ -26,7 +26,9 @@ class TestCase(object): 'onos': 'ONOS', 'ocl': 'OCL', 'tempest_smoke_serial': 'Tempest (smoke)', + 'tempest_smoke': 'Tempest (smoke)', 'tempest_full_parallel': 'Tempest (full)', + 'tempest_full': 'Tempest (full)', 'tempest_defcore': 'Tempest (Defcore)', 'refstack_defcore': 'Refstack', 'rally_sanity': 'Rally (smoke)', @@ -43,20 +45,45 @@ class TestCase(object): 'functest-odl-sfc': 'SFC', 'onos_sfc': 'SFC', 'parser-basics': 'Parser', - 'connection_check': 'Health (connection)', - 'api_check': 'Health (api)', + 'connection_check': 'connectivity', + 'api_check': 'api', 'snaps_smoke': 'SNAPS', - 'snaps_health_check': 'Health (dhcp)', + 'snaps_health_check': 'dhcp', 'gluon_vping': 'Netready', 'fds': 'FDS', 'cloudify_ims': 'vIMS (Cloudify)', + 'cloudify': 'Cloudify', 'orchestra_openims': 'OpenIMS (OpenBaton)', 'orchestra_clearwaterims': 'vIMS (OpenBaton)', 'opera_ims': 'vIMS (Open-O)', 'vyos_vrouter': 'vyos (Cloudify)', 'barometercollectd': 'Barometer', 'odl_netvirt': 'Netvirt', - 'security_scan': 'Security'} + 'security_scan': 'Security', + 'patrole': 'Patrole', + 'tenantnetwork1': 'tenant network 1', + 'tenantnetwork2': 'tenant network 2', + 'vmready1': 'vm ready 1', + 'vmready2': 'vm ready 2', + 'singlevm1': 'single vm 1', + 'singlevm2': 'single vm 2', + 'cinder_test': 'cinder tests', + 'barbican': 'barbican', + 'vmtp': 'vmtp', + 'juju_epc': 'vEPC (Juju)', + 'shaker': 'shaker', + 'neutron_trunk': 'Neutron trunk', + 'tempest_scenario': 'tempest_scenario', + 'networking-bgpvpn': 'networking-bgpvpn', + 'networking-sfc': 'networking-sfc', + 'tempest_full': 'Tempest (full)', + 'cloudify': 'cloudify', + 'heat_ims': 'vIMS (Heat)', + 'vmtp': 'vmtp', + 'tempest_smoke': 'Tempest (smoke)', + 'neutron-tempest-plugin-api': 'Neutron API', + 'vgpu': 'vgpu', + 'stor4nfv_os': 'stor4nfv_os'} try: self.displayName = display_name_matrix[self.name] except: @@ -68,22 +95,22 @@ class TestCase(object): def getProject(self): return self.project - def getConstraints(self): - return self.constraints - def getCriteria(self): return self.criteria def getTier(self): return self.tier + def getConstraints(self): + return self.constraints + def setCriteria(self, criteria): self.criteria = criteria def setIsRunnable(self, isRunnable): self.isRunnable = isRunnable - def checkRunnable(self, installer, scenario, config): + def checkRunnable(self, installer, scenario, arch, config): # Re-use Functest declaration # Retrieve Functest configuration file functest_config.yaml is_runnable = True @@ -98,27 +125,36 @@ class TestCase(object): # Retrieve test constraints # Retrieve test execution param - test_execution_context = {"installer": installer, - "scenario": scenario} + test_execution_context = {"INSTALLER_TYPE": installer, + "DEPLOY_SCENARIO": scenario, + "POD_ARCH": arch} + + # 3 types of constraints + # INSTALLER_TYPE + # DEPLOY_SCENARIO + # POD_ARCH # By default we assume that all the tests are always runnable... # if test_env not empty => dependencies to be checked - if config_test is not None and len(config_test) > 0: - # possible criteria = ["installer", "scenario"] - # consider test criteria from config file - # compare towards CI env through CI en variable - for criteria in config_test: - if re.search(config_test[criteria], - test_execution_context[criteria]) is None: - # print "Test "+ test + " cannot be run on the environment" - is_runnable = False + try: + if config_test is not None and len(config_test) > 0: + # possible criteria = ["installer", "scenario"] + # consider test criteria from config file + # compare towards CI env through CI en variable + for criterias in config_test: + for criteria_key, criteria_value in criterias.iteritems(): + if re.search( + criteria_value, + test_execution_context[criteria_key]) is None: + is_runnable = False + except AttributeError: + is_runnable = False # print is_runnable self.isRunnable = is_runnable def toString(self): testcase = ("Name=" + self.name + ";Criteria=" + str(self.criteria) + ";Project=" + self.project + - ";Constraints=" + str(self.constraints) + ";IsRunnable" + str(self.isRunnable)) return testcase diff --git a/reporting/reporting/qtip/template/index-status-tmpl.html b/reporting/reporting/qtip/template/index-status-tmpl.html index f55f781..9ce82c5 100644 --- a/reporting/reporting/qtip/template/index-status-tmpl.html +++ b/reporting/reporting/qtip/template/index-status-tmpl.html @@ -49,7 +49,7 @@ <li><a href="status-apex.html">Apex</a></li> <!--<li><a href="status-compass.html">Compass</a></li>--> <!--<li><a href="status-daisy.html">Daisy</a></li>--> - <!--<li><a href="status-fuel.html">Fuel</a></li>--> + <li><a href="status-fuel.html">Fuel</a></li> <!--<li><a href="status-joid.html">Joid</a></li>--> </ul> </nav> diff --git a/reporting/reporting/reporting.yaml b/reporting/reporting/reporting.yaml index 8123d01..6a59a44 100644 --- a/reporting/reporting/reporting.yaml +++ b/reporting/reporting/reporting.yaml @@ -9,7 +9,7 @@ general: versions: - master - - euphrates + - gambia log: log_file: reporting.log @@ -36,9 +36,11 @@ testapi: functest: blacklist: - odl_netvirt - - juju_epc - tempest_full_parallel + - tempest_full - rally_full + - heat_ims + - tempest_scenario max_scenario_criteria: 50 test_conf: https://git.opnfv.org/cgit/functest/plain/functest/ci/testcases.yaml log_level: ERROR diff --git a/reporting/reporting/utils/reporting_utils.py b/reporting/reporting/utils/reporting_utils.py index 58a0c62..28499cd 100644 --- a/reporting/reporting/utils/reporting_utils.py +++ b/reporting/reporting/utils/reporting_utils.py @@ -110,13 +110,13 @@ def getApiResults(case, installer, scenario, version): return results -def getScenarios(project, case, installer, version): +def getScenarios(project, case, installer, version, **kwargs): """ Get the list of Scenarios """ test_results = None scenario_results = None - period = get_config('general.period') + period = kwargs.get('period', get_config('general.period')) url_base = get_config('testapi.url') url = ("http://" + url_base + diff --git a/reporting/reporting/vsperf/reporting-status.py b/reporting/reporting/vsperf/reporting-status.py index fc4cc67..377c315 100644 --- a/reporting/reporting/vsperf/reporting-status.py +++ b/reporting/reporting/vsperf/reporting-status.py @@ -6,133 +6,132 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -import datetime import os +import datetime +import logging +from itertools import ifilter import jinja2 -import reporting.utils.reporting_utils as rp_utils -import reporting.utils.scenarioResult as sr - -installers = rp_utils.get_config('general.installers') -PERIOD = rp_utils.get_config('general.period') - -# Logger -logger = rp_utils.getLogger("Storperf-Status") -reportingDate = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") - -logger.info("*******************************************") -logger.info("* Generating reporting scenario status *") -logger.info("* Data retention = %s days *" % PERIOD) -logger.info("* *") -logger.info("*******************************************") - -# retrieve the list of storperf tests -versions = {'master'} - -# For all the versions -for version in versions: - # For all the installers - for installer in installers: - scenario_results = rp_utils.getScenarios("vsperf", - None, - installer, - None) - items = {} - scenario_result_criteria = {} - logger.info("installer %s, version %s, scenario ", installer, version) - - # From each scenarios get results list - for s, s_result in scenario_results.items(): - logger.info("---------------------------------") - logger.info("installer %s, version %s, scenario %s", installer, - version, s) - ten_criteria = len(s_result) - - ten_score = 0 - for v in s_result: - if "PASS" in v['criteria']: - ten_score += 1 - - logger.info("ten_score: %s / %s" % (ten_score, ten_criteria)) - - four_score = 0 - try: - LASTEST_TESTS = rp_utils.get_config( - 'general.nb_iteration_tests_success_criteria') - s_result.sort(key=lambda x: x['start_date']) - four_result = s_result[-LASTEST_TESTS:] - logger.debug("four_result: {}".format(four_result)) - logger.debug("LASTEST_TESTS: {}".format(LASTEST_TESTS)) - # logger.debug("four_result: {}".format(four_result)) - four_criteria = len(four_result) - for v in four_result: - if "PASS" in v['criteria']: - four_score += 1 - logger.info("4 Score: %s / %s " % (four_score, - four_criteria)) - except Exception: - logger.error("Impossible to retrieve the four_score") - - try: - s_status = (four_score * 100) / four_criteria - except ZeroDivisionError: - s_status = 0 - logger.info("Score percent = %s" % str(s_status)) - s_four_score = str(four_score) + '/' + str(four_criteria) - s_ten_score = str(ten_score) + '/' + str(ten_criteria) - s_score_percent = str(s_status) - - logger.debug(" s_status: {}".format(s_status)) - if s_status == 100: - logger.info(">>>>> scenario OK, save the information") - else: - logger.info(">>>> scenario not OK, last 4 iterations = %s, \ - last 10 days = %s" % (s_four_score, s_ten_score)) - - s_url = "" - if len(s_result) > 0: - build_tag = s_result[len(s_result)-1]['build_tag'] - logger.debug("Build tag: %s" % build_tag) - s_url = s_url = rp_utils.getJenkinsUrl(build_tag) - logger.info("last jenkins url: %s" % s_url) - - # Save daily results in a file - path_validation_file = ("./display/" + version + - "/vsperf/scenario_history.txt") - - if not os.path.exists(path_validation_file): - with open(path_validation_file, 'w') as f: - info = 'date,scenario,installer,details,score\n' - f.write(info) - - with open(path_validation_file, "a") as f: - info = (reportingDate + "," + s + "," + installer + - "," + s_ten_score + "," + - str(s_score_percent) + "\n") - f.write(info) - - scenario_result_criteria[s] = sr.ScenarioResult(s_status, - s_four_score, - s_ten_score, - s_score_percent, - s_url) - - logger.info("--------------------------") - - templateLoader = jinja2.FileSystemLoader(".") - templateEnv = jinja2.Environment(loader=templateLoader, - autoescape=True) - - TEMPLATE_FILE = "./reporting/vsperf/template/index-status-tmpl.html" - template = templateEnv.get_template(TEMPLATE_FILE) - - outputText = template.render(scenario_results=scenario_result_criteria, - installer=installer, - period=PERIOD, - version=version, - date=reportingDate) - - with open("./display/" + version + - "/vsperf/status-" + installer + ".html", "wb") as fh: - fh.write(outputText) +from reporting.utils import reporting_utils +from reporting.utils.scenarioResult import ScenarioResult + +LOG = reporting_utils.getLogger("Vsperf-Status") +LOG.setLevel(logging.DEBUG) + +HISTORY_FILE = "./display/{}/vsperf/scenario_history.txt" +TEMPLATE_FILE = "./reporting/vsperf/template/index-status-tmpl.html" +TARGET_FILE = "./display/{}/vsperf/reporting.html" + + +def _get_version_data(data): + version_data = {} + for ele in data: + try: + version = ele['build_tag'].split('-')[-2] + except (KeyError, TypeError, IndexError): + continue + + if version not in version_data: + version_data[version] = [] + version_data[version].append(ele) + return version_data + + +def _get_case_data(data): + case_data = {} + for ele in data: + case_name = ele['case_name'] + if case_name not in case_data: + case_data[case_name] = [] + case_data[case_name].append(ele) + return case_data + + +def _get_score(data): + count = len(list(ifilter(lambda r: r['criteria'] == 'PASS', data))) + total = len(data) + return count, total + + +def _get_result_obj(version, case, data): + fifty_pass, fifty_total = _get_score(data) + four_pass, four_total = _get_score(data[:4]) + status = (four_pass * 100) / four_total + four_score = '{}/{}'.format(four_pass, four_total) + fifty_score = '{}/{}'.format(fifty_pass, fifty_total) + percent = str(status) + url = reporting_utils.getJenkinsUrl(data[-1]['build_tag']) + LOG.debug('Last four score: %s', four_score) + LOG.debug('Fifty days score: %s', fifty_score) + LOG.debug('Last Four percent: %s', percent) + + _write_history_file(version, case, fifty_score, percent) + + return ScenarioResult(status, four_score, fifty_score, percent, url) + + +def _write_history_file(version, case, fifty_score, percent): + file_path = HISTORY_FILE.format(version) + + if not os.path.exists(file_path): + with open(file_path, 'w') as f: + info = 'date,case,details,score\n' + f.write(info) + + date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") + + with open(file_path, "a") as f: + info = '{},{},{},{}\n'.format(date, case, fifty_score, percent) + f.write(info) + + +def _do_generate(version, case_result): + templateLoader = jinja2.FileSystemLoader(".") + templateEnv = jinja2.Environment(loader=templateLoader, + autoescape=True) + + template = templateEnv.get_template(TEMPLATE_FILE) + + date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") + + outputText = template.render(case_result=case_result, + period=50, + version=version, + date=date) + + with open(TARGET_FILE.format(version), 'wb') as f: + f.write(outputText) + + +def _generate_reporting(version, data): + case_result = {} + case_data = _get_case_data(data) + for case, value in case_data.items(): + LOG.debug('version: %s, case: %s', version, case) + case_result[case] = _get_result_obj(version, case, value) + _do_generate(version, case_result) + + +def main(): + LOG.info("*******************************************") + LOG.info("* Generating vsperf reporting status *") + LOG.info("* Data retention = 50 days *") + LOG.info("* *") + LOG.info("*******************************************") + + data = reporting_utils.getScenarios("vsperf", + None, + "fuel", + None, + period=50) + version_data = _get_version_data(data['vsperf']) + + for version in {'master', 'fraser', 'gambia'}: + _generate_reporting(version, version_data.get(version, [])) + + LOG.info("End") + + +if __name__ == '__main__': + main() diff --git a/reporting/reporting/vsperf/template/index-status-tmpl.html b/reporting/reporting/vsperf/template/index-status-tmpl.html index 7e06ef6..0b14798 100644 --- a/reporting/reporting/vsperf/template/index-status-tmpl.html +++ b/reporting/reporting/vsperf/template/index-status-tmpl.html @@ -12,13 +12,12 @@ <script> function onDocumentReady() { // Gauge management - {% for scenario in scenario_results.keys() -%} - var gaugeScenario{{loop.index}} = gauge('#gaugeScenario{{loop.index}}'); + {% for scenario in case_result.keys() -%} var gaugeScenario{{loop.index}} = gauge('#gaugeScenario{{loop.index}}'); {%- endfor %} // assign success rate to the gauge function updateReadings() { - {% for scenario in scenario_results.keys() -%} - gaugeScenario{{loop.index}}.update({{scenario_results[scenario].getScorePercent()}}); + {% for case in case_result.keys() -%} + gaugeScenario{{loop.index}}.update({{case_result[case].getScorePercent()}}); {%- endfor %} } updateReadings(); @@ -28,11 +27,11 @@ d3.csv("./scenario_history.txt", function(data) { // *************************************** // Create the trend line - {% for scenario in scenario_results.keys() -%} + {% for case in case_result.keys() -%} // for scenario {{scenario}} // Filter results var trend{{loop.index}} = data.filter(function(row) { - return row["scenario"]=="{{scenario}}" && row["installer"]=="{{installer}}"; + return row["case"]=="{{case}}"; }) // Parse the date trend{{loop.index}}.forEach(function(d) { @@ -68,41 +67,30 @@ <nav> <ul class="nav nav-justified"> <li class="active"><a href="http://testresults.opnfv.org/reporting/index.html">Home</a></li> - <li><a href="status-apex.html">Apex</a></li> - <li><a href="status-compass.html">Compass</a></li> - <li><a href="status-fuel.html">Fuel</a></li> - <li><a href="status-joid.html">Joid</a></li> + <li><a href="reporting.html">Baremetal</a></li> </ul> </nav> </div> <div class="row"> <div class="col-md-1"></div> <div class="col-md-10"> - <div class="page-header"> - <h2>{{installer}}</h2> - </div> - <div><h1>Reported values represent the percentage of completed - - CI tests during the reporting period, where results - - were communicated to the Test Database.</h1></div> <div class="scenario-overview"> - <div class="panel-heading"><h4><b>List of last scenarios ({{version}}) run over the last {{period}} days </b></h4></div> + <div class="panel-heading"><h4><b>List of last cases ({{version}}) run over the last {{period}} days </b></h4></div> <table class="table"> <tr> - <th width="40%">Scenario</th> + <th width="40%">Case</th> <th width="20%">Status</th> <th width="20%">Trend</th> <th width="10%">Last 4 Iterations</th> - <th width="10%">Last 10 Days</th> + <th width="10%">Last 50 Days</th> </tr> - {% for scenario,result in scenario_results.iteritems() -%} + {% for case,result in case_result.items() -%} <tr class="tr-ok"> - <td><a href="{{scenario_results[scenario].getLastUrl()}}">{{scenario}}</a></td> + <td><a href="{{case_result[case].getLastUrl()}}">{{case}}</a></td> <td><div id="gaugeScenario{{loop.index}}"></div></td> <td><div id="trend_svg{{loop.index}}"></div></td> - <td>{{scenario_results[scenario].getFourDaysScore()}}</td> - <td>{{scenario_results[scenario].getTenDaysScore()}}</td> + <td>{{case_result[case].getFourDaysScore()}}</td> + <td>{{case_result[case].getTenDaysScore()}}</td> </tr> {%- endfor %} </table> diff --git a/reporting/reporting/yardstick/reporting-status.py b/reporting/reporting/yardstick/reporting-status.py index 10cacf0..a6e3cdf 100644 --- a/reporting/reporting/yardstick/reporting-status.py +++ b/reporting/reporting/yardstick/reporting-status.py @@ -140,8 +140,7 @@ def generate_reporting_page(version, installer, archi, scenarios, period): percent) LOG.info("--------------------------") - if scenario_data: - generate_page(scenario_data, installer, period, version, archi) + generate_page(scenario_data, installer, period, version, archi) def main(): diff --git a/testapi/.gitignore b/testapi/.gitignore index e34365e..869a07a 100644 --- a/testapi/.gitignore +++ b/testapi/.gitignore @@ -12,4 +12,6 @@ build .ven docs/_build opnfv_testapi/tests/UI/coverage -3rd_party/static/testapi-ui/testapi-ui
\ No newline at end of file +3rd_party/static/testapi-ui/testapi-ui +.stestr/ + 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 feed1b6..2b20d6b 100644 --- a/testapi/3rd_party/static/testapi-ui/assets/css/style.css +++ b/testapi/3rd_party/static/testapi-ui/assets/css/style.css @@ -281,3 +281,64 @@ a.glyphicon { border-top:none!important; padding-bottom:0px!important; } + +json-tree .key { + color: black!important; +} + +.branch-preview { + display: none!important; +} + +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: 9999; + 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/3rd_party/static/testapi-ui/assets/lib/angular-json-tree/angular-json-tree.css b/testapi/3rd_party/static/testapi-ui/assets/lib/angular-json-tree/angular-json-tree.css new file mode 100644 index 0000000..f25142a --- /dev/null +++ b/testapi/3rd_party/static/testapi-ui/assets/lib/angular-json-tree/angular-json-tree.css @@ -0,0 +1,76 @@ +/* Structure */ +json-tree { + box-sizing: border-box; +} +json-tree *, +json-tree *:before, +json-tree *:after { + box-sizing: border-box; +} +json-tree .key { + vertical-align: middle; +} +json-tree .expandable { + position: relative; + padding-left: 0px +} +json-tree .expandable::before { + pointer-events: none; +} +json-tree .branch-preview { + display: inline-block; + vertical-align: middle; +} +/* Looks */ +json-tree ul { + padding-left: 0px; + margin-bottom: 0px; +} +json-tree li, +json-tree ul { + list-style: none; +} +json-tree li { + line-height: 1.5em; +} +json-tree .key { + font-weight: bold; + color: #D02828; + /* padding: 5px 10px 5px 15px; */ +} +json-tree .key::after { + content: ':'; +} +json-tree json-node.expandable::before { + /* content: '\25b6'; */ + position: absolute; + left: 0px; + font-size: 10px; + -webkit-transition: -webkit-transform .1s ease; + transition: -webkit-transform .1s ease; + transition: transform .1s ease; + transition: transform .1s ease, -webkit-transform .1s ease; +} +json-tree json-node.expandable.expanded::before { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} +json-tree .leaf-value, +json-tree .branch-preview { + word-break: break-all; +} +json-tree .branch-preview { + overflow: hidden; + font-style: italic; + max-width: 40%; + height: 1.5em; + opacity: .7; +} + +json-tree .firstkey::after { + content: ''; +} + +li > json-node > ul { + padding-left: 10px; +}
\ No newline at end of file diff --git a/testapi/3rd_party/static/testapi-ui/assets/lib/angular-json-tree/angular-json-tree.min.js b/testapi/3rd_party/static/testapi-ui/assets/lib/angular-json-tree/angular-json-tree.min.js new file mode 100644 index 0000000..59389b0 --- /dev/null +++ b/testapi/3rd_party/static/testapi-ui/assets/lib/angular-json-tree/angular-json-tree.min.js @@ -0,0 +1,67 @@ +/*global angular */ ! function () { + "use strict"; + var e = { + is: function (e, a) { + return Object.prototype.toString.call(e).slice(8, -1) === a + }, + whatClass: function (e) { + return Object.prototype.toString.call(e).slice(8, -1) + }, + forKeys: function (e, a) { + for (var n in e) + if (e.hasOwnProperty(n) && "function" != typeof e[n] && a(n, e[n])) break + } + }; + angular.module("angular-json-tree", ["ajs.RecursiveDirectiveHelper"]).directive("jsonTree", [function () { + return { + restrict: "E", + scope: { + object: "=", + startExpanded: "&?", + rootName: "&?" + }, + template: '<json-node key="rootName() || \'\'" value="object" start-expanded="startExpanded()"></json-node>' + } + }]).directive("jsonNode", ["ajsRecursiveDirectiveHelper", function (a) { + return { + restrict: "E", + scope: { + key: "=", + value: "=", + startExpanded: "&?" + }, + compile: function (e) { + return a.compile(e, this) + }, + template: ' <span style="padding-left:0px" class= "key col-md-2" ng-class="{\'hidden\' : key==\'\' && key!=\'0\'}" ng-click="toggleExpanded()">{{key}}</span> <span class="leaf-value col-md-10" ng-if="!isExpandable">{{value}}</span> <span class="branch-preview" ng-if="isExpandable" ng-show="!isExpanded" ng-click="toggleExpanded()">{{preview}}</span> <ul class="branch-value" ng-if="isExpandable" > <li ng-repeat="(subkey,subval) in value"> <json-node key="subkey" class="col-md-12" value="subval"></json-node> </li> </ul>', + pre: function (a, n, s) { + if (n.addClass(e.whatClass(a.value).toLowerCase()), e.is(a.value, "Object") || e.is(a.value, "Array")) { + a.isExpandable = !0, n.addClass("expandable"); + var t = e.is(a.value, "Array"); + a.preview = t ? "[ " : "{ ", e.forKeys(a.value, function (e, n) { + t ? a.preview += n + ", " : a.preview += e + ": " + n + ", " + }), a.preview = a.preview.substring(0, a.preview.length - (a.preview.length > 2 ? 2 : 0)) + (t ? " ]" : " }"), a.startExpanded && a.startExpanded() && (a.shouldRender = !0, n.addClass("expanded")), a.isExpanded = a.startExpanded ? a.startExpanded() : !1, a.toggleExpanded = function () { + a.isExpanded = !a.isExpanded, a.isExpanded ? n.addClass("expanded") : n.removeClass("expanded"), a.shouldRender = !0 + } + } else a.isExpandable = !1, n.addClass("not-expandable") + } + } + }]), angular.module("ajs.RecursiveDirectiveHelper", []).factory("ajsRecursiveDirectiveHelper", ["$compile", function (e) { + return { + compile: function (a, n) { + angular.isFunction(n) && (n = { + post: n + }); + var s, t = a.contents().remove(); + return { + pre: n && n.pre ? n.pre : null, + post: function (a, r) { + s || (s = e(t)), s(a, function (e) { + r.append(e) + }), n && n.post && n.post.apply(null, arguments) + } + } + } + } + }]) +}();
\ No newline at end of file diff --git a/testapi/docker/Dockerfile b/testapi/docker/Dockerfile index bbf12fc..03b15e8 100644 --- a/testapi/docker/Dockerfile +++ b/testapi/docker/Dockerfile @@ -23,30 +23,31 @@ # http://www.apache.org/licenses/LICENSE-2.0 # -FROM ubuntu:14.04 +FROM ubuntu:18.04 MAINTAINER SerenaFeng <feng.xiaowei@zte.com.cn> LABEL version="v1" description="OPNFV TestAPI Docker container" -ENV HOME /home +ARG user=ubuntu +ARG group=ubuntu -# Packaged dependencies RUN apt-get update && apt-get install -y \ -curl \ -git \ -gcc \ -wget \ -python-dev \ -python-pip \ -crudini \ ---no-install-recommends - -RUN pip install --upgrade pip requests - -RUN git config --global http.sslVerify false -RUN git clone https://gerrit.opnfv.org/gerrit/releng-testresults /home/releng-testresults - -WORKDIR /home/releng-testresults/testapi -RUN pip install -r requirements.txt - -RUN python setup.py install + curl git gcc wget python-dev python-pip python-wheel python-setuptools \ + crudini libxslt-dev zlib1g-dev --no-install-recommends && \ + groupadd -r $group && useradd -ms /bin/bash $user -g $group && \ + git clone https://gerrit.opnfv.org/gerrit/releng-testresults \ + /home/ubuntu/releng-testresults && \ + pip install -r /home/ubuntu/releng-testresults/testapi/requirements.txt \ + -c /home/ubuntu/releng-testresults/testapi/upper-constraints.txt \ + -c https://raw.githubusercontent.com/openstack/requirements/stable/ussuri/upper-constraints.txt && \ + sed -i '152,152s/)/,\ verify=False)/g' \ + /usr/local/lib/python2.7/dist-packages/cas.py && \ + cd /home/ubuntu/releng-testresults/testapi/ && python setup.py install && \ + for i in /home/ubuntu/releng-testresults /etc/opnfv_testapi /usr/local/share/opnfv_testapi; do \ + mkdir -p $i && chown -R $user:$group $i && \ + find $i -type d |xargs chmod 777 && \ + find $i -type f |xargs chmod 666 ; done && \ + apt-get remove --purge -y python-dev libxslt-dev zlib1g-dev && \ + apt-get autoremove --purge -y && apt-get clean && rm -rf /var/lib/apt/lists/* +WORKDIR /home/ubuntu/releng-testresults/testapi +USER ubuntu CMD ["bash", "docker/start-server.sh"] diff --git a/testapi/docker/prepare-env.sh b/testapi/docker/prepare-env.sh index 9086e77..3b061d2 100755 --- a/testapi/docker/prepare-env.sh +++ b/testapi/docker/prepare-env.sh @@ -2,18 +2,20 @@ FILE=/etc/opnfv_testapi/config.ini -if [ "$mongodb_url" != "" ]; then - sudo crudini --set --existing $FILE mongo url $mongodb_url -fi +[[ "${mongodb_url}" == "" ]] && mongodb_url=mongodb://mongo:27017/ +[[ "${base_url}" == "" ]] && base_url=http://localhost:8000 +[[ ! "${auth}" =~ [f|F]alse ]] && auth=true -if [ "$base_url" != "" ]; then - sudo crudini --set --existing $FILE api url $base_url/api/v1 - sudo crudini --set --existing $FILE ui url $base_url - sudo cat > /usr/local/share/opnfv_testapi/testapi-ui/config.json << EOF +auth_server=`echo ${auth:0:1} | tr '[:lower:]' '[:upper:]'``echo ${auth:1} | tr '[:upper:]' '[:lower:]'` +auth_web=`echo ${auth} | tr '[:upper:]' '[:lower:]'` +crudini --set --existing ${FILE} mongo url ${mongodb_url} +crudini --set --existing ${FILE} api url ${base_url}/api/v1 +crudini --set --existing ${FILE} ui url ${base_url} +crudini --set --existing ${FILE} api authenticate ${auth_server} + +cat > /usr/local/share/opnfv_testapi/testapi-ui/config.json << EOF { - "testapiApiUrl": "$base_url/api/v1", - "authenticate": true + "testapiApiUrl": "${base_url}/api/v1", + "authenticate": ${auth_web} } EOF - -fi diff --git a/testapi/docs/developer/devguide/images/CAS-sequence.jpg b/testapi/docs/developer/devguide/images/CAS-sequence.jpg Binary files differnew file mode 100644 index 0000000..a624871 --- /dev/null +++ b/testapi/docs/developer/devguide/images/CAS-sequence.jpg diff --git a/testapi/docs/developer/devguide/testapi-client-import.rst b/testapi/docs/developer/devguide/testapi-client-import.rst new file mode 100644 index 0000000..c6d7311 --- /dev/null +++ b/testapi/docs/developer/devguide/testapi-client-import.rst @@ -0,0 +1,974 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. (c) 2017 ZTE Corp. + +===================== +TestAPI client import +===================== + +**Python module to communicate with the TestAPI Server** + +This project aims to provide a python module which can +communicate with the TestAPI Server. The user can use this client +to fetch/post/modify the resources on the TestAPI Server. + +Usage +----- + +Pod +^^^ + +GET +""" + +Get a list of all the declared pods from the TestAPI server. + +.. code-block:: shell + + from testapiclient.client import pods + + pod_client = pods.PodsClient() + pod_client.get() + +The user can filter the results by the name attribute. Backend will +use a regular expression to find the list of pods which are +related to given name. + +.. code-block:: shell + + from testapiclient.client import pods + + pod_client = pods.PodsClient() + pod_client.get(name='pod1') + +.. NOTE:: + Response format: [{"_id": "", "creator": "", "role": "", "name": "", + "details": "", "mode": "", "creation_date": ""}] + + +GET ONE +""""""" + +Get a specific pod by its name. + +.. code-block:: shell + + from testapiclient.client import pods + + pod_client = pods.PodsClient() + pod_client.get_one('name') + +.. NOTE:: + Response format: {"_id": "", "creator": "", "role": "", "name": "", + "details": "", "mode": "", "creation_date": ""} + +CREATE +"""""" +This method used to create a project on the server. +The user should provide the user parameter and the password +parameter while initiating the PodsClient. + +Input for the function : + + * pod-json : Json object of the project + +.. NOTE:: + pod-json-schema - '{"role": "", "name": "", "details": "", "mode": ""}' + + * role should be either "community-ci" or "production-ci" + * mode should be either "metal" or "virtual" + +.. code-block:: shell + + from testapiclient.client import pods + + pod_client = pods.PodsClient(user='test', password='pass') + pod_client.create({'name': 'test-api', 'mode':'metal', + 'role':'community_ci', 'details':''}) + + +Project +^^^^^^^ + +GET +""" + +Get a list of all the declared projects from the TestAPI server. + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient() + project_client.get() + +User can filter the results by the name attribute. Backend will +use a regular expression to find the list of projects which are +related to given name. + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient() + project_client.get(name='project1') + +.. NOTE:: + Response format: [{"_id": "", "creator": "", "description": "", + "name": "", "creation_date": ""}] + +GET ONE +""""""" + +Get a specific project by its name. + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient() + project_client.get_one('name') + +.. NOTE:: + Response format: {"_id": "", "creator": "", "description": "", + "name": "", "creation_date": ""} + +CREATE +"""""" + +This method used to create a project on the server. +The user should provide the user parameter and the password +parameter while initiating the ProjectsClient. + +Input for the function : + + * project-json : Json object of the project + +.. NOTE:: + project-json schema - '{"description": "", "name": ""}' + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient(user='test', password='pass') + project_client.create({'name': 'functest', 'description':'sample text'} + +UPDATE +"""""" + +This method used to update an existing project on the server. +The user should provide the user parameter and the password +parameter while initiating the ProjectsClient. + +Input for the function : + + * project-name: name of the project which user want to update. + * project-json: Json object of the project + +.. NOTE:: + project-json schema - '{"description": "", "name": ""}' + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient(user='test', password='pass') + project_client.update('functest', {'name': 'functest', + 'description':'updated text'}) + +DELETE +"""""" + +This method used to delete an existing project on the server. +The user should provide the user parameter and the password +parameter while initiating the ProjectsClient. + +Input for the function : + + * project-name: name of the project which user want to delete. + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient(user='test', password='pass') + project_client.delete('functest') + + +Testcase +^^^^^^^^ + +GET +""" + +Get a list of all the declared testcases under a project +from the TestAPI server. + +Input for the function : + + * project_name : Name of the project + +.. code-block:: shell + + from testapiclient.client import testcases + + testcase_client = testcases.TestcasesClient() + testcase_client.get(project_name='functest') + + +.. NOTE:: + Response format: [{ "project_name": "functest", "run": "", + "description": "", "tags": "", "creation_date": "", + "dependencies": "", "tier": "", "trust": "", "blocking": "", + "name": "", "ci_loop": "", "url": "", "version": "", + "criteria": "", "domains": "", "_id": "", "catalog_description": ""}] + +GET ONE +""""""" + +Get a specific testcase by its name and project name. + +.. code-block:: shell + + from testapiclient.client import testcases + + testcase_client = testcases.TestcasesClient() + testcase_client.get_one(project_name='functest', case_name='name') + +.. NOTE:: + Response format: { "project_name": "functest", "run": "", + "description": "", "tags": "", "creation_date": "", + "dependencies": "", "tier": "", "trust": "", "blocking": "", + "name": "", "ci_loop": "", "url": "", "version": "", + "criteria": "", "domains": "", "_id": "", "catalog_description": ""} + +CREATE +"""""" + +This method used to create a testcase on the server. +The user should provide the user parameter and the password +parameter while initiating the TestcasesClient. + +Input for the function : + * project_name : Project name + * testcase_json : Json object of the testcase + +.. NOTE:: + testcase_json schema - '{ "run": "", "description": "", "tags": "", + "dependencies": "", "tier": "", "trust": "", "blocking": "", + "name": "", "ci_loop": "", "url": "", "version": "", + "criteria": "", "domains": "", "catalog_description": ""}' + +.. code-block:: shell + + from testapiclient.client import testcases + + testcase_client = testcases.TestcasesClient(user='test', password='pass') + testcase_client.create(project_name, testcase_json) + +UPDATE +"""""" + +This method used to update an existing testcase on the server. +The user should provide the user parameter and the password +parameter while initiating the TestcasesClient. + +Input for the function : + * project_name : Project name + * testcase_name: name of the testcase which user want to update. + * testcase_json: Json object of the testcase + +.. NOTE:: + testcase-json schema - '{ "run": "", "description": "", "tags": "", + "dependencies": "", "tier": "", "trust": "", "blocking": "", + "name": "", "ci_loop": "", "url": "", "version": "", + "criteria": "", "domains": "", "catalog_description": ""}' + +.. code-block:: shell + + from testapiclient.client import testcases + + testcase_client = testcases.TestcasesClient(user='test', password='pass') + testcase_client.update(project_name, testcase_name, testcase_json) + +DELETE +"""""" + +This method used to delete an existing testcase on the server. +The user should provide the user parameter and the password +parameter while initiating the TestcasesClient. + +Input for the function : + + * project_name: name of the project + * testcase_name: name of the testcase which user want to delete. + +.. code-block:: shell + + from testapiclient.client import testcases + + testcase_client = testcases.TestcasesClient(user='test', password='pass') + testcase_client.delete(project_name, testcase_name) + + +Result +^^^^^^^ + +GET +""" + +Get a list of all the declared results from the TestAPI server. + +.. code-block:: shell + + from testapiclient.client import results + + result_client = results.ResultsClient() + result_client.get() + +User can filter the results by using some attributes. + +.. NOTE:: + List of search attributes. + + * case : Search results using tesetcase + * build-tag : Search results using build tag + * from : Search results using from date + * last : Search results using last date + * scenario : Search results using scenario + * period : Search results using period + * project : Search results using project + * to : Search results using to + * version : Search results using version + * criteria : Search results using criteria + * installer : Search results using installer + * pod : Search results using pod + * page : Search results using page + +.. code-block:: shell + + from testapiclient.client import results + + result_client = results.ResultsClient() + result_client.get(pod='pod1', project='project1') + + +.. NOTE:: + Response format: [{ "project_name": "", "scenario": "", + "stop_date": "", "case_name": "", "build_tag": "", + "version": "", "pod_name": "", "criteria": "", + "installer": "", "start_date": "", "details": ""}] + +GET ONE +""""""" + +Get a specific result by its id. + +.. code-block:: shell + + from testapiclient.client import results + + result_client = results.ResultsClient() + result_client.get_one(result_id) + +.. NOTE:: + Response format: { "project_name": "", "scenario": "", + "stop_date": "", "case_name": "", "build_tag": "", + "version": "", "pod_name": "", "criteria": "", + "installer": "", "start_date": "", "details": ""} + +CREATE +"""""" + +This method used to create a result on the server. +The user should provide a valid token to run this method. +Read testapi-client.rst to more details. + +Input for the function : + * result_json : Json object of the result + +.. NOTE:: + result_json schema - '{ "project_name": "", "scenario": "", + "stop_date": "", "case_name": "", "build_tag": "", + "version": "", "pod_name": "", "criteria": "", + "installer": "", "start_date": "", "details": ""}' + +.. code-block:: shell + + from testapiclient.client import results + + result_client = results.ResultsClient() + result_client.create(result_json) + +DeployResult +^^^^^^^^^^^^ + +GET +""" + +Get a list of all the declared deploy results from the TestAPI server. + +.. code-block:: shell + + from testapiclient.client import deploy_results + + deploy_result_client = deploy_results.DeployResultsClient() + deploy_result_client.get() + +User can filter the deploy results by using some attributes. + +.. NOTE:: + List of search attributes. + + * job-name : Search results using job + * build_id : Search results using build id + * from : Search results using from date + * last : Search results using last date + * scenario : Search results using scenario + * period : Search results using period + * to : Search results using to + * version : Search results using version + * criteria : Search results using criteria + * installer : Search results using installer + * pod_name : Search results using pod + * page : Search results using page + +.. code-block:: shell + + from testapiclient.client import deploy_results + + deploy_result_client = deploy_results.DeployResultsClient() + deploy_result_client.get(scenario='scenario1', installer='installer1') + + +.. NOTE:: + Response format: [{"build_id": "", "upstream_build_id": "", + "scenario": "", "stop_date": "", "start_date": "", + "upstream_job_name": "", "version": "", "pod_name": "", + "criteria": "", "installer": "", "_id": "", "job_name": "", + "details": ""}] + +GET ONE +""""""" + +Get a specific deploy result by its id. + +.. code-block:: shell + + from testapiclient.client import deploy_results + + deploy_result_client = deploy_results.DeployResultsClient() + deploy_result_client.get_one(deploy_result_id) + +.. NOTE:: + Response format: {"build_id": "", "upstream_build_id": "", + "scenario": "", "stop_date": "", "start_date": "", + "upstream_job_name": "", "version": "", "pod_name": "", + "criteria": "", "installer": "", "_id": "", "job_name": "", + "details": ""} + +CREATE +"""""" + +This method used to create a deploy_result on the server. +The user should provide a valid token to run this method. +Read testapi-client.rst to more details. + +Input for the function : + * deploy_result_json : Json object of the deploy_result + +.. NOTE:: + deploy_result_json schema - '{"build_id": "", "upstream_build_id": "", + "scenario": "", "stop_date": "", "start_date": "", + "upstream_job_name": "", "version": "", "pod_name": "", + "criteria": "", "installer": "", "job_name": "", + "details": ""}' + +.. code-block:: shell + + from testapiclient.client import deploy_results + + deploy_result_client = deploy_results.DeployResultsClient() + deploy_result_client.create(deploy_result_json) + +Scenario +^^^^^^^^ + +GET +""" + +Get a list of all the declared scenarios from the TestAPI server. + +.. code-block:: shell + + from testapiclient.client import scenarios + + scenario_client = scenarios.ScenariosClient() + scenario_client.get() + +User can filter the scenarios by using some attributes. + +.. NOTE:: + List of search attributes. + + * project : Search scenarios using project + * installer : Search scenarios using project + * version : Search scenarios using project + * name: Search scenarios using project + +.. code-block:: shell + + from testapiclient.client import scenarios + + scenario_client = scenarios.ScenariosClient() + scenario_client.get(name='scenario1') + +.. NOTE:: + Response format: [{ "installers": [], "_id": "", "creation_date": "", + "name": "", "creator": ""}] + +GET ONE +""""""" + +Get a specific scenario by its name. + +.. code-block:: shell + + from testapiclient.client import scenarios + + scenario_client = scenarios.ScenariosClient() + scenario_client.get_one('name') + +.. NOTE:: + Response format: { "installers": [], "_id": "", "creation_date": "", + "name": "", "creator": ""} + +CREATE +"""""" + +This method used to create a scenario on the server. +The user should provide the user parameter and the password +parameter while initiating the ScenariosClient. + +Input for the function : + + * scenario-json : Json object of the scenario + +.. NOTE:: + scenario_json schema - '{ "installers": [], + "name": ""}' + + See Installer for installer_schema + +.. code-block:: shell + + from testapiclient.client import scenarios + + scenario_client = scenarios.ScenariosClient(user='test', password='pass') + scenario_client.create(scenario_json) + +UPDATE +"""""" + +This method used to update the name of an existing scenario on the server. +The user should provide the user parameter and the password +parameter while initiating the ScenariosClient. + +Input for the function : + + * scenario-name: name of the scenario which user want to update. + * scenario-json: Json object of the scenario + +.. NOTE:: + * scenario_name + * scenario_update_json schema - '{"name": ""}' + +.. code-block:: shell + + from testapiclient.client import scenarios + + scenario_client = scenarios.ScenariosClient(user='test', password='pass') + scenario_client.update(scenario_name, scenario_update_json) + +DELETE +"""""" + +This method used to delete an existing scenario on the server. +The user should provide the user parameter and the password +parameter while initiating the ScenariosClient. + +Input for the function : + + * scenario_name: name of the scenario which user want to delete. + +.. code-block:: shell + + from testapiclient.client import scenarios + + scenario_client = scenarios.ScenariosClient(user='test', password='pass') + scenario_client.delete('scenario_name') + +Scenario Installer +^^^^^^^^^^^^^^^^^^ + +CREATE +"""""" + +This method used to create an installer under a scenario +on the server. The user should provide the user parameter +and the password parameter while initiating the InstallersClient. + +Input for the function : + * scenario_name + * installer-json : Json object of the installer + +.. NOTE:: + installer_json schema - '{ "versions": [], + "installer": ""}' + + See version for version_schema + +.. code-block:: shell + + from testapiclient.client import installers + + installer_client = installers.InstallersClient(user='test', password='pass') + installer_client.create(scenario_name, installer_json) + +UPDATE +"""""" + +This method used to update the all existing installers of a scenario +The user should provide the user parameter and the password +parameter while initiating the InstallersClient. + +Input for the function : + + * scenario_name + * installer-json: Json object of the installer + +.. NOTE:: + * scenario_name + * installer_json schema - [{ "versions": [], "installer": ""}] + +.. code-block:: shell + + from testapiclient.client import installers + + installer_client = installers.InstallersClient(user='test', password='pass') + installer_client.update(scenario_name, installer_update_json) + +DELETE +"""""" + +This method used to delete existing installers from a scenario. +on the server. +The user should provide the user parameter and the password +parameter while initiating the InstallersClient. + +Input for the function : + * scenario_name + * installer_names: names of the installer which user want to delete. + +.. code-block:: shell + + from testapiclient.client import installers + + installer_client = installers.InstallersClient(user='test', password='pass') + installer_client.delete(scenario_name, installer_names) + +Scenario Version +^^^^^^^^^^^^^^^^ + +CREATE +"""""" + +This method used to create a version under a scenario +on the server. The user should provide the user parameter +and the password parameter while initiating the VersionsClient. + +Input for the function : + + * scenario_name + * installer_name + * version-json : Json object of the version + +.. NOTE:: + version_json schema - '{ "projects": [], "owner": "", + "version": ""}' + + See version for version_schema + +.. code-block:: shell + + from testapiclient.client import versions + + version_client = versions.VersionsClient(user='test', password='pass') + version_client.create(scenario_name, installer_name, version_json) + +UPDATE +"""""" + +This method used to update the all existing versions of a scenario +The user should provide the user parameter and the password +parameter while initiating the VersionsClient. + +Input for the function : + + * scenario_name + * installer_name + * version-json: Json object of the version + +.. NOTE:: + * scenario_name + * version_json schema - [{ "projects": [], "owner": "", "version": ""}] + +.. code-block:: shell + + from testapiclient.client import versions + + version_client = versions.VersionsClient(user='test', password='pass') + version_client.update(scenario_name, installer_name, version_update_json) + +DELETE +"""""" + +This method used to delete existing versions from a scenario. +on the server. +The user should provide the user parameter and the password +parameter while initiating the VersionsClient. + +Input for the function : + * scenario_name + * installer_name + * version_names: names of the version which user want to delete. + +.. code-block:: shell + + from testapiclient.client import versions + + version_client = versions.VersionsClient(user='test', password='pass') + version_client.delete(scenario_name, installer_name, version_names) + +Scenario Project +^^^^^^^^^^^^^^^^ + +CREATE +"""""" + +This method used to create a project under a scenario +on the server. The user should provide the user parameter +and the password parameter while initiating the ProjectsClient. + +Input for the function : + + * scenario_name + * installer_name + * version_name + * project-json : Json object of the project + +.. NOTE:: + project_json schema - '{ "scores": [], "trust_indcators": [], + "customs": [], "project": ""}' + + See project for project_schema + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient(user='test', password='pass') + project_client.create(scenario_name, installer_name, version_name, project_json) + +UPDATE +"""""" + +This method used to update the all existing projects of a scenario +The user should provide the user parameter and the password +parameter while initiating the ProjectsClient. + +Input for the function : + + * scenario_name + * installer_name + * version_name + * project-json: Json object of the project + +.. NOTE:: + * scenario_name + * project_json schema - [{"scores": [], "trust_indcators": [], "customs": [], "project": ""}] + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient(user='test', password='pass') + project_client.update(scenario_name, installer_name, version_name, project_update_json) + +DELETE +"""""" + +This method used to delete existing projects from a scenario. +on the server. +The user should provide the user parameter and the password +parameter while initiating the ProjectsClient. + +Input for the function : + * scenario_name + * installer_name + * version_name + * project_names: names of the project which user want to delete. + +.. code-block:: shell + + from testapiclient.client import projects + + project_client = projects.ProjectsClient(user='test', password='pass') + project_client.delete(scenario_name, installer_name, version_name, project_names) + +Scenario Custom +^^^^^^^^^^^^^^^ + +CREATE +"""""" + +This method used to create a custom under a scenario +on the server. The user should provide the user parameter +and the password parameter while initiating the CustomsClient. + +Input for the function : + + * scenario_name + * installer_name + * version_name + * project_name + * custom : List of Customs + +.. NOTE:: + * scenario_name + * custom schema - ["List of Strings"] + + See custom for custom_schema + +.. code-block:: shell + + from testapiclient.client import customs + + custom_client = customs.CustomsClient(user='test', password='pass') + custom_client.create(scenario_name, installer_name, version_name, + project_name, custom_json) + +UPDATE +"""""" + +This method used to update the all existing customs of a scenario +The user should provide the user parameter and the password +parameter while initiating the CustomsClient. + +Input for the function : + + * scenario_name + * installer_name + * version_name + * project_name + * custom : List of Customs + +.. NOTE:: + * scenario_name + * custom schema - ["List of Strings"] + +.. code-block:: shell + + from testapiclient.client import customs + + custom_client = customs.CustomsClient(user='test', password='pass') + custom_client.update(scenario_name, installer_name, version_name, + project_name custom) + +DELETE +"""""" + +This method used to delete existing customs from a scenario. +on the server. +The user should provide the user parameter and the password +parameter while initiating the CustomsClient. + +Input for the function : + * scenario_name + * installer_name + * version_name + * project_name + * custom: custom which user want to delete. + +.. code-block:: shell + + from testapiclient.client import customs + + custom_client = customs.CustomsClient(user='test', password='pass') + custom_client.delete(scenario_name, installer_name, version_name, + project_name, customs) + +Scenario Scores +^^^^^^^^^^^^^^^ + +CREATE +"""""" + +This method used to create a score under a scenario +on the server. The user should provide the user parameter +and the password parameter while initiating the ScoresClient. + +Input for the function : + + * scenario_name + * installer_name + * version_name + * project_name + * score_json : Schema for the score + +.. NOTE:: + * scenario_name + * score_json schema - '{ "date": "", "score": ""}' + + See score for score_schema + +.. code-block:: shell + + from testapiclient.client import scores + + score_client = scores.ScoresClient(user='test', password='pass') + score_client.create(scenario_name, installer_name, version_name, + project_name, score_json) + +Scenario TrustIndicator +^^^^^^^^^^^^^^^^^^^^^^^ + +CREATE +"""""" + +This method used to create a trust indicator under a scenario +on the server. The user should provide the user parameter +and the password parameter while initiating the TrustIndicatorsClient. + +Input for the function : + + * scenario_name + * installer_name + * version_name + * project_name + * trust_indicator_json : Schema for the trust_indicator + +.. NOTE:: + * scenario_name + * trust_indicator_json schema - '{ "date": "", "status": ""}' + + See trust_indicator for trust_indicator_schema + +.. code-block:: shell + + from testapiclient.client import trust_indicators + + trust_indicator_client = trust_indicators.TrustIndicatorsClient(user='test', password='pass') + trust_indicator_client.create(scenario_name, installer_name, version_name, + project_name, trust_indicator_json)
\ No newline at end of file diff --git a/testapi/docs/developer/devguide/testapi-client.rst b/testapi/docs/developer/devguide/testapi-client.rst index ab4c8e8..5ba5df3 100644 --- a/testapi/docs/developer/devguide/testapi-client.rst +++ b/testapi/docs/developer/devguide/testapi-client.rst @@ -6,5 +6,859 @@ TestAPI client ============== -.. toctree:: - :maxdepth: 2 +TestAPIClient is a command-line client for TestAPI that +brings the command set for pod, project, testcase, results, +deploy result and scenario together in a single shell with a uniform command +structure. + + +Installation +------------ + +User can install this client from the source. + +.. code-block:: shell + + python install testapi/testapi-client/setup.py install + +After the installation, user has to set the environment variables + +.. code-block:: shell + + source testapi/testapi-client/etc/client.creds + + +Authentication Process +---------------------- + +User needs to provide the username and the password with the testapi +command. + +.. code-block:: shell + + $ testapi -u [username] -p [password] + (testapi) pod create + + +or + +.. code-block:: shell + + testapi pode create -u [username] -p [password] [pod-schema] + +First one, user can continue the progress after the authentication. +cli will create a new session to handle the request. + +In second one, cli won't create a session. one time command. + +Token is also used for the authorization purpose. User has to obtain the +valid token from the TestAPI comminity and set it in the following file +: **testapi/testapi-client/etc/client.creds** + +.. code-block:: shell + + source testapi/testapi-client/etc/client.creds + + +Command Structure +----------------- + +TestAPIClient follows a common command Structure. + +.. code-block:: shell + + testapi [resource-name] [function] [-u] [username] [-p] [password] + [command-arguments] + +.. NOTE:: + resource-name : include first order parent name and resource name. + + example: + scenario installer, scenario version, scenario project, scenario custom, + scenario trustindicator, scenario score, pod , project, testcase, result, + deployresult and scenario. + +.. NOTE:: + -u and -p are optional commands. The user can decide on them. + +There are many arguments for each commands. User can get them using +help command in the cli. + +.. code-block:: shell + + pod create --help/-h + +Pod +^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi pod create [-u] [username] [-p] [password] [pod-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) pod create [pod-schema] + +.. NOTE:: + pod-schema - '{"role": "", "name": "", "details": "", "mode": ""}' + +Get +""" + +Authentication is not required + +.. code-block:: shell + + testapi pod get [-name] [key-word] + +.. NOTE:: + -name is not mandatory. The user can use the -name option to reduce the + search result otherwise they will get the details about all pods. + +Get one +""""""" + +Authentication is not required + +.. code-block:: shell + + testapi pod getone [name-keyword] + +.. NOTE:: + name-keyword is mandatory. + + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi pod delete [-u] [username] [-p] [password] [pod-name] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) pod delete [pod-name] + +.. NOTE:: + pod-name is mandatory. + + +Project +^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi project create [-u] [username] [-p] [password] [project-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) project create [project-schema] + +.. NOTE:: + project-schema - '{"description": "", "name": ""}' + + +Get +""" + +Authentication is not required + +.. code-block:: shell + + testapi project get [-name] [key-word] + +.. NOTE:: + -name is not mandatory. The user can use the -name option to reduce the + search result otherwise they will get the details about all projects. + +Get one +""""""" + +Authentication is not required + +.. code-block:: shell + + testapi project getone [name-keyword] + +.. NOTE:: + name-keyword is mandatory. + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi project put [-u] [username] [-p] [password] [project-name] + [project-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) project put [project-name] [project-schema] + +.. NOTE:: + project-schema - '{"name": "", "description": ""}' + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi project delete [-u] [username] [-p] [password] [project-name] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) project delete [project-name] + +.. NOTE:: + project-name is mandatory. + +Testcase +^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi testcase create [-u] [username] [-p] [password] + [--project-name] [testcase-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) testcase create [--project-name] [testcase-schema] + +.. NOTE:: + testcase-schema - '{"run": "", "name": "", "ci_loop": "", "tags": "", + "url": "", "catalog_description": "", "tier": "", + "dependencies": "", "version": "", "criteria": "", + "domains": "", "trust": "", "blocking": "", + "description": ""}' + + +Get +""" + +Authentication is not required + +.. code-block:: shell + + testapi testcase get [--project-name] + +.. NOTE:: + --project-name is mandatory. + +Get one +""""""" + +Authentication is not required + +.. code-block:: shell + + testapi testcase getone [--project-name] [name] + +.. NOTE:: + name and project-name are mandatory. + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi testcase put [-u] [username] [-p] [password] [--project-name] + [name] [testcase-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) testcase put [--project-name] [name] [testcase-schema] + +.. NOTE:: + testcase-schema - '{"run": "", "name": "", "ci_loop": "", "tags": "", + "url": "", "catalog_description": "", "tier": "", + "dependencies": "", "version": "", "criteria": "", + "domains": "", "trust": "", "blocking": "", + "description": ""} + + +Result +^^^^^^^ + +Create +"""""" + +Token is required. Set token as an environment variable. + +.. code-block:: shell + + testapi result create [-u] [username] [-p] [password] [result-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) result create [result-schema] + +.. NOTE:: + result-schema - '{"project_name": "", "scenario": "", "stop_date": "", + "case_name": "", "build_tag": "", "version": "", + "pod_name": "", "criteria": "", "installer": "", + "start_date": "", "details": ""}' + + +Get +""" + +Authentication is not required + +.. code-block:: shell + + testapi result get [-cli-arguments] [arguments-value] + +.. NOTE:: + List of commandline arguments + + * -case : Search results using tesetcase + * -build-tag : Search results using build tag + * -from : Search results using from date + * -last : Search results using last date + * -scenario : Search results using scenario + * -period : Search results using period + * -project : Search results using project + * -to : Search results using to + * ---version : Search results using version + * -criteria : Search results using criteria + * -installer : Search results using installer + * -pod : Search results using pod + * -page : Search results using page + +Get one +""""""" + +Token is required. Set token as an environment variable. + +.. code-block:: shell + + testapi result getone [result_id] + +.. NOTE:: + result_id is mandatory. + +Deploy Result +^^^^^^^^^^^^^ + +Create +"""""" + +Token is required. Set token as an environment variable. + + +.. code-block:: shell + + testapi deployresult [-u] [username] [-p] [password] + [--project-name] [deployresult-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) deployresult create [deployresult-schema] + +.. NOTE:: + deployresult-schema - '{"run": "", "name": "", "ci_loop": "", "tags": "", + "url": "", "catalog_description": "", "tier": "", + "dependencies": "", "version": "", "criteria": "", + "domains": "", "trust": "", "blocking": "", + "description": ""}' + + +Get +""" + +Authentication is not required + +.. code-block:: shell + + testapi deployresult get [-cli-arguments] [arguments-value] + +.. NOTE:: + List of command line arguments + + * -job-name : Search results using job + * -build-id : Search results using build id + * -from : Search results using from date + * -last : Search results using last date + * -scenario : Search results using scenario + * -period : Search results using period + * -to : Search results using to + * ---version : Search results using version + * -criteria : Search results using criteria + * -installer : Search results using installer + * -pod-name : Search results using pod + * -page : Search results using page + +Get one +""""""" + +Authentication is not required + +.. code-block:: shell + + testapi deployresult getone [deployresult_id] + +.. NOTE:: + deployresult_id is mandatory. + + +Scenario +^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario create [-u] [username] [-p] [password] [scenario-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario create [scenario-schema] + +.. NOTE:: + scenario-schema - '{"name": "", "installers": []}' + + +Get +""" + +Authentication is not required + +.. code-block:: shell + + testapi scenario get [-name] [key-word] + +.. NOTE:: + + user can use some attributes to reduce the search results. These are not + mandatory. + + * -name : Backend will use regular expression to search. + * -project : Search using project name + * -installer : Search using installer name + * -version : Search using version name + +Get one +""""""" + +Authentication is not required + +.. code-block:: shell + + testapi scenario getone [name-keyword] + +.. NOTE:: + name-keyword is mandatory. + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario put [-u] [username] [-p] [password] [scenario-name] + [scenario-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario put [scenario-name] [scenario-schema] + +.. NOTE:: + scenario-schema - '{"name": "", "installers": []}' + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi scenario delete [-u] [username] [-p] [password] [scenario-name] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario delete [scenario-name] + +.. NOTE:: + scenario-name is mandatory. + +Scenario installer +^^^^^^^^^^^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario installer create [-u] [username] [-p] [password] + --scenario-name [scenario-name] [installer-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario installer create --scenario-name [scenario-name] + [installer-schema] + +.. NOTE:: + installer-schema - '{"installer": "", "versions": []}' + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario installer put [-u] [username] [-p] [password] + --scenario-name [scenario-name] [installer-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario put --scenario-name [scenario-name] [installer-schema] + +.. NOTE:: + scenario-schema - '{"installer": "", "installers": []}' + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi scenario delete [-u] [username] [-p] [password] --scenario-name + [scenario-name] [name] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario delete --scenario-name [scenario-name] [name] + +Scenario version +^^^^^^^^^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario version create [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] [version-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario installer create --scenario-name [scenario-name] + --installer [installer] [version-schema] + +.. NOTE:: + installer-schema - '{"version": "", "owner": "", "projects": []}' + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario installer put [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] [version-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario installer put --scenario-name [scenario-name] + --installer [installer] [installer-schema] + +.. NOTE:: + scenario-schema - '{"version": "","owner": "", "projects": []}' + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi scenario installer delete [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] [name] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario installer delete --scenario-name [scenario-name] + --installer [installer] [name] + +Scenario Project +^^^^^^^^^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario project create [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] + ---version [version] [project-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario project create --scenario-name [scenario-name] + --installer [installer] ---version [version] [project-schema] + +.. NOTE:: + installer-schema - '{"scores": [],"customs": [], "trust_indicators": [], + project:""}' + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario project put [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] ---version + [version] [project-schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario project put --scenario-name [scenario-name] --installer + [installer] ---version [version] [project-schema] + +.. NOTE:: + scenario-schema - '{"scores": [],"customs": [], "trust_indicators": [], + project:""}' + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi scenario project delete [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] ---version + [version] [name] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario project delete --scenario-name [scenario-name] + --installer [installer] ---version [version] [name] + +Scenario Customs +^^^^^^^^^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario custom create [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] + ---version [version] --project [project] [customs] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario custom create --scenario-name [scenario-name] + --installer [installer] ---version [version] --project [project] [customs] + +.. NOTE:: + + customs : Space sperated strings + +Update +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario custom put [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] ---version + [version] --project [project] [customs] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario custom put --scenario-name [scenario-name] --installer + [installer] ---version [version] --project [project] [customs] + +.. NOTE:: + + customs : Space sperated strings + +Delete +"""""" + +Authentication is required + +.. code-block:: shell + + testapi scenario custom delete [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] ---version + [version] --project [project] [customs] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario custom delete --scenario-name [scenario-name] + --installer [installer] ---version [version] --project [project] + [customs] + +.. NOTE:: + + customs : Space sperated strings + +Scenario Score +^^^^^^^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario score create [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] + ---version [version] --project [project] [score_schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario score create --scenario-name [scenario-name] + --installer [installer] ---version [version] --project [project] + [score_schema] + +.. NOTE:: + + score_schema : '{"score": "", "date": ""}' + +Scenario TrustIndicators +^^^^^^^^^^^^^^^^^^^^^^^^ + +Create +"""""" + +Authentication required + +.. code-block:: shell + + testapi scenario trustindicator create [-u] [username] [-p] [password] + --scenario-name [scenario-name] --installer [installer] + ---version [version] --project [project] [trustindicator_schema] + +or + +.. code-block:: shell + + $ testapi [-u] [username] [-p] [password] + (testapi) scenario trustindicator create --scenario-name [scenario-name] + --installer [installer] ---version [version] --project [project] + [trustindicator_schema] + +.. NOTE:: + + trustindicator_schema : '{"status": "", "date": ""}'
\ No newline at end of file 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/docs/internship/webportal.rst b/testapi/docs/internship/webportal.rst new file mode 100644 index 0000000..1ea9c19 --- /dev/null +++ b/testapi/docs/internship/webportal.rst @@ -0,0 +1,169 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 + + +***************************** +Web Portal for OPNFV Test API +***************************** + +Author: Tharmarajasingam Thuvarakan Mentors: S. Feng, J.Lausuch, M.Richomme + +Abstract +======== + +TestAPI is used by all the test projects to report results. It is also used to declare projects, +test cases and labs. It is defined in the Functest developer guide. The internship aims to add web +portal for TestAPI. The showcase user got through the web portal, will be more human-friendly +compared to the swagger page. Also, internship aims to build a python client to reduce the workload. +Python client can be used as a python module. + +Overview +======== + +The internship time period was from Oct 9th to May 18th. The project proposal page is here [1]_. +The intern project was assigned to Thuvarakan and was mentored by S. Feng, J.Lausuch, M.Richomme. +The link to the patches submitted is [2]_. The internship was successfully completed and the +documentation is as follows. + + + +Problem Statement +================= + +The following were to be accomplished within the internship time frame. + +* Redesign the website theme + + Change the existing theme of the frontend and design a unified theme. + +* Add separate pages to each resource + + Create a separate web page for each resource in the new theme. + +* Implement all the functionalities in the frontend + + Implement the backend functionalities in the frontend for each + resource. + +* Authentication for testapiclient + + Implement the authentication functionality in the testapiclient. + +* Add all functionalities to testapiclient + + Implement the backend functionalities in the testapiclient for each + resource. + +* Add support to testapiclient as a python module + + Convert the testapiclient from CLI only to CLI and python module + mode. + + +Curation Phase +============== + +The curation phase was the first 4 to 8 weeks of the internship. This phase +was to get familiar with the testapi code and functionality and propose the +solutions/tools for the tasks mentioned above. + +These are the tools, we proposed for the solutions. + +* protractor: An end-to-end test framework for Angular and AngularJS applications + +* grunt: A Javascript task runner, a tool used to automatically perform + frequent tasks such as minification, compilation, unit testing, and + linting. + +* cliff: Command Line Interface Formulation Framework. + + +Schedule +======== + +=================== ======================================================== + Date Comment +=================== ======================================================== +10th Oct ~17th Oct Setting up the development environment, design decisions +17th Oct ~ 24th Oct Pod web portal CRUD +24th Oct ~ 31st Oct Projects web portal CRUD +13st Oct ~ 7th Nov Test cases web portal CRUD +7th Nov ~ 14th Nov Results web portal R +14th Nov ~ 21st Nov Results web portal R +21st Nov ~ 28th Nov Scenario web portal CRUD +28th Nov ~ 5th Dec Testapi-client framework +5th Dec ~ 12nd Dec Pods testapi-client CRUD +12nd Dec ~ 19th Dec Projects testapi-client CRUD +19th Dec ~ 26th Dec Test cases testapi-client CRUD +26th Dec ~ 9th Jan Results testapi-client CRUD +9th Jan ~ 23rd Jan Scenario testapi-client CRUD +23rd Jan ~ 6th Mar Bugfix in the frontend and testapiclient +6th Mar ~ 20th Mar Convert testapiclient to python module +20th Mar ~ 3rd Apr Testing the python module +3rd Apr ~ 9th May Documentation & Bugfix +=================== ======================================================== + + +FAQ +=== + + +Frontend +******** + +1. How to add a new test file for the frontend? + + * Frontend test is handled by protractor [3]_ and + automated by the grunt [4]_. All the tests are located in + "opnfv_testapi/tests/UI/e2e". + First, create a text file in the e2e folder. Then add it to the spec + list in the "opnfv_testapi/ui/Gruntfile.js". + + +2. How to test application's functionalities interactively? + + * Application requires authentication for many functionalities. + It will cause time for the developer to check the functionalities. + Developers can use the application in the authentication false mode. + To do that, first, change the authenticate to false in the + "etc/config.ini" file then change the authenticate to false in + the "opnfv_testapi/ui/config.json" file. + + +3. Browser does not reflect the code changes, what is it mean? + + * Browser is saving the caches for fast reloadings. Sometime browser + won't reload the new changes, to solve that we have to clear the browser + caches. + + +Testapiclient +************* + +1. How to add a new test file for testapiclient? + + * Frontend test is handled by testtools [5]_ and automated by tox [6]_. + All the tests are located in "testapi-client/tests/unit". Create a text + file in the unit folder. The name of the test file should start with + 'test\_'. It will automatically add that test file to queue. + + +2. Difference between client and cli? + + * Client is used to importing testapiclient as a python module. + The cli folder contained the command line interface for the testapiclient. + +References +========== + +.. [1] https://wiki.opnfv.org/display/DEV/Intern+Project%3A+Web+Portal+for+OPNFV+Test+API + +.. [2] https://gerrit.opnfv.org/gerrit/#/q/status:merged+owner:%22Thuvarakan+Tharmarajasingam+%253Ctharma.thuva%2540gmail.com%253E%22 + +.. [3] https://www.protractortest.org/ + +.. [4] https://gruntjs.com + +.. [5] https://github.com/testing-cabal/testtools + +.. [6] https://tox.readthedocs.io/en/latest/#
\ No newline at end of file diff --git a/testapi/etc/config.ini b/testapi/etc/config.ini index 86cb0ca..5f568a7 100644 --- a/testapi/etc/config.ini +++ b/testapi/etc/config.ini @@ -15,7 +15,7 @@ port = 8000 results_per_page = 20 # With debug_on set to true, error traces will be shown in HTTP responses -debug = True +debug = False token_check = False authenticate = True diff --git a/testapi/opnfv_testapi/common/check.py b/testapi/opnfv_testapi/common/check.py index 333871d..fdc527f 100644 --- a/testapi/opnfv_testapi/common/check.py +++ b/testapi/opnfv_testapi/common/check.py @@ -21,7 +21,8 @@ from opnfv_testapi.db import api as dbapi def is_authorized(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): - if CONF.api_authenticate and self.table in ['pods', 'projects', 'testcases']: + resources = ['pods', 'projects', 'testcases', 'scenarios'] + if CONF.api_authenticate and self.table in resources: testapi_id = self.get_secure_cookie(constants.TESTAPI_ID) if not testapi_id: raises.Unauthorized(message.not_login()) @@ -29,17 +30,23 @@ def is_authorized(method): if not user_info: raises.Unauthorized(message.not_lfid()) if method.__name__ == "_create": - kwargs['owner'] = testapi_id - if self.table in ['projects']: + kwargs['creator'] = testapi_id + if self.table in ['projects', 'testcases']: + map_name = { + 'projects': 'name', + 'testcases': 'project_name' + } + group = "opnfv-gerrit-{}-submitters" query = kwargs.get('query') if type(query) is not dict: query_data = query() else: - if self.json_args is None: + if (self.json_args is None or + map_name[self.table] not in self.json_args): query_data = query else: query_data = self.json_args - group = "opnfv-gerrit-" + query_data['name'] + "-submitters" + group = group.format(query_data[map_name[self.table]]) if group not in user_info['groups']: raises.Unauthorized(message.no_permission()) ret = yield gen.coroutine(method)(self, *args, **kwargs) @@ -47,6 +54,32 @@ def is_authorized(method): return wrapper +def is_reource_tied(method): + @functools.wraps(method) + def wrapper(self, *args, **kwargs): + query_data = {} + tied_maps = { + 'projects': ('testcases', 'project_name'), + 'pods': ('results', 'pod_name'), + 'testcases': ('results', 'case_name') + } + if self.table in tied_maps: + if method.__name__ == '_update': + 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) + if data: + raises.Unauthorized(message.tied_with_resource()) + ret = yield gen.coroutine(method)(self, *args, **kwargs) + raise gen.Return(ret) + return wrapper + + def valid_token(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): @@ -136,8 +169,10 @@ def new_not_exists(xstep): if query: query_data = query() if self.table == 'pods': - if query_data.get('name') is not None: - query_data['name'] = re.compile('\\b' + query_data.get('name') + '\\b', re.IGNORECASE) + if query_data.get('name'): + query_data['name'] = re.compile( + '\\b{}\\b'.format(query_data.get('name')), + re.IGNORECASE) to_data = yield dbapi.db_find_one(self.table, query_data) if to_data: raises.Forbidden(message.exist(self.table, query())) @@ -165,7 +200,9 @@ def query_by_name(xstep): def wrap(self, *args, **kwargs): if 'name' in self.request.query_arguments.keys(): query = kwargs.get('query', {}) - query.update({'name': re.compile(self.get_query_argument('name'), re.IGNORECASE)}) + query.update({ + 'name': re.compile(self.get_query_argument('name'), + re.IGNORECASE)}) kwargs.update({'query': query}) ret = yield gen.coroutine(xstep)(self, *args, **kwargs) diff --git a/testapi/opnfv_testapi/common/constants.py b/testapi/opnfv_testapi/common/constants.py index 70c9223..c7f82d9 100644 --- a/testapi/opnfv_testapi/common/constants.py +++ b/testapi/opnfv_testapi/common/constants.py @@ -1,4 +1,3 @@ TESTAPI_ID = 'testapi_id' CSRF_TOKEN = 'csrf_token' -ROLE = 'role' TESTAPI_USERS = ['opnfv-testapi-users'] diff --git a/testapi/opnfv_testapi/common/message.py b/testapi/opnfv_testapi/common/message.py index b92b7f0..52cc2ad 100644 --- a/testapi/opnfv_testapi/common/message.py +++ b/testapi/opnfv_testapi/common/message.py @@ -10,10 +10,6 @@ not_found_base = 'Could Not Found' exist_base = 'Already Exists' -def key_error(key): - return "KeyError: '{}'".format(key) - - def no_body(): return 'No Body' @@ -64,3 +60,7 @@ def must_int(name): def no_permission(): return 'You do not have permission to perform this action' + + +def tied_with_resource(): + return 'Selected resource is associated with other resources' diff --git a/testapi/opnfv_testapi/handlers/base_handlers.py b/testapi/opnfv_testapi/handlers/base_handlers.py index 9eac737..3dbd53a 100644 --- a/testapi/opnfv_testapi/handlers/base_handlers.py +++ b/testapi/opnfv_testapi/handlers/base_handlers.py @@ -79,8 +79,8 @@ class GenericApiHandler(web.RequestHandler): @check.valid_token @check.no_body @check.miss_fields - @check.new_not_exists @check.is_authorized + @check.new_not_exists @check.values_check @check.carriers_exist def _create(self, **kwargs): @@ -179,6 +179,7 @@ class GenericApiHandler(web.RequestHandler): @gen.coroutine @check.not_exist @check.is_authorized + @check.is_reource_tied def _delete(self, data, query=None): yield dbapi.db_delete(self.table, query) self.finish_request() @@ -189,6 +190,7 @@ class GenericApiHandler(web.RequestHandler): @check.not_exist @check.updated_one_not_exist @check.is_authorized + @check.is_reource_tied def _update(self, data, query=None, **kwargs): data = self.table_cls.from_dict(data) update_req = self._update_requests(data) @@ -200,6 +202,7 @@ class GenericApiHandler(web.RequestHandler): @gen.coroutine @check.no_body @check.not_exist + @check.is_authorized @check.updated_one_not_exist def pure_update(self, data, query=None, **kwargs): data = self.table_cls.from_dict(data) diff --git a/testapi/opnfv_testapi/handlers/deploy_result_handlers.py b/testapi/opnfv_testapi/handlers/deploy_result_handlers.py index 973bfef..a8fcd88 100644 --- a/testapi/opnfv_testapi/handlers/deploy_result_handlers.py +++ b/testapi/opnfv_testapi/handlers/deploy_result_handlers.py @@ -1,6 +1,7 @@ from opnfv_testapi.handlers import result_handlers from opnfv_testapi.models import deploy_result_models from opnfv_testapi.tornado_swagger import swagger +from bson import objectid class GenericDeployResultHandler(result_handlers.GenericResultHandler): @@ -113,3 +114,17 @@ class DeployResultsHandler(GenericDeployResultHandler): self._create(miss_fields=miss_fields, carriers=carriers, values_check=values_check) + + +class DeployResultHandler(GenericDeployResultHandler): + @swagger.operation(nickname='getTestDeployResultById') + def get(self, result_id): + """ + @description: get a single deploy result by result_id + @rtype: L{DeployResult} + @return 200: Deploy result exist + @raise 404: Deploy result not exist + """ + query = dict() + query["_id"] = objectid.ObjectId(result_id) + self._get_one(query=query) diff --git a/testapi/opnfv_testapi/handlers/result_handlers.py b/testapi/opnfv_testapi/handlers/result_handlers.py index b0691cd..edcac6e 100644 --- a/testapi/opnfv_testapi/handlers/result_handlers.py +++ b/testapi/opnfv_testapi/handlers/result_handlers.py @@ -8,12 +8,9 @@ ############################################################################## from datetime import datetime from datetime import timedelta -import json -import logging from bson import objectid -from opnfv_testapi.common import constants from opnfv_testapi.common import message from opnfv_testapi.common import raises from opnfv_testapi.common.config import CONF @@ -41,7 +38,6 @@ class GenericResultHandler(base_handlers.GenericApiHandler): query = dict() date_range = dict() - query['public'] = {'$not': {'$eq': 'false'}} for k in self.request.query_arguments.keys(): v = self.get_query_argument(k) if k == 'project' or k == 'pod' or k == 'case': @@ -52,21 +48,12 @@ class GenericResultHandler(base_handlers.GenericApiHandler): period = datetime.now() - timedelta(days=v) obj = {"$gte": str(period)} query['start_date'] = obj - elif k == 'trust_indicator': - query[k + '.current'] = float(v) elif k == 'from': date_range.update({'$gte': str(v)}) elif k == 'to': date_range.update({'$lt': str(v)}) elif 'build_id' in k: query[k] = self.get_int(k, v) - elif k == 'signed': - username = self.get_secure_cookie(constants.TESTAPI_ID) - role = self.get_secure_cookie(constants.ROLE) - if role: - del query['public'] - if role != "reviewer": - query['user'] = username elif k not in ['last', 'page', 'descend']: query[k] = v if date_range: @@ -119,12 +106,9 @@ class ResultsCLHandler(GenericResultHandler): - to : ending time in 2016-01-01 or 2016-01-01 00:01:23 - scenario : the test scenario (previously version) - criteria : the global criteria status passed or failed - - trust_indicator : evaluate the stability of the test case - to avoid running systematically long and stable test case - - signed : get logined user result GET /results/project=functest&case=vPing&version=Arno-R1 \ - &pod=pod_name&period=15&signed + &pod=pod_name&period=15 @return 200: all test results consist with query, empty list if no result is found @rtype: L{TestResults} @@ -180,14 +164,6 @@ class ResultsCLHandler(GenericResultHandler): @type page: L{int} @in page: query @required page: False - @param trust_indicator: must be float - @type trust_indicator: L{float} - @in trust_indicator: query - @required trust_indicator: False - @param signed: user results or all results - @type signed: L{string} - @in signed: query - @required signed: False @param descend: true, newest2oldest; false, oldest2newest @type descend: L{string} @in descend: query @@ -231,36 +207,8 @@ class ResultsCLHandler(GenericResultHandler): values_check=values_check) -class ResultsUploadHandler(ResultsCLHandler): - @swagger.operation(nickname="uploadTestResult") - def post(self): - """ - @description: upload and create a test result - @param body: result to be created - @type body: L{ResultCreateRequest} - @in body: body - @rtype: L{CreateResponse} - @return 200: result is created. - @raise 404: pod/project/testcase not exist - @raise 400: body/pod_name/project_name/case_name not provided - """ - logging.info('file upload') - fileinfo = self.request.files['file'][0] - is_public = self.get_body_argument('public') - logging.warning('public:%s', is_public) - logging.info('results is :%s', fileinfo['filename']) - logging.info('results is :%s', fileinfo['body']) - self.json_args = json.loads(fileinfo['body']).copy() - self.json_args['public'] = is_public - - openid = self.get_secure_cookie(constants.TESTAPI_ID) - if openid: - self.json_args['user'] = openid - - super(ResultsUploadHandler, self).post() - - class ResultsGURHandler(GenericResultHandler): + @swagger.operation(nickname='getTestResultById') def get(self, result_id): """ @@ -272,19 +220,3 @@ class ResultsGURHandler(GenericResultHandler): query = dict() query["_id"] = objectid.ObjectId(result_id) self._get_one(query=query) - - @swagger.operation(nickname="updateTestResultById") - def put(self, result_id): - """ - @description: update a single result by _id - @param body: fields to be updated - @type body: L{ResultUpdateRequest} - @in body: body - @rtype: L{Result} - @return 200: update success - @raise 404: result not exist - @raise 403: nothing to update - """ - query = {'_id': objectid.ObjectId(result_id)} - db_keys = [] - self._update(query=query, db_keys=db_keys) diff --git a/testapi/opnfv_testapi/handlers/sign_handlers.py b/testapi/opnfv_testapi/handlers/sign_handlers.py index 7540662..2039971 100644 --- a/testapi/opnfv_testapi/handlers/sign_handlers.py +++ b/testapi/opnfv_testapi/handlers/sign_handlers.py @@ -46,7 +46,7 @@ class SigninReturnHandler(SignBaseHandler): dbapi.db_update(self.table, q_user, login_user) self.clear_cookie(constants.TESTAPI_ID) - self.set_secure_cookie(constants.TESTAPI_ID, user) + self.set_secure_cookie(constants.TESTAPI_ID, user, 1) self.redirect(url=CONF.ui_url) diff --git a/testapi/opnfv_testapi/models/pod_models.py b/testapi/opnfv_testapi/models/pod_models.py index 0eddfcc..8af5297 100644 --- a/testapi/opnfv_testapi/models/pod_models.py +++ b/testapi/opnfv_testapi/models/pod_models.py @@ -30,7 +30,7 @@ class Pod(PodCreateRequest): def __init__(self, **kwargs): self._id = kwargs.pop('_id', '') self.creation_date = kwargs.pop('creation_date', '') - self.owner = kwargs.pop('owner', '') + self.creator = kwargs.pop('creator', '') super(Pod, self).__init__(**kwargs) diff --git a/testapi/opnfv_testapi/models/project_models.py b/testapi/opnfv_testapi/models/project_models.py index 5f280f1..5c8c048 100644 --- a/testapi/opnfv_testapi/models/project_models.py +++ b/testapi/opnfv_testapi/models/project_models.py @@ -27,11 +27,16 @@ class ProjectUpdateRequest(base_models.ModelBase): @swagger.model() class Project(base_models.ModelBase): def __init__(self, - name=None, _id=None, description=None, create_date=None): + name=None, + creator='', + _id=None, + description=None, + creation_date=None): + self.creator = creator self._id = _id self.name = name self.description = description - self.creation_date = create_date + self.creation_date = creation_date @swagger.model() diff --git a/testapi/opnfv_testapi/models/result_models.py b/testapi/opnfv_testapi/models/result_models.py index 1dbe729..602318a 100644 --- a/testapi/opnfv_testapi/models/result_models.py +++ b/testapi/opnfv_testapi/models/result_models.py @@ -11,45 +11,7 @@ from opnfv_testapi.tornado_swagger import swagger @swagger.model() -class TIHistory(base_models.ModelBase): - """ - @ptype step: L{float} - """ - def __init__(self, date=None, step=0): - self.date = date - self.step = step - - -@swagger.model() -class TI(base_models.ModelBase): - """ - @property histories: trust_indicator update histories - @ptype histories: C{list} of L{TIHistory} - @ptype current: L{float} - """ - def __init__(self, current=0): - self.current = current - self.histories = list() - - def __eq__(self, other): - return (self.current == other.current and self._histories_eq(other)) - - def _histories_eq(self, other): - hs_equal = all(self.histories[i] == other.histories[i] - for i in range(len(self.histories))) - return len(self.histories) == len(other.histories) and hs_equal - - @staticmethod - def attr_parser(): - return {'histories': TIHistory} - - -@swagger.model() class ResultCreateRequest(base_models.ModelBase): - """ - @property trust_indicator: - @ptype trust_indicator: L{TI} - """ def __init__(self, pod_name=None, project_name=None, @@ -61,10 +23,7 @@ class ResultCreateRequest(base_models.ModelBase): details=None, build_tag=None, scenario=None, - criteria=None, - user=None, - public="true", - trust_indicator=None): + criteria=None): self.pod_name = pod_name self.project_name = project_name self.case_name = case_name @@ -76,37 +35,15 @@ class ResultCreateRequest(base_models.ModelBase): self.build_tag = build_tag self.scenario = scenario self.criteria = criteria - self.user = user - self.public = public - self.trust_indicator = trust_indicator if trust_indicator else TI(0) def __eq__(self, other): - simple_equal = all(getattr(self, k) == getattr(other, k) - for k in self.format().keys() - if k not in ['_id', 'trust_indicator']) - return simple_equal and self.trust_indicator == other.trust_indicator - - @staticmethod - def attr_parser(): - return {'trust_indicator': TI} - - -@swagger.model() -class ResultUpdateRequest(base_models.ModelBase): - """ - @property trust_indicator: - @ptype trust_indicator: L{TI} - """ - def __init__(self, trust_indicator=None): - self.trust_indicator = trust_indicator + return all(getattr(self, k) == getattr(other, k) + for k in self.format().keys() + if k not in ['_id']) @swagger.model() class TestResult(ResultCreateRequest): - """ - @property trust_indicator: used for long duration test case - @ptype trust_indicator: L{TI} - """ def __init__(self, _id=None, **kwargs): super(TestResult, self).__init__(**kwargs) self._id = _id diff --git a/testapi/opnfv_testapi/models/scenario_models.py b/testapi/opnfv_testapi/models/scenario_models.py index 0610c6b..537650e 100644 --- a/testapi/opnfv_testapi/models/scenario_models.py +++ b/testapi/opnfv_testapi/models/scenario_models.py @@ -178,10 +178,16 @@ class Scenario(base_models.ModelBase): @property installers: @ptype installers: C{list} of L{ScenarioInstaller} """ - def __init__(self, name='', create_date='', _id='', installers=None): + def __init__(self, + name='', + creation_date='', + _id='', + creator='', + installers=None): self.name = name + self.creator = creator self._id = _id - self.creation_date = create_date + self.creation_date = creation_date self.installers = list_default(installers) @staticmethod diff --git a/testapi/opnfv_testapi/router/url_mappings.py b/testapi/opnfv_testapi/router/url_mappings.py index a857725..8f01a66 100644 --- a/testapi/opnfv_testapi/router/url_mappings.py +++ b/testapi/opnfv_testapi/router/url_mappings.py @@ -49,9 +49,9 @@ mappings = [ # Push results with mandatory request payload parameters # (project, case, and pod) (r"/api/v1/results", result_handlers.ResultsCLHandler), - (r'/api/v1/results/upload', result_handlers.ResultsUploadHandler), (r"/api/v1/results/([^/]+)", result_handlers.ResultsGURHandler), (r"/api/v1/deployresults", deploy_handlers.DeployResultsHandler), + (r"/api/v1/deployresults/([^/]+)", deploy_handlers.DeployResultHandler), # scenarios (r"/api/v1/scenarios", scenario_handlers.ScenariosCLHandler), diff --git a/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js new file mode 100644 index 0000000..6b9a2bf --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/authenticateFalseSpec.js @@ -0,0 +1,540 @@ + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000/"; + + +describe('testing the pods page for anonymous user', function () { + beforeEach(function(){ + mock([{ + request: { + path: '/api/v1/pods', + method: 'GET' + }, + 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"}] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it('Delete button is visible for anonymous user', function () { + var fs = require('fs') + var m = JSON.parse(fs.readFileSync('testapi-ui/config.json').toString()); + m.authenticate = false; + fs.writeFile('testapi-ui/config.json', JSON.stringify(m, null, 2)) + browser.restart(); + browser.get(baseURL+'#/pods'); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true) + }); + + it('Delete button is visible for anonymous user', function () { + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true) + }); + + it('delete Operation is visible for user ', function () { + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); +}); + +describe('testing the project page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data: { + "projects": [ + { + "owner": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it('create button is visible for user', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('delete Operation is visible for user ', function () { + browser.get(baseURL+'#/projects'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('Edit Operation is visible for user ', function () { + browser.get(baseURL+'#/projects'); + var editOperation = element(by.css('a[title=Edit]')); + expect(editOperation.isDisplayed()).toBe(true); + }); +}); + + +describe('testing the scenarios page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/scenarios', + method: 'GET' + }, + response: { + data: { + "scenarios": [ + { + "installers": [ + { + "installer": "fuel", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "dvs" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + }, + { + "project": "functest", + "customs": [ + "vping_ssh", + "vping_userdata", + ], + "scores": [ + { + "date": "2016-12-09 11:28", + "score": "6/8" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + }, + { + "project": "sla", + "customs": [], + "scores": [ + { + "date": "2018-01-16T18:30:00.000Z", + "score": "sdS" + } + ], + "trust_indicators": [] + }, + { + "project": "dvsd", + "customs": [], + "scores": [], + "trust_indicators": [] + } + ] + }, + { + "owner": "dfgvds", + "version": "df", + "projects": [] + } + ] + }, + { + "installer": "fuel2", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "tc002", + "tc005", + "tc010", + "tc011" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + }, + { + "project": "functest", + "customs": [ + "vping_ssh", + "vping_userdata" + ], + "scores": [ + { + "date": "2016-12-09 11:28", + "score": "6/8" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + } + ] + } + ] + } + ], + "_id": "5a50fcacsdgdsgdasgfvb861c", + "name": "test-scenario", + "creation_date": "2018-01-06 22:13:24.160407" + } + ] + } + } + } + ]) + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the add installer option 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[5]/td[2]/div[2]/button')) + expect(buttonAdd.isDisplayed()).toBe(true); + }); + + it( 'should show the delete installer option for 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[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[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[3]/button')) + expect(installerDelete.isDisplayed()).toBe(true); + }); + + it( 'should show the add version option for 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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[2]/button')) + versionAdd.click() + expect(versionAdd.isDisplayed()).toBe(true); + }); + + it( 'should show the delete version option for 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[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[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[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[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[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); + }); + + it( 'should show the add project option for 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[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[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[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[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[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); + }); + + it( 'should show the delete project option for 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[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[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[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[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[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[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); + }); + + it( 'should show the add customs option for 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[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[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[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[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[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[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[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[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); + }); + + it( 'should show the delete customs option for 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[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[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[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[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[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[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[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[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); + }); +}); + + +describe('testing the testCases page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data: { + "projects": [ + { + "creator": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject', + method: 'GET' + }, + response: { + data: { + "creator": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases', + method: 'GET' + }, + response: { + data: { + "testcases": [ + { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/projects/testproject/cases/testCase', + method: 'GET' + }, + response: { + data: { + "project_name": "testproject", + "run": null, + "description": null, + "tags": null, + "creation_date": "2017-12-20 18:47:04.025544", + "dependencies": null, + "tier": null, + "trust": null, + "blocking": null, + "name": "testCase", + "ci_loop": null, + "url": null, + "version": null, + "criteria": null, + "domains": null, + "_id": "5a3a62d09a07c836e06858fb", + "catalog_description": null + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the testCases for anonymous user', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var row = element.all(by.repeater('(index, testcase) in testCasesCtrl.data.testcases')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("testCase"); + }); + + it( 'navigate anonymous user to testCase page', function() { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var testCase = element(by.linkText('testCase')); + testCase.click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/projects/testproject/testCase'), 10000); + }); + + it('create button is visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('Delete button is not visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true); + }); + + it('delete Operation is visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('Edit Operation is visible for anonymous user ', function () { + browser.get(baseURL+"#/projects/testproject"); + var testCases = element(by.linkText('Test Cases')); + testCases.click(); + var editOperation = element(by.css('a[title=Edit]')); + expect(editOperation.isDisplayed()).toBe(true); + }); +}); + + diff --git a/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js new file mode 100644 index 0000000..40f60e4 --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js @@ -0,0 +1,397 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000/" + +describe('testing the result page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/pods', + method: 'GET' + }, + 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"}] + } + } + }, + { + request: { + path: '/api/v1/deployresults', + method: 'GET', + queryString: { + page: '1' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "deployresults": [ + { + "build_id": 411, + "upstream_build_id": 184, + "scenario": "os-nosdn-nofeature-ha", + "stop_date": "2018-01-2723:21:31.3N", + "start_date": "2018-01-2723:21:28.3N", + "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master", + "version": "master", + "pod_name": "zte-pod", + "criteria": "PASS", + "installer": "daisy", + "_id": "5a6dc1089a07c80f3c9f8d62", + "job_name": "daisy-deploy-baremetal-daily-master", + "details": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/deployresults', + method: 'GET', + queryString: { + page: '1', + installer: 'daisy' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "deployresults": [ + { + "build_id": 411, + "upstream_build_id": 184, + "scenario": "os-nosdn-nofeature-ha", + "stop_date": "2018-01-2723:21:31.3N", + "start_date": "2018-01-2723:21:28.3N", + "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master", + "version": "master", + "pod_name": "zte-pod", + "criteria": "PASS", + "installer": "daisy", + "_id": "5a6dc1089a07c80f3c9f8d63", + "job_name": "daisy-deploy-baremetal-daily-master", + "details": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/deployresults', + method: 'GET', + queryString: { + page: '1', + installer: 'daisy', + job_name: 'daisy-deploy-baremetal-daily-master' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "deployresults": [ + { + "build_id": 411, + "upstream_build_id": 184, + "scenario": "os-nosdn-nofeature-ha", + "stop_date": "2018-01-2723:21:31.3N", + "start_date": "2018-01-2723:21:28.3N", + "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master", + "version": "master", + "pod_name": "zte-pod", + "criteria": "PASS", + "installer": "daisy", + "_id": "5a6dc1089a07c80f3c9f8d64", + "job_name": "daisy-deploy-baremetal-daily-master", + "details": null + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the deploy results page for anonymous user', function() { + browser.get(baseURL+"#/deployresults"); + expect(element(by.cssContainingText(".ng-binding.ng-scope","Deploy Results")).isDisplayed()).toBe(true); + }); + + it( 'navigate anonymous user to results page', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Deploy Results')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/deployresults'), 10000); + }); + + it('Should show the results in results page for anonymous user ', function () { + browser.get(baseURL+"#/deployresults"); + 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"); + }); + + it('Should show the results in results page related to the filters for anonymous user ', function () { + browser.get(baseURL+"#/deployresults"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('installer'); + filterText.sendKeys('daisy'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.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("3c9f8d63"); + filter.sendKeys('job_name'); + filterText.sendKeys('daisy-deploy-baremetal-daily-master') + buttonFilter.click(); + expect(cells.get(0).getText()).toContain("3c9f8d64"); + }); + it('Should not show the results in results page related to the filters for anonymous user ', function () { + browser.get(baseURL+"#/deployresults"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('installer'); + filterText.sendKeys('daisyl'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(true); + }); + +}); + +describe('testing the result page for user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/pods', + method: 'GET' + }, + 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"}] + } + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/deployresults', + method: 'GET', + queryString: { + page: '1' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "deployresults": [ + { + "build_id": 411, + "upstream_build_id": 184, + "scenario": "os-nosdn-nofeature-ha", + "stop_date": "2018-01-2723:21:31.3N", + "start_date": "2018-01-2723:21:28.3N", + "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master", + "version": "master", + "pod_name": "zte-pod", + "criteria": "PASS", + "installer": "daisy", + "_id": "5a6dc1089a07c80f3c9f8d62", + "job_name": "daisy-deploy-baremetal-daily-master", + "details": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/deployresults', + method: 'GET', + queryString: { + page: '1', + installer: 'daisy' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "deployresults": [ + { + "build_id": 411, + "upstream_build_id": 184, + "scenario": "os-nosdn-nofeature-ha", + "stop_date": "2018-01-2723:21:31.3N", + "start_date": "2018-01-2723:21:28.3N", + "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master", + "version": "master", + "pod_name": "zte-pod", + "criteria": "PASS", + "installer": "daisy", + "_id": "5a6dc1089a07c80f3c9f8d63", + "job_name": "daisy-deploy-baremetal-daily-master", + "details": null + } + ] + } + } + }, + { + request: { + path: '/api/v1/deployresults', + method: 'GET', + queryString: { + page: '1', + installer: 'daisy', + job_name : 'daisy-deploy-baremetal-daily-master' + } + }, + response: { + data: { + "pagination": { + "current_page": 1, + "total_pages": 1 + }, + "deployresults": [ + { + "build_id": 411, + "upstream_build_id": 184, + "scenario": "os-nosdn-nofeature-ha", + "stop_date": "2018-01-2723:21:31.3N", + "start_date": "2018-01-2723:21:28.3N", + "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master", + "version": "master", + "pod_name": "zte-pod", + "criteria": "PASS", + "installer": "daisy", + "_id": "5a6dc1089a07c80f3c9f8d64", + "job_name": "daisy-deploy-baremetal-daily-master", + "details": null + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the deploy results page for user', function() { + browser.get(baseURL+"#/deployresults"); + expect(element(by.cssContainingText(".ng-binding.ng-scope","Deploy Results")).isDisplayed()).toBe(true); + }); + + it( 'navigate user to results page', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Deploy Results')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/deployresults'), 10000); + }); + + it('Should show the results in results page for user ', function () { + browser.get(baseURL+"#/deployresults"); + 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"); + }); + + it('Should show the results in results page related to the filters for user ', function () { + browser.get(baseURL+"#/deployresults"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('installer'); + filterText.sendKeys('daisy'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.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("3c9f8d63"); + filter.sendKeys('job_name'); + filterText.sendKeys('daisy-deploy-baremetal-daily-master') + buttonFilter.click(); + expect(cells.get(0).getText()).toContain("3c9f8d64"); + }); + it('Should not show the results in results page related to the filters for user ', function () { + browser.get(baseURL+"#/deployresults"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('installer'); + filterText.sendKeys('daisy1'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) + .isDisplayed()).toBe(true); + }); + + it('Clear the filter', function () { + browser.get(baseURL+"#/deployresults"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('installer'); + filterText.sendKeys('daisy'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.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("3c9f8d63"); + var buttonClear = element(by.buttonText('Clear')); + 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("3c9f8d63"); + }); + + it('view the deploy results ', function () { + browser.get(baseURL+"#/deployresults"); + var viewOperation = element(by.linkText('3c9f8d62')) + viewOperation.click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains('#/deployresults/5a6dc1089a07c80f3c9f8d62'), 10000); + }); + +});
\ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/homeControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/homeControllerSpec.js new file mode 100644 index 0000000..013527d --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/homeControllerSpec.js @@ -0,0 +1,56 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000" +describe('testing the home page for anonymous user', function () { + + it( 'should navigate to pods link ', function() { + browser.get(baseURL); + var signOut = element(by.linkText('Sign In / Sign Up')) + expect(signOut.isDisplayed()).toBe(true); + }); +}); + +describe('testing the home page for user', function () { + beforeEach(function(){ + mock([{ + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + data: { + pods: [{role: "community-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"}] + } + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com" + } + } + }]) + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the profile page', function() { + browser.get(baseURL); + var profile = element(by.linkText('Profile')) + expect(profile.isDisplayed()).toBe(true); + profile.click() + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/profile'), 10000); + }); +});
\ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js index c3961ab..ac1f954 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/podsControllerSpec.js @@ -4,202 +4,418 @@ var mock = require('protractor-http-mock'); var baseURL = "http://localhost:8000" describe('testing the Pods page for anonymous user', function () { - beforeEach(function(){ - mock([{ - request: { - path: '/api/v1/pods', - method: 'GET' - }, - 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"}] - } - } - }]); - }); - - afterEach(function(){ - mock.teardown(); - }); - - it( 'should navigate to pods link ', function() { - browser.get(baseURL); - var podslink = element(by.linkText('Pods')).click(); - var EC = browser.ExpectedConditions; - browser.wait(EC.urlContains(baseURL+ '/#/pods'), 10000); - }); - - it('create button is not visible for anonymous user', function () { - browser.get(baseURL+'#/pods'); - var buttonCreate = element(by.buttonText('Create')); - expect(buttonCreate.isDisplayed()).toBeFalsy(); - }); - - it('filter button is visible for anonymous user', function () { - var buttonFilter = element(by.buttonText('Filter')); - expect(buttonFilter.isDisplayed()).toBe(true) - }); - - it('clear button is visible for anonymous user', function () { - var buttonClear = element(by.buttonText('Clear')); - expect(buttonClear.isDisplayed()).toBe(true) - }); - - it('Show results when click filter button', function () { - var buttonFilter = element(by.buttonText('Filter')); - buttonFilter.click(); - var pod = element(by.css('.show-pod')); - expect(pod.isPresent()).toBe(true); - }); - - it('Show results when click clear button', function () { - browser.get(baseURL+'#/pods'); - var buttonClear = element(by.buttonText('Clear')); - buttonClear.click(); - var pod = element(by.css('.show-pod')); - expect(pod.isPresent()).toBe(true); - }); - - it('If details is not shown then show details when click the link',function() { - expect(element(by.css('.show-pod.hidden')).isPresent()).toBe(true); - var podslink = element(by.linkText('test')).click(); - expect(element(by.css('.show-pod.hidden')).isPresent()).toBe(false); - }); - - it('If details is shown then hide details when click the link',function() { - expect(element(by.css('.show-pod.hidden')).isPresent()).toBe(false); - var podslink = element(by.linkText('test')).click(); - expect(element(by.css('.show-pod.hidden')).isPresent()).toBe(true); - }); - - it('If backend is not responding then show error when click filter button', function () { - browser.get(baseURL + '/#/pods'); - 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); - }); - }); + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/pods', + method: 'GET', + queryString: { + name: 'test' + } + }, + response: { + data: { + pods: [{role: "community-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"}] + } + } + }, + { + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + data: { + pods: [ + {role: "community-ci", name: "test2", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7ae5", + creation_date: "2017-10-25 11:58:25.926168"}, + {role: "production-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "virtual", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"} + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should navigate to pods link ', function() { + browser.get(baseURL); + var podslink = element(by.linkText('Pods')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/pods'), 10000); + }); + + it('create button is not visible for anonymous user', function () { + browser.get(baseURL+'#/pods'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + + it('filter button is visible for anonymous user', function () { + var buttonFilter = element(by.buttonText('Filter')); + expect(buttonFilter.isDisplayed()).toBe(true) + }); + + it('Delete button is visible for anonymous user', function () { + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBeFalsy(); + }); + + it('Show results in a sorted order', function () { + var row = element.all(by.repeater('(index, pod) in ctrl.data.pods')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test"); + }); + + it('Sort the results by mode', function () { + browser.get(baseURL+'#/pods'); + 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')); + expect(cells.get(1).getText()).toContain("test2"); + }); + + it('Sort the results by role', function () { + browser.get(baseURL+'#/pods'); + 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')); + expect(cells.get(1).getText()).toContain("test2"); + }); + + it('Show relevant results to the filter', function () { + var filter = element(by.model('ctrl.filterText')); + filter.sendKeys('test'); + var buttonFilter = element(by.buttonText('Filter')); + var row = element.all(by.repeater('(index, pod) in ctrl.data.pods')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test"); + }); + + it('delete Operation is not visible for user ', function () { + browser.get(baseURL+'#/pods'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBeFalsy(); + }); + }); describe('testing the Pods page for authorized user', function () { - beforeEach(function(){ - mock([ - { - request: { - path: '/api/v1/pods', - method: 'POST' - }, - response: { - data: { - href: baseURL+"/api/v1/pods/test" - } - } - }, - { - request: { - path: '/api/v1/pods', - method: 'POST', - data: { - name: 'test1', - details : 'DemoDetails', - role : 'community-ci', - mode : 'metal' - } - }, - response: { - status : 403 - } - }, - { - request: { - path: '/api/v1/profile', - method: 'GET' - }, - response: { - data: { - "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", - "user": "testUser", "groups": ["opnfv-testapi-users", - "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com" - } - } - } - ]); - }); - - afterEach(function(){ - mock.teardown(); - }); - - it('create button is visible for authorized user', function () { - browser.get(baseURL + '/#/pods'); - var buttonCreate = element(by.buttonText('Create')); - expect(buttonCreate.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.details')); - name.sendKeys('test'); - 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); - }); - }); - - it('Show error when user click the create button with a empty name', function () { - browser.get(baseURL+ '/#/pods'); - var details = element(by.model('ctrl.details')); - details.sendKeys('DemoDetails'); - 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+ '/#/pods'); - var name = element(by.model('ctrl.name')); - var details = element(by.model('ctrl.details')); - name.sendKeys('test1'); - details.sendKeys('DemoDetails'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click(); - expect(element(by.cssContainingText(".alert","Error creating the new pod from server: undefined")).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"], - "email": "testuser@test.com" - } - } - } - ]); - browser.get(baseURL+ '/#/pods'); - var name = element(by.model('ctrl.name')); - var details = element(by.model('ctrl.details')); - name.sendKeys('test'); - details.sendKeys('DemoDetails'); - var buttonCreate = element(by.buttonText('Create')); - buttonCreate.click().then(function(){ - expect(element(by.css('.alert.alert-danger.ng-binding')) - .isDisplayed()).toBe(true); - }); - }); + beforeEach(function(){ + mock([{ + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + data: { + pods: [{role: "community-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"}] + } + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/pods', + method: 'GET', + queryString: { + name: 'test' + } + }, + response: { + data: { + pods: [{role: "community-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"}] + } + } + }, + { + request: { + path: '/api/v1/pods/test', + method: 'DELETE' + }, + response: { + data: { + href: baseURL+"/api/v1/pods/test" + } + } + }, + { + request: { + path: '/api/v1/pods/test1', + method: 'DELETE' + }, + response: { + data: { + href: baseURL+"/api/v1/pods/test1" + } + } + }, + { + request: { + path: '/api/v1/pods', + method: 'POST' + }, + response: { + data: { + href: baseURL+"/api/v1/pods/test1" + } + } + }, + { + request: { + path: '/api/v1/pods/test', + method: 'GET' + }, + response: { + data: {role: "community-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"} + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should navigate to pods link ', function() { + browser.get(baseURL); + var podslink = element(by.linkText('Pods')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '/#/pods'), 10000); + }); + + it('create button is not visible for user', function () { + browser.get(baseURL+'#/pods'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('filter button is visible for user', function () { + var buttonFilter = element(by.buttonText('Filter')); + expect(buttonFilter.isDisplayed()).toBe(true) + }); + + it('Delete button is visible for user', function () { + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true) + }); + + it('Show results', function () { + var row = element.all(by.repeater('(index, pod) in ctrl.data.pods')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test"); + }); + + it('Show relevant results to the filter', function () { + var filter = element(by.model('ctrl.filterText')); + filter.sendKeys('test'); + var buttonFilter = element(by.buttonText('Filter')); + var row = element.all(by.repeater('(index, pod) in ctrl.data.pods')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test"); + }); + + it('delete Operation is visible for user ', function () { + browser.get(baseURL+'#/pods'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('Batch Delete the pods ', function () { + browser.get(baseURL+"#/pods"); + var checkBox = element(by.model('ctrl.checkBox[index]')); + checkBox.click(); + var buttonDelete = element(by.buttonText('Delete'));; + buttonDelete.click(); + expect(element(by.cssContainingText('label', "You are about to delete following pods : test")) + .isDisplayed()).toBe(true); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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 () { + browser.get(baseURL+"#/pods"); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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 () { + browser.get(baseURL+"#/pods"); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('PodModalCtrl.pod.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test1'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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 () { + browser.get(baseURL+"#/pods"); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('PodModalCtrl.pod.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click() + 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 () { + browser.get(baseURL+"#/pods"); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonCancel = element(by.buttonText('Cancel')); + buttonCancel.click(); + expect(buttonCancel.isPresent()).toBe(false); + }); + + it('Delete the pods which do not exist ', function () { + mock.teardown(); + mock([{ + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + data: { + pods: [{role: "community-ci", name: "test1", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"}] + } + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/pods/test1', + method: 'DELETE' + }, + response: { + status : 403, + data : 'pods do not exist' + } + } + ]); + browser.get(baseURL+"#/pods"); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.css(".error.show")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('view the test case ', function () { + browser.get(baseURL+"#/pods"); + var viewOperation = element(by.linkText('test')); + viewOperation.click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains('#/pods/test'), 10000); + }); + + it('Show error if server is not responding', function () { + mock.teardown(); + mock([{ + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + status : 404 + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users", + "opnfv-gerrit-functest-submitters"], "email": "testuser@test.com" + } + } + }, + ]); + browser.get(baseURL+"#/pods"); + 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/projectControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js deleted file mode 100644 index 475e037..0000000 --- a/testapi/opnfv_testapi/tests/UI/e2e/projectControllerSpec.js +++ /dev/null @@ -1,354 +0,0 @@ -'use strict'; - -var mock = require('protractor-http-mock'); -var baseURL = "http://localhost:8000/#/" - -describe('testing the Project Link for anonymous user', function () { - 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" - } - } - } - ]); - }); - - 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 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('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(not a submitter)', function () { - 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" - } - } - }, - { - 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/projects/testproject', - method: 'GET' - }, - response: { - data: { - "owner": "thuva4", - "_id": "5a0c022f9a07c846d3c2cc94", - "creation_date": "2017-11-15 14:30:31.200259", - "description": "dsfsd", - "name": "testproject" - } - } - }, - { - request: { - path: '/api/v1/projects/testproject1', - method: 'GET' - }, - response: { - data: { - "owner": "thuva4", - "_id": "5a0c022f9a07c846d3c2cc94", - "creation_date": "2017-11-15 14:30:31.200259", - "description": "dsfsd", - "name": "testproject1" - } - } - }, - { - request: { - path: '/api/v1/projects', - method: 'GET' - }, - response: { - data: { - "projects": [ - { - "owner": "thuva4", - "_id": "5a0c022f9a07c846d3c2cc94", - "creation_date": "2017-11-15 14:30:31.200259", - "description": "dsfsd", - "name": "testproject" - } - ] - } - } - }, - { - request: { - path: '/api/v1/projects/testproject', - method: '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 : 200, - data : { - "owner": "thuva4", - "_id": "5a0c022f9a07c846d3c2cc94", - "creation_date": "2017-11-15 14:30:31.200259", - "description": "dsfsd", - "name": "testproject2" - } - } - }, - { - request: { - path: '/api/v1/projects/testproject', - method: 'PUT', - data: { - name: 'testProject1', - description : 'demoDescription', - } - }, - response: { - 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" - } - } - }, - ]); - }); - - 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 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 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('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 updating the existing Project from server: undefined")) - .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('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 index 64a5aeb..da86389 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/projectsControllerSpec.js @@ -15,7 +15,7 @@ describe('testing the Projects Link for anonymous user', function () { data: { "projects": [ { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -28,13 +28,13 @@ describe('testing the Projects Link for anonymous user', function () { ]); }); - afterEach(function(){ - mock.teardown(); - }); + afterEach(function(){ + mock.teardown(); + }); - it( 'should show the Projects Link for anonymous user', function() { + it( 'should show the Projects Link for anonymous user', function() { mock.teardown(); - browser.get(baseURL); + browser.get(baseURL); var projectslink = element(by.linkText('Projects')); expect(projectslink.isPresent()).toBe(true); }); @@ -51,16 +51,22 @@ describe('testing the Projects Link for anonymous user', function () { 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('Delete button is not visible for anonymous user ', function () { + browser.get(baseURL+'#/projects'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); }); - 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); + it('Show projects list when anonymous user comes to the projects page', function () { + var row = element.all(by.repeater('(index, project) in ctrl.data.projects')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("testproject"); + }); + + it('delete Operation is not visible for anonymous user ', function () { + browser.get(baseURL+'#/projects'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBeFalsy(); }); }); @@ -70,6 +76,26 @@ describe('testing the Project Link for user who is not in submitter group', func mock([ { request: { + path: '/api/v1/projects', + method: 'GET', + queryString: { + name: 'test' + } + }, + response: { + data: { + "projects": [ + { + "_id": "5a0c1c9a9a07c846d3a7247b", + "creation_date": "2017-11-15 16:23:14.217093", + "description": "sdgfd", + "name": "test" + }] + } + } + }, + { + request: { path: '/api/v1/profile', method: 'GET' }, @@ -80,13 +106,32 @@ describe('testing the Project Link for user who is not in submitter group', func "email": "testuser@test.com" } } + }, + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data: { + "projects": [ + { + "creator": "thuva4", + "_id": "5a0c022f9a07c846d3c2cc94", + "creation_date": "2017-11-15 14:30:31.200259", + "description": "dsfsd", + "name": "testproject" + } + ] + } + } } ]); }); afterEach(function(){ - mock.teardown(); - }); + mock.teardown(); + }); it( 'should show the Project Link for user', function() { browser.get(baseURL); @@ -106,6 +151,28 @@ describe('testing the Project Link for user who is not in submitter group', func var buttonCreate = element(by.buttonText('Create')); expect(buttonCreate.isDisplayed()).toBeFalsy(); }); + + it('Delete button is not visible for user ', function () { + browser.get(baseURL+'#/projects'); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBeFalsy(); + }); + + it('delete Operation is not visible for user ', function () { + browser.get(baseURL+'#/projects'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBeFalsy(); + }); + + it('Show relevant results to the filter', function () { + var filter = element(by.model('ctrl.filterText')); + filter.sendKeys('test'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click() + var row = element.all(by.repeater('(index, project) in ctrl.data.projects')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test"); + }); }) describe('testing the Project Link for user who is in submitter group', function () { @@ -139,6 +206,28 @@ describe('testing the Project Link for user who is in submitter group', function }, { request: { + path: '/api/v1/projects/vsfv', + method: 'DELETE' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject1" + } + } + }, + { + request: { + path: '/api/v1/projects/vsfv', + method: 'PUT' + }, + response: { + data: { + href: baseURL+"/api/v1/projects/testProject1" + } + } + }, + { + request: { path: '/api/v1/projects', method: 'POST', data: { @@ -163,13 +252,30 @@ describe('testing the Project Link for user who is in submitter group', function status : 403, data : 'You do not have permission to perform this action' } + }, + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data : { + "projects": [ + { + "_id": "5a0c1c9a9a07c846d3a7247b", + "creation_date": "2017-11-15 16:23:14.217093", + "description": "sdgfd", + "name": "vsfv" + }] + } + } } ]); }); afterEach(function(){ - mock.teardown(); - }); + mock.teardown(); + }); it( 'should show the Project Link for user', function() { browser.get(baseURL); @@ -190,58 +296,121 @@ describe('testing the Project Link for user who is in submitter group', function 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.")) + it('Delete button is visible for user ', function () { + browser.get(baseURL+'#/projects'); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true); + }); + + it('delete Operation is visible for user ', function () { + browser.get(baseURL+'#/projects'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('Edit Operation is visible for user ', function () { + browser.get(baseURL+'#/projects'); + var editOperation = element(by.css('a[title=Edit]')); + expect(editOperation.isDisplayed()).toBe(true); + }); + + it('Create the Project', function () { + browser.get(baseURL+"#/projects"); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('testproject'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Project is successfully created.")) .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")) + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('Show error if user doesnt have permission to Create the Project', function () { + browser.get(baseURL+"#/projects"); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + var description = element(by.model('ProjectModalCtrl.project.description')); + name.sendKeys('testProject2'); + description.sendKeys('demoDescription'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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 () { + browser.get(baseURL+"#/projects"); + var buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + 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 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')); + it('Show error when user click the create button with an already existing name', function () { + browser.get(baseURL+"#/projects"); + 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); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + var description = element(by.model('ProjectModalCtrl.project.description')); + name.sendKeys('testProject3'); + description.sendKeys('demoDescription'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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 () { + browser.get(baseURL+"#/projects"); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonCancel = element(by.buttonText('Cancel')); + buttonCancel.click(); + expect(buttonCancel.isPresent()).toBe(false); }); - 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('Delete the projects ', function () { + browser.get(baseURL+"#/projects"); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + expect(element(by.cssContainingText('label', "You are about to delete following projects : vsfv")) + .isDisplayed()).toBe(true); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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(){ - mock.teardown(); - mock([ + it(' Show error if user doesnt has permission to delete the projects ', function () { + mock.teardown(); + mock([ { request: { path: '/api/v1/profile', @@ -256,16 +425,185 @@ describe('testing the Project Link for user who is in submitter group', function "email": "testuser@test.com" } } + }, + { + request: { + path: '/api/v1/projects/testproject3', + method: 'DELETE' + }, + response: { + status: 403 + } + }, + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data : { + "projects": [ + { + "_id": "5a0c1c9a9a07c846d3a7247b", + "creation_date": "2017-11-15 16:23:14.217093", + "description": "sdgfd", + "name": "testproject3" + }] + } + } } - ]); - 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); - }); - }); + ]); + browser.get(baseURL+"#/projects"); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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 () { + browser.get(baseURL+"#/projects"); + var editOperation = element(by.css('a[title=Edit]')); + editOperation.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test1'); + var buttonCancel = element(by.buttonText('Cancel')); + buttonCancel.click(); + expect(name.isPresent()).toBe(false); + }); + + it('Edit the Project ', function () { + browser.get(baseURL+"#/projects"); + var editOperation = element(by.css('a[title=Edit]')); + editOperation.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test1'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click() + 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 () { + 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" + } + } + }, + { + request: { + path: '/api/v1/projects/testproject3', + method: 'PUT' + }, + response: { + status: 403 + } + }, + { + request: { + path: '/api/v1/projects', + method: 'GET' + }, + response: { + data : { + "projects": [ + { + "_id": "5a0c1c9a9a07c846d3a7247b", + "creation_date": "2017-11-15 16:23:14.217093", + "description": "sdgfd", + "name": "testproject3" + }] + } + } + } + ]); + browser.get(baseURL+"#/projects"); + var editOperation = element(by.css('a[title=Edit]')); + editOperation.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test1'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click() + browser.ignoreSynchronization = true; + expect(element(by.css(".error")).isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('Batch Delete the projects ', function () { + browser.get(baseURL+"#/projects"); + var checkBox = element(by.model('ctrl.checkBox[index]')); + checkBox.click(); + var buttonDelete = element(by.buttonText('Delete'));; + buttonDelete.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + 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(){ + 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 buttonCreate = element(by.buttonText('Create')); + buttonCreate.click(); + var name = element(by.model('ProjectModalCtrl.project.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + var details = element(by.model('ProjectModalCtrl.project.description')); + name.sendKeys('testproject'); + details.sendKeys('demoDescription'); + var buttonOK = element(by.buttonText('Ok')); + 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 a14f8ea..48690a1 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/resultsControllerSpec.js @@ -30,7 +30,6 @@ describe('testing the result page for anonymous user', function () { "user": null, "installer": "fuel", "scenario": "test-scenario", - "trust_indicator": null, "public": "true", "version": "euphrates", "details": "", @@ -45,11 +44,40 @@ describe('testing the result page for anonymous user', function () { }, { request: { + path: '/api/v1/results/5a45170bbb2092000e2643f4', + method: 'GET', + }, + response: { + data: { + "project_name": "testproject", + "description": "Demo results", + "stop_date": "2017-12-28 16:08:43", + "case_name": "testcase", + "build_tag": null, + "user": null, + "installer": "fuel", + "scenario": "test-scenario", + "public": "true", + "version": "euphrates", + "details": { + "failures": 0, + "errors": 0, + "stream": "steam text" + }, + "criteria": "PASS", + "_id": "5a45170bbb2092000e2643f4", + "start_date": "2017-12-28 14:44:27", + "pod_name": "testPod" + } + } + }, + { + request: { path: '/api/v1/results', method: 'GET', queryString: { page: '1', - project: 'testproject' + installer: 'testinstaller' } }, response: { @@ -66,7 +94,7 @@ describe('testing the result page for anonymous user', function () { "case_name": "testcase", "build_tag": null, "user": null, - "installer": "fuel", + "installer": "testinstaller", "scenario": "test-scenario", "trust_indicator": null, "public": "true", @@ -87,8 +115,8 @@ describe('testing the result page for anonymous user', function () { method: 'GET', queryString: { page: '1', - project: 'testproject', - case: 'testcase' + installer: 'testinstaller', + version: 'testversion' } }, response: { @@ -105,11 +133,10 @@ describe('testing the result page for anonymous user', function () { "case_name": "testcase", "build_tag": null, "user": null, - "installer": "fuel", + "installer": "testinstaller", "scenario": "test-scenario", - "trust_indicator": null, "public": "true", - "version": "euphrates", + "version": "testversion", "details": "", "criteria": "PASS", "_id": "5a45170bbb2092000e2643f6", @@ -119,7 +146,25 @@ describe('testing the result page for anonymous user', function () { ] } } - } + }, + { + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + data: { + pods: [ + {role: "community-ci", name: "test2", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7ae5", + creation_date: "2017-10-25 11:58:25.926168"}, + {role: "production-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "virtual", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"} + ] + } + } + } ]); }); @@ -127,47 +172,58 @@ describe('testing the result page for anonymous user', function () { mock.teardown(); }); - it( 'should show the results page for anonymous user', function() { + it( 'should show the results page ', function() { browser.get(baseURL+"#/results"); expect(element(by.cssContainingText(".ng-binding.ng-scope","Test Results")).isDisplayed()).toBe(true); }); - it( 'navigate anonymous user to testCase page', function() { + it( 'navigate anonymous user to results page', function() { browser.get(baseURL); var resultLink = element(by.linkText('Results')).click(); var EC = browser.ExpectedConditions; browser.wait(EC.urlContains(baseURL+ '#/results'), 10000); }); - it('Should show the results in results page for anonymous user ', function () { + it('Should show the results in results page', function () { browser.get(baseURL+"#/results"); var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); var cells = row.all(by.tagName('td')); - expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f4"); + expect(cells.get(0).getText()).toContain("0e2643f4"); + }); + + it( 'navigate to result page and check details', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Results')).click(); + var EC = browser.ExpectedConditions; + 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-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 for anonymous user ', function () { + it('Should show the results in results page related to the filters', function () { browser.get(baseURL+"#/results"); var filter = element(by.model('ctrl.filter')); var filterText = element(by.model('ctrl.filterText')); - filter.sendKeys('project'); - filterText.sendKeys('testproject'); + filter.sendKeys('installer'); + filterText.sendKeys('testinstaller'); var buttonFilter = element(by.buttonText('Filter')); buttonFilter.click(); var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); var cells = row.all(by.tagName('td')); - expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f5"); - filter.sendKeys('case'); - filterText.sendKeys('testcase') + expect(cells.get(0).getText()).toContain("0e2643f5"); + filter.sendKeys('version'); + filterText.sendKeys('testversion') buttonFilter.click(); - expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f6"); + expect(cells.get(0).getText()).toContain("0e2643f6"); }); it('Should not show the results in results page related to the filters for anonymous user ', function () { browser.get(baseURL+"#/results"); var filter = element(by.model('ctrl.filter')); var filterText = element(by.model('ctrl.filterText')); - filter.sendKeys('project'); - filterText.sendKeys('testproject1'); + filter.sendKeys('installer'); + filterText.sendKeys('testisntaller1'); var buttonFilter = element(by.buttonText('Filter')); buttonFilter.click(); expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) @@ -235,7 +291,7 @@ describe('testing the result page for user', function () { method: 'GET', queryString: { page: '1', - project: 'testproject' + installer: 'testinstaller' } }, response: { @@ -252,9 +308,8 @@ describe('testing the result page for user', function () { "case_name": "testcase", "build_tag": null, "user": null, - "installer": "fuel", + "installer": "testinstaller", "scenario": "test-scenario", - "trust_indicator": null, "public": "true", "version": "euphrates", "details": "", @@ -273,8 +328,8 @@ describe('testing the result page for user', function () { method: 'GET', queryString: { page: '1', - project: 'testproject', - case: 'testcase' + installer: 'testinstaller', + version: 'testversion' } }, response: { @@ -291,11 +346,11 @@ describe('testing the result page for user', function () { "case_name": "testcase", "build_tag": null, "user": null, - "installer": "fuel", + "installer": "testinstaller", "scenario": "test-scenario", "trust_indicator": null, "public": "true", - "version": "euphrates", + "version": "testversion", "details": "", "criteria": "PASS", "_id": "5a45170bbb2092000e2643f6", @@ -305,7 +360,25 @@ describe('testing the result page for user', function () { ] } } - } + }, + { + request: { + path: '/api/v1/pods', + method: 'GET' + }, + response: { + data: { + pods: [ + {role: "community-ci", name: "test2", creator: "testUser", + details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7ae5", + creation_date: "2017-10-25 11:58:25.926168"}, + {role: "production-ci", name: "test", creator: "testUser", + details: "DemoDetails", mode: "virtual", _id: "59f02f099a07c84bfc5c7aed", + creation_date: "2017-10-25 11:58:25.926168"} + ] + } + } + } ]); }); @@ -329,31 +402,49 @@ describe('testing the result page for user', function () { browser.get(baseURL+"#/results"); var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); var cells = row.all(by.tagName('td')); - expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f4"); + expect(cells.get(0).getText()).toContain("0e2643f4"); }); it('Should show the results in results page related to the filters for user ', function () { browser.get(baseURL+"#/results"); var filter = element(by.model('ctrl.filter')); var filterText = element(by.model('ctrl.filterText')); - filter.sendKeys('project'); - filterText.sendKeys('testproject'); + filter.sendKeys('installer'); + filterText.sendKeys('testinstaller'); var buttonFilter = element(by.buttonText('Filter')); buttonFilter.click(); var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); var cells = row.all(by.tagName('td')); - expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f5"); - filter.sendKeys('case'); - filterText.sendKeys('testcase') + expect(cells.get(0).getText()).toContain("0e2643f5"); + filter.sendKeys('version'); + filterText.sendKeys('testversion') buttonFilter.click(); - expect(cells.get(0).getText()).toContain("5a45170bbb2092000e2643f6"); + expect(cells.get(0).getText()).toContain("0e2643f6"); + }); + + it('Clear the filter', function () { + browser.get(baseURL+"#/results"); + var filter = element(by.model('ctrl.filter')); + var filterText = element(by.model('ctrl.filterText')); + filter.sendKeys('installer'); + filterText.sendKeys('testinstaller'); + var buttonFilter = element(by.buttonText('Filter')); + buttonFilter.click(); + var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(0).getText()).toContain("0e2643f5"); + var buttonClear = element(by.buttonText('Clear')); + buttonClear.click(); + var row = element.all(by.repeater('(index, result) in ctrl.data.results')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(0).getText()).toContain("0e2643f4"); }); it('Should not show the results in results page related to the filters for user ', function () { browser.get(baseURL+"#/results"); var filter = element(by.model('ctrl.filter')); var filterText = element(by.model('ctrl.filterText')); - filter.sendKeys('project'); - filterText.sendKeys('testproject1'); + filter.sendKeys('installer'); + filterText.sendKeys('testisntaller1'); var buttonFilter = element(by.buttonText('Filter')); buttonFilter.click(); expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope')) diff --git a/testapi/opnfv_testapi/tests/UI/e2e/scenarioControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/scenarioControllerSpec.js new file mode 100644 index 0000000..37b42dc --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/scenarioControllerSpec.js @@ -0,0 +1,1108 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000/" + +describe('testing the scenarios page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/scenarios', + method: 'GET' + }, + response: { + data: { + "scenarios": [ + { + "installers": [ + { + "installer": "fuel", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "dvs" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + }, + { + "date": "2016-12-15 05:28", + "score": "17/24" + }, + { + "date": "2016-12-17 03:41", + "score": "16/24" + }, + { + "date": "2018-01-22T18:30:00.000Z", + "score": "10/13" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + }, + { + "date": "2016-12-25 08:22", + "status": "gold" + }, + { + "date": "2018-01-22T18:30:00.000Z", + "status": "sf" + }, + { + "date": "2018-01-17T18:30:00.000Z", + "status": "df" + } + ] + }, + { + "project": "functest", + "customs": [ + "vping_ssh", + "vping_userdata", + ], + "scores": [ + { + "date": "2016-12-09 11:28", + "score": "6/8" + }, + { + "date": "2016-12-14 15:34", + "score": "8/8" + }, + { + "date": "2016-12-19 13:22", + "score": "8/8" + }, + { + "date": "2016-12-22 18:17", + "score": "8/8" + }, + { + "date": "2016-12-25 08:22", + "score": "8/8" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + }, + { + "project": "sla", + "customs": [], + "scores": [ + { + "date": "2018-01-16T18:30:00.000Z", + "score": "sdS" + } + ], + "trust_indicators": [] + }, + { + "project": "dvsd", + "customs": [], + "scores": [], + "trust_indicators": [] + } + ] + }, + { + "owner": "dfgvds", + "version": "df", + "projects": [] + } + ] + }, + { + "installer": "fuel2", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "tc002", + "tc005", + "tc010", + "tc011" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + }, + { + "date": "2016-12-15 05:28", + "score": "17/24" + }, + { + "date": "2016-12-17 03:41", + "score": "16/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + }, + { + "date": "2016-12-25 08:22", + "status": "gold" + } + ] + }, + { + "project": "functest", + "customs": [ + "vping_ssh", + "vping_userdata" + ], + "scores": [ + { + "date": "2016-12-09 11:28", + "score": "6/8" + }, + { + "date": "2016-12-14 15:34", + "score": "8/8" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + } + ] + } + ] + } + ], + "_id": "5a50fcacsdgdsgdasgfvb861c", + "name": "test-scenario", + "creation_date": "2018-01-06 22:13:24.160407" + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the scenarios page for anonymous user', function() { + browser.get(baseURL+"#/scenarios"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios'), 10000); + var row = element.all(by.repeater('(index, scenario) in ctrl.data.scenarios')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test-scenario"); + var scenarioLink = element(by.linkText('test-scenario')).click(); + browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000); + }); + + it( 'should not show the add installer option 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[5]/td[2]/div[2]/button')) + expect(buttonAdd.isDisplayed()).toBe(false); + }); + + it( 'Expand installers by anonymous user', function() { + browser.get(baseURL+"#/scenarios/test-scenario"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000); + var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a')) + installerShow.click(); + var row = element.all(by.repeater('(indexI, installer) in ctrl.data.scenarios[0].installers')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("fuel"); + }); + + it( 'should not show the delete installer option 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[5]/td[2]/div[1]/a')) + installersShow.click(); + 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(false); + }); + + it( 'Expand installer by 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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p')) + expect(versionsShow.isDisplayed()).toBe(true) + }); + + it( 'Expand versions by 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[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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a')) + expect(versionShow.isDisplayed()).toBe(true); + }); + + it( 'Expand version by 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[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[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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a')) + expect(projectsShow.isDisplayed()).toBe(true); + }); + + it( 'Expand projects by 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[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[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[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[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[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[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')) + expect(projectShow.isDisplayed()).toBe(true) + }); + + it( 'Expand project by 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[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[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[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[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[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[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[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[4]/td[2]/a/p')) + expect(customsShow.isDisplayed()).toBe(true) + var trustIndicatorsShow = 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[2]/td[2]/a/p')) + expect(trustIndicatorsShow.isDisplayed()).toBe(true) + var scoresShow = 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[3]/td[2]/a/p')) + expect(scoresShow.isDisplayed()).toBe(true) + }); + +}); + + +describe('testing the scenarios page for user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/scenarios', + method: 'GET' + }, + response: { + data: { + "scenarios": [ + { + "installers": [ + { + "installer": "fuel", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "dvs" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + }, + { + "date": "2016-12-15 05:28", + "score": "17/24" + }, + { + "date": "2016-12-17 03:41", + "score": "16/24" + }, + { + "date": "2018-01-22T18:30:00.000Z", + "score": "10/13" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + }, + { + "date": "2016-12-25 08:22", + "status": "gold" + }, + { + "date": "2018-01-22T18:30:00.000Z", + "status": "sf" + }, + { + "date": "2018-01-17T18:30:00.000Z", + "status": "df" + } + ] + }, + { + "project": "functest", + "customs": [ + "vping_ssh", + "vping_userdata", + ], + "scores": [ + { + "date": "2016-12-09 11:28", + "score": "6/8" + }, + { + "date": "2016-12-14 15:34", + "score": "8/8" + }, + { + "date": "2016-12-19 13:22", + "score": "8/8" + }, + { + "date": "2016-12-22 18:17", + "score": "8/8" + }, + { + "date": "2016-12-25 08:22", + "score": "8/8" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + }, + { + "project": "sla", + "customs": [], + "scores": [ + { + "date": "2018-01-16T18:30:00.000Z", + "score": "sdS" + } + ], + "trust_indicators": [] + }, + { + "project": "dvsd", + "customs": [], + "scores": [], + "trust_indicators": [] + } + ] + }, + { + "owner": "dfgvds", + "version": "df", + "projects": [] + } + ] + }, + { + "installer": "fuel2", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "tc002", + "tc005", + "tc010", + "tc011" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + }, + { + "date": "2016-12-15 05:28", + "score": "17/24" + }, + { + "date": "2016-12-17 03:41", + "score": "16/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + }, + { + "date": "2016-12-25 08:22", + "status": "gold" + } + ] + }, + { + "project": "functest", + "customs": [ + "vping_ssh", + "vping_userdata" + ], + "scores": [ + { + "date": "2016-12-09 11:28", + "score": "6/8" + }, + { + "date": "2016-12-14 15:34", + "score": "8/8" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + } + ] + } + ] + } + ], + "_id": "5a50fcacsdgdsgdasgfvb861c", + "name": "test-scenario", + "creation_date": "2018-01-06 22:13:24.160407" + } + ] + } + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/installers', + method: 'POST' + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/installers', + method: 'DELETE' + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/versions', + method: 'POST', + queryString: { + installer: 'fuel' + } + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/versions', + method: 'DELETE', + queryString: { + installer: 'fuel' + } + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/projects', + method: 'POST', + queryString: { + installer: 'fuel', + version: 'colorado' + } + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/projects', + method: 'DELETE', + queryString: { + installer: 'fuel', + version: 'colorado' + } + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/customs', + method: 'POST', + queryString: { + installer: 'fuel', + version: 'colorado', + project: 'yardstick' + } + }, + response: { + status : 200 + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario/customs', + method: 'DELETE', + queryString: { + installer: 'fuel', + version: 'colorado', + project: 'yardstick' + } + }, + response: { + status : 200 + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the scenarios page for user', function() { + browser.get(baseURL+"#/scenarios"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios'), 10000); + var row = element.all(by.repeater('(index, scenario) in ctrl.data.scenarios')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test-scenario"); + var scenarioLink = element(by.linkText('test-scenario')).click(); + browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000); + }); + + it( 'should show the add installer option for 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[5]/td[2]/div[2]/button')) + expect(buttonAdd.isDisplayed()).toBe(true); + }); + + it('add installer', 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[5]/td[2]/div[2]/button')) + buttonAdd.click(); + var name = element(by.model('installerModalCtrl.installer.installer')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonOk = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[2]/button[1]')) + buttonOk.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Installers are successfully updated.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + + }); + + it( 'Expand installers by user', function() { + browser.get(baseURL+"#/scenarios/test-scenario"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000); + var installerShow = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[1]/a/p')) + installerShow.click(); + var row = element.all(by.repeater('(indexI, installer) in ctrl.data.scenarios[0].installers')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("fuel"); + }); + + it( 'should show the delete installer option for 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[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[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[3]/button')) + expect(installerDelete.isDisplayed()).toBe(true); + }); + + it( 'delete installer', 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[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[5]/td[2]/div[3]/div/table/tbody/tr[1]/td[3]/button')) + installerDelete.click() + expect(element(by.cssContainingText('label', "You are about to delete following installers : fuel")) + .isDisplayed()).toBe(true); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Installer is successfully deleted.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Expand installer by 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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[1]/a/p')) + expect(versionsShow.isDisplayed()).toBe(true) + }); + + it( 'add version for an installer', 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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[2]/button')) + versionAdd.click() + var version = element(by.model('versionModalCtrl.version.version')); + browser.wait(EC.visibilityOf(version), 5000); + version.sendKeys('testV'); + var owner = element(by.model('versionModalCtrl.version.owner')); + owner.sendKeys('testOwner'); + var buttonOk = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[2]/button[1]')) + buttonOk.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Versions are successfully updated.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Expand versions by 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[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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[1]/td[2]/a')) + expect(versionShow.isDisplayed()).toBe(true); + var installerAdd = element(by.xpath('//*[@id="ng-app"]/body/div/div[1]/div/table/tbody/tr[5]/td[2]/div[2]/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(installerAdd.isDisplayed()).toBe(false); + expect(installerDelete.isDisplayed()).toBe(false) + }); + + it( 'delete version', 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[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[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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody[1]/tr[1]/td[3]/button')) + versionDelete.click() + expect(element(by.cssContainingText('label', "You are about to delete following version : colorado")) + .isDisplayed()).toBe(true); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Versions are successfully deleted.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Expand version by 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[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[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[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[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[5]/td[2]/div[3]/div/table/tbody/tr[2]/td[2]/div[3]/div/table/tbody/tr[3]/td[2]/div[1]/a')) + expect(projectsShow.isDisplayed()).toBe(true); + }); + + it( 'add project', 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[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[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[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[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[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() + var project = element(by.model('projectModalCtrl.project.project')); + browser.wait(EC.visibilityOf(project), 5000); + project.sendKeys('testP'); + var buttonOk = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[2]/button[1]')) + buttonOk.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Projects are successfully updated.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Expand projects by 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[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[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[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[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[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[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')) + expect(projectShow.isDisplayed()).toBe(true) + 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')) + 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/tr[1]/td[3]/button')) + expect(versionAdd.isDisplayed()).toBe(false) + expect(versionDelete.isDisplayed()).toBe(false) + }); + + it( 'delete project', 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[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[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[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[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[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[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(element(by.cssContainingText('label', "You are about to delete following projects : yardstick")) + .isDisplayed()).toBe(true); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Projects are successfully Deleted.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Expand project by 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[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[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[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[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[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[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[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[4]/td[2]/a/p')) + expect(customsShow.isDisplayed()).toBe(true) + var trustIndicatorsShow = 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[2]/td[2]/a/p')) + expect(trustIndicatorsShow.isDisplayed()).toBe(true) + var scoresShow = 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[3]/td[2]/a/p')) + expect(scoresShow.isDisplayed()).toBe(true) + }); + + it( 'Expand trust indicator by 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[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[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[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[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[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[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 trustIndicatorsShow = 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[2]/td[2]/a/p')) + trustIndicatorsShow.click(); + var row = element.all(by.repeater('(indexTI, trust_indicator) in project.trust_indicators')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("silver"); + }); + + it( 'Expand score by 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[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[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[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[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[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[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 scoresShow = 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[3]/td[2]/a/p')) + scoresShow.click(); + var row = element.all(by.repeater('(indexSC, score) in project.scores')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("14/24"); + }); + + it( 'Expand Customs by 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[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[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[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[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[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[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[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 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')) + 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')) + expect(projectDelete.isDisplayed()).toBe(false) + expect(projectAdd.isDisplayed()).toBe(false) + 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')) + 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')) + expect(buttonAdd.isDisplayed()).toBe(true) + expect(buttonDelete.isDisplayed()).toBe(true) + }); + + it( 'Add Customs by 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[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[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[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[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[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[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[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[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() + var custom = element(by.model('customModalCtrl.custom')); + browser.wait(EC.visibilityOf(custom), 5000); + custom.sendKeys('testC'); + var buttonOk = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[2]/button[1]')) + buttonOk.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Customs are successfully updated.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Add multiple Customs by 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[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[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[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[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[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[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[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[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() + var custom = element(by.model('customModalCtrl.custom')); + browser.wait(EC.visibilityOf(custom), 5000); + custom.sendKeys('testC,testD,'); + var buttonOk = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[2]/button[1]')) + buttonOk.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Customs are successfully updated.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it( 'Delete Customs by 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[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[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[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[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[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[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[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[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(element(by.cssContainingText('label', "You are about to delete following customs : dvs")) + .isDisplayed()).toBe(true); + var buttonOk = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div[3]/button[1]')) + buttonOk.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Customs are successfully deleted.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + +});
\ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/scenariosControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/scenariosControllerSpec.js new file mode 100644 index 0000000..55922ad --- /dev/null +++ b/testapi/opnfv_testapi/tests/UI/e2e/scenariosControllerSpec.js @@ -0,0 +1,500 @@ +'use strict'; + +var mock = require('protractor-http-mock'); +var baseURL = "http://localhost:8000/" + +describe('testing the scenarios page for anonymous user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/scenarios', + method: 'GET' + }, + response: { + data: { + "scenarios": [ + { + "installers": [ + { + "installer": "fuel", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "dvs" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + } + ] + } + ] + } + ], + "_id": "5a50fcacsdgdsgdasgfvb861c", + "name": "test-scenario", + "creation_date": "2018-01-06 22:13:24.160407" + }, + { + "installers": [ + { + "installer": "fuel", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "dvs" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + } + ] + } + ] + } + ], + "_id": "5a50fcacsdgdsgdasgfvb861d", + "name": "z-test-scenario", + "creation_date": "2018-01-06 22:13:24.160407" + } + ] + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the scenarios page for anonymous user', function() { + browser.get(baseURL+"#/scenarios"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios'), 10000); + }); + + it( 'navigate anonymous user to scenarios page', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Scenarios')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios'), 10000); + }); + + it('Should show the scenarios in scenarios page for anonymous user ', function () { + browser.get(baseURL+"#/scenarios"); + var row = element.all(by.repeater('(index, scenario) in ctrl.data.scenarios')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test-scenario"); + }); + + it('Sort scenarios', function () { + browser.get(baseURL+"#/scenarios"); + var sort = element(by.xpath('//*[@id="ng-app"]/body/div/div[5]/div/table/thead/tr/th[2]/a[2]/span')) + sort.click(); + var row = element.all(by.repeater('(index, scenario) in ctrl.data.scenarios')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("z-test-scenario"); + }); + + it('create button is not visible for anonymous user ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + it('delete button is not visible for anonymous user ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Delete')); + expect(buttonCreate.isDisplayed()).toBeFalsy(); + }); + +}); + +describe('testing the scenarios page for user', function () { + beforeEach(function(){ + mock([ + { + request: { + path: '/api/v1/scenarios', + method: 'GET' + }, + response: { + data: { + "scenarios": [ + { + "installers": [ + { + "installer": "fuel", + "versions": [ + { + "owner": "testUser", + "version": "colorado", + "projects": [ + { + "project": "yardstick", + "customs": [ + "dvs" + ], + "scores": [ + { + "date": "2016-12-11 01:45", + "score": "14/24" + } + ], + "trust_indicators": [ + { + "date": "2016-12-09 11:38", + "status": "silver" + } + ] + } + ] + } + ] + } + ], + "_id": "5a50fcacsdgdsgdasgfvb861c", + "name": "test-scenario", + "creation_date": "2018-01-06 22:13:24.160407" + } + ] + } + } + }, + { + request: { + path: '/api/v1/profile', + method: 'GET' + }, + response: { + data: { + "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed", + "user": "testUser", "groups": ["opnfv-testapi-users"], + "email": "testuser@test.com" + } + } + }, + { + request: { + path: '/api/v1/scenarios', + method: 'POST' + }, + response: { + data: { + href: baseURL+"/api/v1/scenarios/testScenario" + } + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario', + method: 'DELETE' + }, + response: { + data: { + href: baseURL+"/api/v1/scenarios/testScenario" + } + } + }, + { + request: { + path: '/api/v1/scenarios/test-scenario', + method: 'PUT' + }, + response: { + data: { + href: baseURL+"/api/v1/scenarios/testScenario" + } + } + } + ]); + }); + + afterEach(function(){ + mock.teardown(); + }); + + it( 'should show the scenarios page for user', function() { + browser.get(baseURL+"#/scenarios"); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios'), 10000); + }); + + it( 'navigate user to scenarios page', function() { + browser.get(baseURL); + var resultLink = element(by.linkText('Scenarios')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios'), 10000); + }); + + it('Should show the scenarios in scenarios page for user ', function () { + browser.get(baseURL+"#/scenarios"); + var row = element.all(by.repeater('(index, scenario) in ctrl.data.scenarios')).first(); + var cells = row.all(by.tagName('td')); + expect(cells.get(1).getText()).toContain("test-scenario"); + }); + + it('create button is visible for user ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')); + expect(buttonCreate.isDisplayed()).toBe(true); + }); + + it('delete button is visible for user ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonDelete = element(by.buttonText('Delete')); + expect(buttonDelete.isDisplayed()).toBe(true); + }); + + it('craete scenarrio by user without installers ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')).click(); + var name = element(by.model('scenarioModalController.scenario.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully created.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('create scenarrio by user with installers ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')).click(); + var name = element(by.model('scenarioModalController.scenario.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonInstaller = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonInstaller.click(); + var installer = element(by.model('installerModalCtrl.installer.installer')); + browser.wait(EC.visibilityOf(installer), 5000); + installer.sendKeys('testI'); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully created.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('create scenarrio by user with installers with versions ', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')).click(); + var name = element(by.model('scenarioModalController.scenario.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonInstaller = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonInstaller.click(); + var installer = element(by.model('installerModalCtrl.installer.installer')); + browser.wait(EC.visibilityOf(installer), 5000); + installer.sendKeys('testI'); + var buttonVersion = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonVersion.click(); + var version = element(by.model('versionModalCtrl.version.version')); + browser.wait(EC.visibilityOf(version), 5000); + version.sendKeys('testV'); + var owner = element(by.model('versionModalCtrl.version.owner')); + owner.sendKeys('testOwner'); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[5]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully created.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('create scenarrio by user with installers with versions with project', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')).click(); + var name = element(by.model('scenarioModalController.scenario.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonInstaller = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonInstaller.click(); + var installer = element(by.model('installerModalCtrl.installer.installer')); + browser.wait(EC.visibilityOf(installer), 5000); + installer.sendKeys('testI'); + var buttonVersion = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonVersion.click(); + var version = element(by.model('versionModalCtrl.version.version')); + browser.wait(EC.visibilityOf(version), 5000); + version.sendKeys('testV'); + var owner = element(by.model('versionModalCtrl.version.owner')); + owner.sendKeys('testOwner'); + var buttonProject = element(by.xpath('//*[@id="ng-app"]/body/div[5]/div/div/div/div[1]/div[1]/fieldset/div/div/div[3]/div[2]/button')) + buttonProject.click(); + var project = element(by.model('projectModalCtrl.project.project')); + browser.wait(EC.visibilityOf(project), 5000); + project.sendKeys('testP'); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[6]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[5]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully created.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('create scenarrio by user with installers with versions with project with custom', function () { + browser.get(baseURL+'#/scenarios'); + var buttonCreate = element(by.buttonText('Create')).click(); + var name = element(by.model('scenarioModalController.scenario.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test'); + var buttonInstaller = element(by.xpath('//*[@id="ng-app"]/body/div[3]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonInstaller.click(); + var installer = element(by.model('installerModalCtrl.installer.installer')); + browser.wait(EC.visibilityOf(installer), 5000); + installer.sendKeys('testI'); + var buttonVersion = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonVersion.click(); + var version = element(by.model('versionModalCtrl.version.version')); + browser.wait(EC.visibilityOf(version), 5000); + version.sendKeys('testV'); + var owner = element(by.model('versionModalCtrl.version.owner')); + owner.sendKeys('testOwner'); + var buttonProject = element(by.xpath('//*[@id="ng-app"]/body/div[5]/div/div/div/div[1]/div[1]/fieldset/div/div/div[3]/div[2]/button')) + buttonProject.click(); + var project = element(by.model('projectModalCtrl.project.project')); + browser.wait(EC.visibilityOf(project), 5000); + project.sendKeys('testP'); + var buttonCustom = element(by.xpath('//*[@id="ng-app"]/body/div[6]/div/div/div/div[1]/div[1]/fieldset/div/div/div[2]/div[2]/button')) + buttonCustom.click(); + var custom = element(by.model('customModalCtrl.custom')); + browser.wait(EC.visibilityOf(custom), 5000); + custom.sendKeys('testC'); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[7]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[6]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[5]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.xpath('//*[@id="ng-app"]/body/div[4]/div/div/div/div[2]/button[1]')); + buttonOK.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully created.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('view scenarrio by user ', function () { + browser.get(baseURL+'#/scenarios'); + var scenarioLink = element(by.linkText('test-scenario')).click(); + var EC = browser.ExpectedConditions; + browser.wait(EC.urlContains(baseURL+ '#/scenarios/test-scenario'), 10000); + }); + + it('delete Operation is visible for user ', function () { + browser.get(baseURL+'#/scenarios'); + var deleteOperation = element(by.css('a[title=Delete]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('edit Operation is visible for user ', function () { + browser.get(baseURL+'#/scenarios'); + var deleteOperation = element(by.css('a[title=Edit]')); + expect(deleteOperation.isDisplayed()).toBe(true); + }); + + it('Delete the Scenario ', function () { + browser.get(baseURL+'#/scenarios'); + var deleteOperation = element(by.css('a[title=Delete]')); + deleteOperation.click(); + expect(element(by.cssContainingText('label', "You are about to delete following scenarios : test-scenario")) + .isDisplayed()).toBe(true); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully deleted.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('Batch Delete the scenarios ', function () { + browser.get(baseURL+'#/scenarios'); + var checkBox = element(by.model('ctrl.checkBox[index]')); + checkBox.click(); + var buttonDelete = element(by.buttonText('Delete'));; + buttonDelete.click(); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click(); + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully deleted.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + + it('Edit the scenarios ', function () { + browser.get(baseURL+'#/scenarios'); + var editOperation = element(by.css('a[title=Edit]')); + editOperation.click(); + var name = element(by.model('ScenarioNameUpdateCtrl.name')); + var EC = browser.ExpectedConditions; + browser.wait(EC.visibilityOf(name), 5000); + name.sendKeys('test2'); + var buttonOK = element(by.buttonText('Ok')); + buttonOK.click() + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Scenario is successfully Updated.")) + .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; + }); + +});
\ No newline at end of file diff --git a/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js index d509c57..38e0f24 100644 --- a/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js +++ b/testapi/opnfv_testapi/tests/UI/e2e/testCasesControllerSpec.js @@ -15,7 +15,7 @@ describe('testing the testCases page for anonymous user', function () { data: { "projects": [ { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -32,7 +32,7 @@ describe('testing the testCases page for anonymous user', function () { }, response: { data: { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -182,7 +182,7 @@ describe('testing the testcaese page for user who is not in submitter group', fu data: { "projects": [ { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -199,7 +199,7 @@ describe('testing the testcaese page for user who is not in submitter group', fu }, response: { data: { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -397,7 +397,7 @@ describe('testing the test cases page for user who is in submitter group', funct data: { "projects": [ { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -414,7 +414,7 @@ describe('testing the test cases page for user who is in submitter group', funct }, response: { data: { - "owner": "thuva4", + "creator": "thuva4", "_id": "5a0c022f9a07c846d3c2cc94", "creation_date": "2017-11-15 14:30:31.200259", "description": "dsfsd", @@ -550,8 +550,11 @@ describe('testing the test cases page for user who is in submitter group', funct name.sendKeys('test'); var buttonOK = element(by.buttonText('Ok')); buttonOK.click(); - expect(element(by.cssContainingText(".alert","Testcase is successfully created.")) + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Testcase is successfully created.")) .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; }); it('Showing error when creating with a empty name ', function () { @@ -565,8 +568,11 @@ describe('testing the test cases page for user who is in submitter group', funct 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 test case ', function () { @@ -575,6 +581,8 @@ describe('testing the test cases page for user who is in submitter group', funct testCases.click(); var deleteOperation = element(by.css('a[title=Delete]')); deleteOperation.click(); + expect(element(by.cssContainingText('label', "You are about to delete following testcases : testCase")) + .isDisplayed()).toBe(true); var buttonCancel = element(by.buttonText('Cancel')); buttonCancel.click(); expect(buttonCancel.isPresent()).toBe(false); @@ -588,8 +596,11 @@ describe('testing the test cases page for user who is in submitter group', funct deleteOperation.click(); var buttonOK = element(by.buttonText('Ok')); buttonOK.click(); - expect(element(by.cssContainingText(".alert","Test case is successfully deleted")) + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Test case is successfully deleted")) .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; }); it('cancel the Edit modal of the test case ', function () { @@ -619,8 +630,11 @@ describe('testing the test cases page for user who is in submitter group', funct name.sendKeys('test1'); var buttonOK = element(by.buttonText('Ok')); buttonOK.click(); - expect(element(by.cssContainingText(".alert","Test case is successfully updated")) + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Test case is successfully updated")) .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; }); it('view the test case ', function () { @@ -644,7 +658,10 @@ describe('testing the test cases page for user who is in submitter group', funct buttonDelete.click(); var buttonOK = element(by.buttonText('Ok')); buttonOK.click(); - expect(element(by.cssContainingText(".alert","Test case is successfully deleted")) + browser.ignoreSynchronization = true; + expect(element(by.cssContainingText(".success.show","Test case is successfully deleted")) .isDisplayed()).toBe(true); + browser.sleep(500); + browser.ignoreSynchronization = false; }); }) diff --git a/testapi/opnfv_testapi/tests/unit/common/test_config.py b/testapi/opnfv_testapi/tests/unit/common/test_config.py index 6d160ce..8e4966f 100644 --- a/testapi/opnfv_testapi/tests/unit/common/test_config.py +++ b/testapi/opnfv_testapi/tests/unit/common/test_config.py @@ -11,7 +11,7 @@ def test_config_normal(mocker, config_normal): assert CONF.mongo_url == 'mongodb://127.0.0.1:27017/' assert CONF.mongo_dbname == 'test_results_collection' assert CONF.api_port == 8000 - assert CONF.api_debug is True + assert CONF.api_debug is False assert CONF.api_token_check is False assert CONF.api_authenticate is True assert CONF.ui_url == 'http://localhost:8000' diff --git a/testapi/opnfv_testapi/tests/unit/executor.py b/testapi/opnfv_testapi/tests/unit/executor.py index 743c076..7c8cb8a 100644 --- a/testapi/opnfv_testapi/tests/unit/executor.py +++ b/testapi/opnfv_testapi/tests/unit/executor.py @@ -18,9 +18,9 @@ O_get_secure_cookie = ( def thread_execute(method, *args, **kwargs): - with ThreadPoolExecutor(max_workers=2) as executor: - result = executor.submit(method, *args, **kwargs) - return result + with ThreadPoolExecutor(max_workers=2) as executor: + result = executor.submit(method, *args, **kwargs) + return result def mock_invalid_lfid(): @@ -43,20 +43,6 @@ def mock_valid_lfid(): return _mock_valid_lfid -def upload(excepted_status, excepted_response): - def _upload(create_request): - @functools.wraps(create_request) - def wrap(self): - request = create_request(self) - status, body = self.upload(request) - if excepted_status == httplib.OK: - getattr(self, excepted_response)(body) - else: - self.assertIn(excepted_response, body) - return wrap - return _upload - - def create(excepted_status, excepted_response): def _create(create_request): @functools.wraps(create_request) @@ -105,7 +91,7 @@ def delete(excepted_status, excepted_response): def wrap(self): request = delete_request(self) if isinstance(request, tuple): - status, body = self.delete(request[0], *(request[1])) + status, body = self.delete(*request) else: status, body = self.delete(request) if excepted_status == httplib.OK: diff --git a/testapi/opnfv_testapi/tests/unit/fake_pymongo.py b/testapi/opnfv_testapi/tests/unit/fake_pymongo.py index 041e6e8..631e9ac 100644 --- a/testapi/opnfv_testapi/tests/unit/fake_pymongo.py +++ b/testapi/opnfv_testapi/tests/unit/fake_pymongo.py @@ -15,9 +15,9 @@ from concurrent.futures import ThreadPoolExecutor def thread_execute(method, *args, **kwargs): - with ThreadPoolExecutor(max_workers=2) as executor: - result = executor.submit(method, *args, **kwargs) - return result + with ThreadPoolExecutor(max_workers=2) as executor: + result = executor.submit(method, *args, **kwargs) + return result class MemCursor(object): diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_base.py b/testapi/opnfv_testapi/tests/unit/handlers/test_base.py index 6436b8b..1fd3324 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_base.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_base.py @@ -35,20 +35,21 @@ class TestBase(testing.AsyncHTTPTestCase): details='zte pod 1', role='community-ci', _id=str(ObjectId()), - owner='ValidUser', + creator='ValidUser', creation_date=str(datetime.now())) self.pod_e = pod_models.Pod(name='zte-pod2', mode='metal', details='zte pod 2', role='production-ci', _id=str(ObjectId()), - owner='ValidUser', + creator='ValidUser', creation_date=str(datetime.now())) self.project_e = project_models.Project( name='functest', description='functest test', + creator='ValidUser', _id=str(ObjectId()), - create_date=str(datetime.now())) + creation_date=str(datetime.now())) self.req_d = None self.req_e = None @@ -62,7 +63,8 @@ class TestBase(testing.AsyncHTTPTestCase): 'opnfv-gerrit-functest-submitters', 'opnfv-gerrit-qtip-submitters', 'opnfv-gerrit-qtip-contributors', - 'opnfv-gerrit-apex-submitters'] + 'opnfv-gerrit-apex-submitters', + 'opnfv-gerrit-noProject-submitters'] }) def tearDown(self): @@ -157,7 +159,6 @@ class TestBase(testing.AsyncHTTPTestCase): res = self.fetch(url, method='DELETE', headers=self.headers) - return res.code, res.body def delete(self, *args): diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_deploy_result.py b/testapi/opnfv_testapi/tests/unit/handlers/test_deploy_result.py index 65e765e..8f2ca76 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_deploy_result.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_deploy_result.py @@ -92,6 +92,10 @@ class DeployResultGet(DeployResultBase): self.req_d_id = self._create_d() self.req_10d_later = self._create_changed_date(days=10) + @executor.get(httplib.OK, 'assert_res') + def test_getOne(self): + return self.req_d_id + @executor.query(httplib.OK, '_query_success', 3) def test_queryInstaller(self): return self._set_query('installer') @@ -165,6 +169,19 @@ class DeployResultGet(DeployResultBase): self._create_error_start_date('') return self._set_query(period=5) + @executor.query(httplib.OK, '_query_success', 0) + def test_notFound(self): + return self._set_query('installer', + 'version', + 'job_name', + 'build_id', + 'scenario', + 'upstream_job_name', + 'upstream_build_id', + 'criteria', + pod='notExistPod', + period=1) + def _query_success(self, body, number): self.assertEqual(number, len(body.deployresults)) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_pod.py b/testapi/opnfv_testapi/tests/unit/handlers/test_pod.py index 2818513..3a16799 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_pod.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_pod.py @@ -10,6 +10,7 @@ import httplib from opnfv_testapi.common import message from opnfv_testapi.models import pod_models as pm +from opnfv_testapi.models import result_models as rm 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 @@ -23,11 +24,13 @@ class TestPodBase(base.TestBase): self.basePath = '/api/v1/pods' self.req_d = pm.PodCreateRequest.from_dict(self.pod_d.format()) self.req_e = pm.PodCreateRequest.from_dict(self.pod_e.format()) + self.results_d = rm.ResultCreateRequest.from_dict( + self.load_json('test_result')) def assert_get_body(self, pod, req=None): if not req: req = self.req_d - self.assertEqual(pod, pm.Pod(owner='ValidUser', **req.format())) + self.assertEqual(pod, pm.Pod(creator='ValidUser', **req.format())) self.assertIsNotNone(pod.creation_date) self.assertIsNotNone(pod._id) @@ -101,3 +104,38 @@ class TestPodGet(TestPodBase): self.assert_get_body(pod) else: self.assert_get_body(pod, self.req_e) + + +class TestPodDelete(TestPodBase): + @executor.mock_valid_lfid() + def setUp(self): + super(TestPodDelete, self).setUp() + fake_pymongo.pods.insert(self.pod_d.format()) + fake_pymongo.projects.insert({'name': self.results_d.project_name}) + fake_pymongo.testcases.insert({ + 'name': self.results_d.case_name, + 'project_name': self.results_d.project_name}) + + @executor.delete(httplib.BAD_REQUEST, message.not_login()) + def test_notlogin(self): + return self.pod_d.name + + @executor.delete(httplib.NOT_FOUND, message.not_found_base) + def test_notFound(self): + return 'notFound' + + @executor.mock_valid_lfid() + @executor.delete(httplib.UNAUTHORIZED, message.tied_with_resource()) + def test_deleteNotAllowed(self): + self.create_help('/api/v1/results', self.results_d) + return self.pod_d.name + + @executor.mock_valid_lfid() + @executor.delete(httplib.OK, '_assert_delete') + def test_success(self): + return self.pod_d.name + + def _assert_delete(self, body): + self.assertEqual(body, '') + code, body = self.get(self.pod_d.name) + self.assertEqual(code, httplib.NOT_FOUND) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_project.py b/testapi/opnfv_testapi/tests/unit/handlers/test_project.py index cbd1a4e..5903507 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_project.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_project.py @@ -12,6 +12,7 @@ import urllib from opnfv_testapi.common import message from opnfv_testapi.models import project_models +from opnfv_testapi.models import testcase_models as tcm from opnfv_testapi.tests.unit import executor from opnfv_testapi.tests.unit.handlers import test_base as base @@ -23,6 +24,9 @@ class TestProjectBase(base.TestBase): 'qtip-ssh test') self.req_e = project_models.ProjectCreateRequest('functest', 'functest test') + self.testcase_d = tcm.TestcaseCreateRequest.from_dict( + self.load_json('testcase_d')) + self.project = 'qtip' self.get_res = project_models.Project self.list_res = project_models.Projects self.update_res = project_models.Project @@ -150,6 +154,15 @@ class TestProjectUpdate(TestProjectBase): return self.req_d, self.req_d.name @executor.mock_valid_lfid() + @executor.update(httplib.UNAUTHORIZED, message.tied_with_resource()) + def test_updateNotAllowed(self): + self.create_help('/api/v1/projects/%s/cases', + self.testcase_d, + self.req_d.name) + req = project_models.ProjectUpdateRequest('apex', 'apex test') + return req, self.req_d.name + + @executor.mock_valid_lfid() @executor.update(httplib.OK, '_assert_update') def test_success(self): req = project_models.ProjectUpdateRequest('apex', 'apex test') @@ -178,6 +191,14 @@ class TestProjectDelete(TestProjectBase): return 'notFound' @executor.mock_valid_lfid() + @executor.delete(httplib.UNAUTHORIZED, message.tied_with_resource()) + def test_deleteNotAllowed(self): + self.create_help('/api/v1/projects/%s/cases', + self.testcase_d, + self.req_d.name) + return self.req_d.name + + @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/tests/unit/handlers/test_result.py b/testapi/opnfv_testapi/tests/unit/handlers/test_result.py index f1f055e..6435367 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_result.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_result.py @@ -10,7 +10,6 @@ import copy from datetime import datetime from datetime import timedelta import httplib -import json import urllib from opnfv_testapi.common import message @@ -34,8 +33,9 @@ class TestResultBase(base.TestBase): self.basePath = '/api/v1/results' fake_pymongo.pods.insert({'name': self.req_d.pod_name}) fake_pymongo.projects.insert({'name': self.req_d.project_name}) - fake_pymongo.testcases.insert({'name': self.req_d.case_name, - 'project_name': self.req_d.project_name}) + fake_pymongo.testcases.insert({ + 'name': self.req_d.case_name, + 'project_name': self.req_d.project_name}) def assert_res(self, result, req=None): if req is None: @@ -46,22 +46,6 @@ class TestResultBase(base.TestBase): _, res = self.create_d() return res.href.split('/')[-1] - def upload(self, req): - if req and not isinstance(req, str) and hasattr(req, 'format'): - req = req.format() - res = self.fetch(self.basePath + '/upload', - method='POST', - body=json.dumps(req), - headers=self.headers) - - return self._get_return(res, self.create_res) - - -class TestResultUpload(TestResultBase): - @executor.upload(httplib.BAD_REQUEST, message.key_error('file')) - def test_filenotfind(self): - return None - class TestResultCreate(TestResultBase): @executor.create(httplib.BAD_REQUEST, message.no_body()) @@ -121,18 +105,6 @@ class TestResultCreate(TestResultBase): req.details = {'1.name': 'dot_name'} return req - @executor.create(httplib.OK, '_assert_no_ti') - def test_no_ti(self): - req = copy.deepcopy(self.req_d) - req.trust_indicator = rm.TI(0) - self.actual_req = req - return req - - def _assert_no_ti(self, body): - _id = body.href.split('/')[-1] - code, body = self.get(_id) - self.assert_res(body, self.actual_req) - class TestResultGet(TestResultBase): def setUp(self): @@ -174,10 +146,6 @@ class TestResultGet(TestResultBase): return self._set_query('scenario') @executor.query(httplib.OK, '_query_success', 3) - def test_queryTrustIndicator(self): - return self._set_query('trust_indicator') - - @executor.query(httplib.OK, '_query_success', 3) def test_queryCriteria(self): return self._set_query('criteria') @@ -197,16 +165,6 @@ class TestResultGet(TestResultBase): def test_queryLast(self): return self._set_query(last=1) - @executor.query(httplib.OK, '_query_success', 4) - def test_queryPublic(self): - self._create_public_data() - return self._set_query() - - @executor.query(httplib.OK, '_query_success', 1) - def test_queryPrivate(self): - self._create_private_data() - return self._set_query(public='false') - @executor.query(httplib.OK, '_query_period_one', 1) def test_combination(self): return self._set_query('pod', @@ -216,7 +174,6 @@ class TestResultGet(TestResultBase): 'installer', 'build_tag', 'scenario', - 'trust_indicator', 'criteria', period=5) @@ -228,7 +185,6 @@ class TestResultGet(TestResultBase): 'installer', 'build_tag', 'scenario', - 'trust_indicator', 'criteria', pod='notExistPod', period=1) @@ -266,24 +222,10 @@ class TestResultGet(TestResultBase): self.create(req) return req - def _create_public_data(self, **kwargs): - req = copy.deepcopy(self.req_d) - req.public = 'true' - self.create(req) - return req - - def _create_private_data(self, **kwargs): - req = copy.deepcopy(self.req_d) - req.public = 'false' - self.create(req) - return req - def _set_query(self, *args, **kwargs): def get_value(arg): if arg in ['pod', 'project', 'case']: return getattr(self.req_d, arg + '_name') - elif arg == 'trust_indicator': - return self.req_d.trust_indicator.current else: return getattr(self.req_d, arg) @@ -293,24 +235,3 @@ class TestResultGet(TestResultBase): for k, v in kwargs.iteritems(): query.append((k, v)) return urllib.urlencode(query) - - -class TestResultUpdate(TestResultBase): - def setUp(self): - super(TestResultUpdate, self).setUp() - self.req_d_id = self._create_d() - - @executor.update(httplib.OK, '_assert_update_ti') - def test_success(self): - update_date = str(datetime.now() + timedelta(days=1)) - update_step = -0.05 - self.after_update = copy.deepcopy(self.req_d) - self.after_update.trust_indicator.current += update_step - self.after_update.trust_indicator.histories.append( - rm.TIHistory(update_date, update_step)) - update = rm.ResultUpdateRequest( - trust_indicator=self.after_update.trust_indicator) - return update, self.req_d_id - - def _assert_update_ti(self, request, body): - self.assert_res(body, self.after_update) diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_scenario.py b/testapi/opnfv_testapi/tests/unit/handlers/test_scenario.py index 5d1b738..481078d 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_scenario.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_scenario.py @@ -4,6 +4,7 @@ import httplib from opnfv_testapi.common import message import opnfv_testapi.models.scenario_models as sm +from opnfv_testapi.tests.unit import executor from opnfv_testapi.tests.unit.handlers import test_base as base @@ -20,6 +21,7 @@ class TestScenarioBase(base.TestBase): self.req_d = self.load_json('scenario-c1') self.req_2 = self.load_json('scenario-c2') + @executor.mock_valid_lfid() def create_return_name(self, req): _, res = self.create(req) return res.href.split('/')[-1] @@ -45,27 +47,32 @@ class TestScenarioBase(base.TestBase): class TestScenarioCreate(TestScenarioBase): + @executor.mock_valid_lfid() def test_withoutBody(self): (code, body) = self.create() self.assertEqual(code, httplib.BAD_REQUEST) + @executor.mock_valid_lfid() def test_emptyName(self): req_empty = sm.ScenarioCreateRequest('') (code, body) = self.create(req_empty) self.assertEqual(code, httplib.BAD_REQUEST) self.assertIn(message.missing('name'), body) + @executor.mock_valid_lfid() def test_noneName(self): req_none = sm.ScenarioCreateRequest(None) (code, body) = self.create(req_none) self.assertEqual(code, httplib.BAD_REQUEST) self.assertIn(message.missing('name'), body) + @executor.mock_valid_lfid() def test_success(self): (code, body) = self.create_d() self.assertEqual(code, httplib.OK) self.assert_create_body(body) + @executor.mock_valid_lfid() def test_alreadyExist(self): self.create_d() (code, body) = self.create_d() @@ -128,6 +135,7 @@ class TestScenarioDelete(TestScenarioBase): code, body = self.delete('notFound') self.assertEqual(code, httplib.NOT_FOUND) + @executor.mock_valid_lfid() def test_success(self): scenario = self.create_return_name(self.req_d) code, _ = self.delete(scenario) @@ -182,6 +190,7 @@ class TestScenarioUpdate(TestScenarioBase): return wrapper return _update_partial + @executor.mock_valid_lfid() @update_partial('_add', '_success') def test_addScore(self): add = sm.ScenarioScore(date=str(datetime.now()), score='11/12') @@ -193,6 +202,7 @@ class TestScenarioUpdate(TestScenarioBase): return add + @executor.mock_valid_lfid() @update_partial('_add', '_success') def test_addTrustIndicator(self): add = sm.ScenarioTI(date=str(datetime.now()), status='gold') @@ -204,6 +214,7 @@ class TestScenarioUpdate(TestScenarioBase): return add + @executor.mock_valid_lfid() @update_partial('_add', '_success') def test_addCustoms(self): adds = ['odl', 'parser', 'vping_ssh'] @@ -214,6 +225,7 @@ class TestScenarioUpdate(TestScenarioBase): self.locate_project) return adds + @executor.mock_valid_lfid() @update_partial('_update', '_success') def test_updateCustoms(self): updates = ['odl', 'parser', 'vping_ssh'] @@ -225,6 +237,7 @@ class TestScenarioUpdate(TestScenarioBase): return updates + @executor.mock_valid_lfid() @update_partial('_delete', '_success') def test_deleteCustoms(self): deletes = ['vping_ssh'] @@ -236,6 +249,7 @@ class TestScenarioUpdate(TestScenarioBase): return deletes + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_add', '_success') def test_addProjects_succ(self): @@ -243,12 +257,14 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'][0]['versions'][0]['projects'].append(add) return [add] + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_add', '_conflict') def test_addProjects_already_exist(self): add = sm.ScenarioProject(project='functest').format() return [add] + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_add', '_bad_request') def test_addProjects_bad_schema(self): @@ -256,6 +272,7 @@ class TestScenarioUpdate(TestScenarioBase): add['score'] = None return [add] + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_update', '_success') def test_updateProjects_succ(self): @@ -263,12 +280,14 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'][0]['versions'][0]['projects'] = [update] return [update] + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_update', '_conflict') def test_updateProjects_duplicated(self): update = sm.ScenarioProject(project='qtip').format() return [update, update] + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_update', '_bad_request') def test_updateProjects_bad_schema(self): @@ -276,6 +295,7 @@ class TestScenarioUpdate(TestScenarioBase): update['score'] = None return [update] + @executor.mock_valid_lfid() @update_url_fixture('projects') @update_partial('_delete', '_success') def test_deleteProjects(self): @@ -286,6 +306,7 @@ class TestScenarioUpdate(TestScenarioBase): projects) return deletes + @executor.mock_valid_lfid() @update_url_fixture('owner') @update_partial('_update', '_success') def test_changeOwner(self): @@ -294,6 +315,7 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'][0]['versions'][0]['owner'] = new_owner return update + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_add', '_success') def test_addVersions_succ(self): @@ -301,12 +323,14 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'][0]['versions'].append(add) return [add] + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_add', '_conflict') def test_addVersions_already_exist(self): add = sm.ScenarioVersion(version='master').format() return [add] + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_add', '_bad_request') def test_addVersions_bad_schema(self): @@ -314,6 +338,7 @@ class TestScenarioUpdate(TestScenarioBase): add['notexist'] = None return [add] + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_update', '_success') def test_updateVersions_succ(self): @@ -321,12 +346,14 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'][0]['versions'] = [update] return [update] + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_update', '_conflict') def test_updateVersions_duplicated(self): update = sm.ScenarioVersion(version='euphrates').format() return [update, update] + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_update', '_bad_request') def test_updateVersions_bad_schema(self): @@ -334,6 +361,7 @@ class TestScenarioUpdate(TestScenarioBase): update['not_owner'] = 'Iam' return [update] + @executor.mock_valid_lfid() @update_url_fixture('versions') @update_partial('_delete', '_success') def test_deleteVersions(self): @@ -344,6 +372,7 @@ class TestScenarioUpdate(TestScenarioBase): versions) return deletes + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_add', '_success') def test_addInstallers_succ(self): @@ -351,12 +380,14 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'].append(add) return [add] + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_add', '_conflict') def test_addInstallers_already_exist(self): add = sm.ScenarioInstaller(installer='apex').format() return [add] + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_add', '_bad_request') def test_addInstallers_bad_schema(self): @@ -364,6 +395,7 @@ class TestScenarioUpdate(TestScenarioBase): add['not_exist'] = 'not_exist' return [add] + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_update', '_success') def test_updateInstallers_succ(self): @@ -371,12 +403,14 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['installers'] = [update] return [update] + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_update', '_conflict') def test_updateInstallers_duplicated(self): update = sm.ScenarioInstaller(installer='daisy').format() return [update, update] + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_update', '_bad_request') def test_updateInstallers_bad_schema(self): @@ -384,6 +418,7 @@ class TestScenarioUpdate(TestScenarioBase): update['not_exist'] = 'not_exist' return [update] + @executor.mock_valid_lfid() @update_url_fixture('installers') @update_partial('_delete', '_success') def test_deleteInstallers(self): @@ -394,6 +429,7 @@ class TestScenarioUpdate(TestScenarioBase): installers) return deletes + @executor.mock_valid_lfid() @update_url_fixture('rename') @update_partial('_update', '_success') def test_renameScenario(self): @@ -402,6 +438,7 @@ class TestScenarioUpdate(TestScenarioBase): self.req_d['name'] = new_name return update + @executor.mock_valid_lfid() @update_url_fixture('rename') @update_partial('_update', '_forbidden') def test_renameScenario_exist(self): diff --git a/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py b/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py index 9a2bf58..e0ce381 100644 --- a/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py +++ b/testapi/opnfv_testapi/tests/unit/handlers/test_testcase.py @@ -10,6 +10,7 @@ import httplib from opnfv_testapi.common import message from opnfv_testapi.models import testcase_models as tcm +from opnfv_testapi.models import result_models as rm 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 @@ -31,7 +32,8 @@ class TestCaseBase(base.TestBase): self.update_res = tcm.Testcase self.basePath = '/api/v1/projects/%s/cases' fake_pymongo.projects.insert(self.project_e.format()) - print self.req_d.format() + self.results_d = rm.ResultCreateRequest.from_dict( + self.load_json('test_result')) def assert_body(self, case, req=None): if not req: @@ -69,8 +71,8 @@ class TestCaseBase(base.TestBase): return super(TestCaseBase, self).update(new, self.project, case) @executor.mock_valid_lfid() - def delete(self, case): - return super(TestCaseBase, self).delete(self.project, case) + def delete(self, case=None, project=None): + return super(TestCaseBase, self).delete(project, case) class TestCaseCreate(TestCaseBase): @@ -78,6 +80,11 @@ class TestCaseCreate(TestCaseBase): def test_noBody(self): return None + @executor.create(httplib.FORBIDDEN, message.no_permission()) + def test_unauthorized(self): + self.project = 'newProject' + return self.req_d + @executor.create(httplib.FORBIDDEN, message.not_found_base) def test_noProject(self): self.project = 'noProject' @@ -151,6 +158,12 @@ class TestCaseUpdate(TestCaseBase): self.create_e() return self.update_req, self.req_d.name + @executor.update(httplib.FORBIDDEN, message.no_permission()) + def test_unauthorized(self): + update_req_e = tcm.TestcaseUpdateRequest(project_name="newProject", + **self.req_e.format()) + return update_req_e, self.req_d.name + @executor.update(httplib.FORBIDDEN, message.no_update()) def test_noUpdate(self): update = tcm.TestcaseUpdateRequest(project_name=self.project, @@ -176,14 +189,31 @@ class TestCaseDelete(TestCaseBase): def setUp(self): super(TestCaseDelete, self).setUp() self.create_d() + fake_pymongo.pods.insert(self.pod_d.format()) + fake_pymongo.projects.insert({'name': self.results_d.project_name}) + fake_pymongo.testcases.insert({ + 'name': self.results_d.case_name, + 'project_name': self.results_d.project_name}) + fake_pymongo.testcases.insert({ + 'name': 'newCase', + 'project_name': 'newProject'}) @executor.delete(httplib.NOT_FOUND, message.not_found_base) def test_notFound(self): - return 'notFound' + return 'notFound', self.project + + @executor.delete(httplib.FORBIDDEN, message.no_permission()) + def test_unauthorized(self): + return 'newCase', 'newProject' + + @executor.delete(httplib.UNAUTHORIZED, message.tied_with_resource()) + def test_deleteNotAllowed(self): + self.create_help('/api/v1/results', self.results_d) + return self.results_d.case_name, self.project @executor.delete(httplib.OK, '_delete_success') def test_success(self): - return self.req_d.name + return self.req_d.name, self.project def _delete_success(self, body): self.assertEqual(body, '') diff --git a/testapi/opnfv_testapi/tests/unit/templates/test_result.json b/testapi/opnfv_testapi/tests/unit/templates/test_result.json index b7cb910..8f1d5fa 100644 --- a/testapi/opnfv_testapi/tests/unit/templates/test_result.json +++ b/testapi/opnfv_testapi/tests/unit/templates/test_result.json @@ -1,10 +1,6 @@ { "project_name": "functest", "scenario": "odl-l2", - "trust_indicator": { - "current": 0.7, - "histories": [] - }, "case_name": "vPing", "build_tag": "v3.0", "public": "true", diff --git a/testapi/opnfv_testapi/ui/Gruntfile.js b/testapi/opnfv_testapi/ui/Gruntfile.js index 72a47e1..d6a2f47 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: { @@ -115,19 +130,29 @@ module.exports = function (grunt) { noColor: false, coverageDir: '../tests/UI/coverage', args: { - specs: ['../tests/UI/e2e/testCasesControllerSpec.js', - '../tests/UI/e2e/resultsControllerSpec.js'] + specs: [ + '../tests/UI/e2e/homeControllerSpec.js', + '../tests/UI/e2e/podsControllerSpec.js', + '../tests/UI/e2e/projectsControllerSpec.js', + '../tests/UI/e2e/testCasesControllerSpec.js', + '../tests/UI/e2e/resultsControllerSpec.js', + '../tests/UI/e2e/scenariosControllerSpec.js', + '../tests/UI/e2e/scenarioControllerSpec.js', + '../tests/UI/e2e/deployResultsControllerSpec.js', + '../tests/UI/e2e/authenticateFalseSpec.js' + ] } }, local: { options: { - configFile: '../tests/UI/protractor-conf.js' + configFile: 'protractor-conf.js' } } }, makeReport: { src: '../tests/UI/coverage/*.json', options: { + type: 'cobertura', print: 'detail' } } @@ -150,6 +175,7 @@ module.exports = function (grunt) { 'wait:default', 'protractor_coverage', 'makeReport', + 'convert', 'shell:deleteFiles' ]); } diff --git a/testapi/opnfv_testapi/ui/app.js b/testapi/opnfv_testapi/ui/app.js index 3da12b1..3fb4bc7 100644 --- a/testapi/opnfv_testapi/ui/app.js +++ b/testapi/opnfv_testapi/ui/app.js @@ -19,7 +19,7 @@ angular .module('testapiApp', [ 'ui.router','ui.bootstrap', 'cgBusy', - 'ngResource', 'angular-confirm' + 'ngResource', 'angular-confirm', 'angular-json-tree' ]); angular @@ -28,6 +28,57 @@ angular .module('testapiApp') + .service("keepState", function(){ + this.filter = {}; + }); + + angular + .module('testapiApp') + .service('sortService', function(){ + + this.sortFunction = function(data, field, ascending){ + if(ascending){ + data.sort(function(a,b) { + if (a[field].toLowerCase() > b[field].toLowerCase()) { + return -1; + } + if (a[field].toLowerCase() < b[field].toLowerCase()) { + return 1; + } + return 0; + }); + }else{ + data.sort(function(a,b) { + if (a[field].toLowerCase() < b[field].toLowerCase()) { + return -1; + } + if (a[field].toLowerCase() > b[field].toLowerCase()) { + return 1; + } + return 0; + }); + } + return data + } + }); + + angular + .module('testapiApp') + .service('dataFieldService', function(){ + this.dataFunction = dataFunction + function dataFunction(data, data_field){ + Object.keys(data).forEach(function (key) { + if (typeof data[key] === 'object' && data[key] != null) { + return dataFunction(data[key], data_field); + } + data_field[key] = key.replace(/_/g, " ").trim(); + }); + return data_field; + } + }); + + angular + .module('testapiApp') .directive('dynamicModel', ['$compile', '$parse', function ($compile, $parse) { return { restrict: 'A', @@ -42,6 +93,21 @@ }; }]); + angular + .module('testapiApp') + .directive('ngEnter', function () { + return function (scope, element, attrs) { + element.bind("keydown keypress", function (event) { + if(event.which === 13) { + scope.$apply(function (){ + scope.$eval(attrs.ngEnter); + }); + event.preventDefault(); + } + }); + }; + }); + configureRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; /** @@ -79,6 +145,16 @@ templateUrl: 'testapi-ui/components/projects/project/project.html', controller: 'ProjectController as ctrl' }). + state('scenarios', { + url: '/scenarios', + templateUrl: 'testapi-ui/components/scenarios/scenarios.html', + controller: 'ScenariosController as ctrl' + }). + state('scenario', { + url: '/scenarios/:name', + templateUrl: 'testapi-ui/components/scenarios/scenario/scenario.html', + controller: 'ScenarioController as ctrl' + }). state('testCase', { url: '/projects/:project_name/:name', templateUrl: 'testapi-ui/components/projects/project/testCases/testCase/testCase.html', @@ -94,6 +170,16 @@ templateUrl: 'testapi-ui/components/results/result/result.html', controller: 'ResultController as ctrl' }). + state('deployresults', { + url: '/deployresults', + templateUrl: 'testapi-ui/components/deploy-results/deployResults.html', + controller: 'DeployResultsController as ctrl' + }). + state('deployresult', { + url: '/deployresults/:_id', + templateUrl: 'testapi-ui/components/deploy-results/deploy-result/deployResult.html', + controller: 'DeployResultController as ctrl' + }). state('profile', { url: '/profile', templateUrl: 'testapi-ui/components/profile/profile.html', @@ -144,6 +230,7 @@ function setup($http, $rootScope, $window, $state, testapiApiUrl, authenticate) { $rootScope.auth = {}; + $rootScope.authenticate = authenticate $rootScope.auth.doSignIn = doSignIn; $rootScope.auth.doSignOut = doSignOut; $rootScope.auth.doSignCheck = doSignCheck; @@ -160,10 +247,14 @@ /** This function will initate a sign out. */ function doSignOut() { - $rootScope.auth.currentUser = null; - $rootScope.auth.isAuthenticated = false; - $rootScope.auth.projectNames = []; - $window.location.href = sign_out_url; + if(authenticate){ + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = false; + $rootScope.auth.projectNames = []; + $window.location.href = sign_out_url; + }else{ + $state.go("home", {reload: true}) + } } /** @@ -171,21 +262,23 @@ * authenticated. */ function doSignCheck() { - return $http.get(profile_url, {withCredentials: true}). - success(function (data) { - $rootScope.auth.currentUser = data; - $rootScope.auth.isAuthenticated = true; - if(authenticate){ + if(authenticate){ + return $http.get(profile_url, {withCredentials: true}). + success(function (data) { + $rootScope.auth.currentUser = data; + $rootScope.auth.isAuthenticated = true; $rootScope.auth.projectNames = $rootScope.auth.doSubmitterCheck(data.groups); - }else{ - $rootScope.auth.projectNames = ["anonymous"] - } - }). - error(function () { - $rootScope.auth.currentUser = null; - $rootScope.auth.isAuthenticated = false; - $rootScope.auth.projectNames = []; - }); + }). + error(function () { + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = false; + $rootScope.auth.projectNames = []; + }); + }else{ + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = true; + $rootScope.auth.projectNames = [] + } } function doSubmitterCheck(groups){ diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResult.html b/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResult.html new file mode 100644 index 0000000..c0803ff --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResult.html @@ -0,0 +1,31 @@ +<legend>Result</legend> +<div style="padding-right:0px"> + <div class="table-responsive"> + <table class="table" ng-data="ctrl.data.pods"> + <tbody> + <tr ng-repeat-start="(index, detail) in ctrl.data_field" style="padding:9px"> + <td class="podsTableTd">{{detail | capitalize}} :</td> + <td width="86%" class="podsTableLeftTd">{{ctrl.data[index]}}</td> + </tr> + <tr ng-repeat-end=> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Details :</td> + <td width="86%" class="podsTableLeftTd"> + <json-tree object="ctrl.json.object" root-name="object" start-expanded="true"></json-tree> + </td> + </tr> + </tbody> + </table> + </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"> {{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"> {{ctrl.success}}</span> + <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResultController.js b/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResultController.js new file mode 100644 index 0000000..6f9b84c --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResultController.js @@ -0,0 +1,66 @@ +/* + * 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('DeployResultController', DeployResultController); + + DeployResultController.$inject = [ + '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', + 'confirmModal', 'dataFieldService' + ]; + + /** + * TestAPI DeployResultController + * This controller is for the '/result/:_id' page where a user can browse + * through result declared in TestAPI. + */ + function DeployResultController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal, dataFieldService) { + var ctrl = this; + ctrl.url = testapiApiUrl + '/deployresults'; + ctrl._id = $state.params['_id']; + ctrl.loadDetails = loadDetails + ctrl.data_field = {} + + ctrl.json = {}; + ctrl.json.string = '{"id": ""}'; + ctrl.json.object = JSON.parse(ctrl.json.string); + + /** + *Contact the testapi and retrevie the result details + */ + function loadDetails(){ + var resultUrl = ctrl.url + '/' + ctrl._id; + ctrl.showError = false; + ctrl.podsRequest = + $http.get(resultUrl).success(function (data) { + ctrl.data = data; + ctrl.object=JSON.stringify(ctrl.data.details) + ctrl.json.object = JSON.parse(ctrl.object) + delete ctrl.data.details; + ctrl.data_field = dataFieldService.dataFunction(ctrl.data, ctrl.data_field) + }).catch(function (error) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = error.statusText; + }); + } + + ctrl.loadDetails(); + } +})();
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deployResults.html b/testapi/opnfv_testapi/ui/components/deploy-results/deployResults.html new file mode 100644 index 0000000..7998f3c --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/deploy-results/deployResults.html @@ -0,0 +1,130 @@ +<h3>{{ctrl.pageHeader}}</h3> +<p>{{ctrl.pageParagraph}}</p> +<div class="row" style="margin-bottom:24px;"></div> +<div class="result-filters" style="border-top: none;"> + <div class="row podTable" style="vertical-align:middle"> + <div class="col-sm-1 pull-right"> + <button type="button" class="btn btn-danger" ng-click="ctrl.clearFilters()"> + <i class="fa fa-search"></i> Clear + </button> + </div> + <div class="col-sm-1 pull-right"> + <button type="button" class="btn btn-success" ng-click="ctrl.filterList()"> + <i class="fa fa-search"></i> Filter</button> + </div> + <div class="col-sm-2 pull-right" ng-class="{'hidden': ctrl.filter=='start_date' || ctrl.filter=='end_date'}"> + <span style="margin-top:6px">Search: </span> + <input list="filter" name="filter" class="form-control search" style="display:inline;width:105px;padding-left:6px;" + ng-enter="ctrl.filterList()" ng-Model="ctrl.filterText" placeholder="Search String"> + <datalist id="filter" ng-class="{ 'hidden' : ctrl.filterOption.length<0}"> + <option ng-repeat="(index, filterValue) in ctrl.filterOption " value="{{filterValue}}">{{filterValue}}</option> + </datalist> + </div> + <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='start_date'}"> + <span style="margin-top:6px">Start Date: </span> + <p class="input-group" style="width:48%;display:inline-flex;"> + <input type="text" class="form-control" + ng-enter="ctrl.filterList()" + uib-datepicker-popup="{{ctrl.format}}" + ng-model="ctrl.filterText" is-open="ctrl.startOpen" + close-text="Close" /> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'startOpen')"> + <i class="glyphicon glyphicon-calendar"></i> + </button> + </span> + </p> + </div> + <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='end_date'}"> + <span style="margin-top:6px">End Date: </span> + <p class="input-group" style="width:48%;display:inline-flex;"> + <input type="text" class="form-control" + ng-enter="ctrl.filterList()" + uib-datepicker-popup="{{ctrl.format}}" + ng-model="ctrl.filterText" is-open="ctrl.endOpen" + close-text="Close" /> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'endOpen')"> + <i class="glyphicon glyphicon-calendar"></i> + </button> + </span> + </p> + </div> + <div class="col-md-2 row pull-right" style="width: 20%;"> + <span style="margin-top:6px">Filter: </span> + <select ng-model="ctrl.filter" ng-change="ctrl.encodeFilter()" class="form-control" style="display:inline; width:150px;"> + <option value="pod" ng-disabled="ctrl.testFilter('pod')" >Pod Name</option> + <option value="job_name" ng-disabled="ctrl.testFilter('job_name')" >Job Name</option> + <option value="installer" ng-disabled="ctrl.testFilter('installer')">Installer</option> + <option value="version" ng-disabled="ctrl.testFilter('version')">Version</option> + <option value="scenario" ng-disabled="ctrl.testFilter('scenario')">Scenario</option> + <option value="build_id" ng-disabled="ctrl.testFilter('build_id')">Build ID</option> + <option value="criteria" ng-disabled="ctrl.testFilter('criteria')">Criteria</option> + <option value="start_date" ng-disabled="ctrl.testFilter('start_date')">Start Date</option> + <option value="end_date" ng-disabled="ctrl.testFilter('end_date')">End Date</option> + </select> + </div> + <div class='filter-box'> + <div class='filter-tag col-md-1' ng-repeat="(key, tag) in ctrl.tagArray" style="background-color: #f5f5f5;border: 1px solid #e3e3e3;/* border: 1px; */margin-top: 3px;padding: 4px;margin-left: 15px;width: 13%;"> + {{key}} : {{tag}} + <div class='delete-tag btn btn-danger btn-xs' ng-click='ctrl.deleteTag(key)'> + × + </div> + </div> + </div> + </div> +</div> + +<div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div> +<div cg-busy="{promise:ctrl.resultsRequest,message:'Loading'}"></div> +<div ng-show="ctrl.data" class="results-table"> + <table ng-data="ctrl.data.deployresults" ng-show="ctrl.data" class="table table-striped table-hover"> + <thead> + <tr> + <th>ID</th> + <th>Installer</th> + <th>Version</th> + <th>Scenario</th> + <th>Pod</th> + <th>Criteria</th> + <th>Start Date</th> + <th>Stop Date</th> + </tr> + </thead> + + <tbody> + <tr ng-repeat-start="(index, result) in ctrl.data.deployresults"> + <td><a ng-click="ctrl.viewDeployResult(result._id)">{{ result._id.substr(-8) }}</a></td> + <td>{{ result.installer }}</td> + <td>{{ result.version }}</td> + <td>{{ result.scenario }}</td> + <td>{{ result.pod_name }}</td> + <td>{{ result.criteria }}</td> + <td>{{ result.start_date }}</td> + <td>{{ result.stop_date }}</td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + + <div class="pages"> + <uib-pagination + total-items="ctrl.totalItems" + ng-model="ctrl.currentPage" + items-per-page="ctrl.itemsPerPage" + max-size="ctrl.maxSize" + class="pagination-sm" + boundary-links="true" + rotate="false" + num-pages="ctrl.numPages" + ng-change="ctrl.filterList()"> + </uib-pagination> + </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> diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deployResultsController.js b/testapi/opnfv_testapi/ui/components/deploy-results/deployResultsController.js new file mode 100644 index 0000000..1128825 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/deploy-results/deployResultsController.js @@ -0,0 +1,200 @@ +/* + * 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('DeployResultsController', DeployResultsController); + + DeployResultsController.$inject = [ + '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert', + 'keepState' + ]; + + /** + * TestAPI Deploy Results Controller + * This controller is for the '/deployresults' page where a user can browse + * a listing of community uploaded results. + */ + function DeployResultsController($scope, $http, $filter, $state, testapiApiUrl, + raiseAlert, keepState) { + var ctrl = this; + + ctrl.open = open; + ctrl.clearFilters = clearFilters; + ctrl.deleteTag = deleteTag; + ctrl.filterList= filterList; + ctrl.testFilter = testFilter + ctrl.filter = "pod" + ctrl.filterValue = "pod_name" + ctrl.encodeFilter = encodeFilter + ctrl.viewDeployResult = viewDeployResult + ctrl.tagArray = {} + ctrl.filterOption=[] + + /** Initial page to be on. */ + ctrl.currentPage = 1; + + /** + * How many results should display on each page. Since pagination + * is server-side implemented, this value should match the + * 'results_per_page' configuration of the TestAPI server which + * defaults to 20. + */ + ctrl.itemsPerPage = 20; + + /** + * How many page buttons should be displayed at max before adding + * the '...' button. + */ + ctrl.maxSize = 5; + + /** The upload date lower limit to be used in filtering results. */ + ctrl.startDate = ''; + + /** The upload date upper limit to be used in filtering results. */ + ctrl.endDate = ''; + + /** The date format for the date picker. */ + ctrl.format = 'yyyy-MM-dd'; + + ctrl.pageHeader = "Deploy Results" + + ctrl.pageParagraph = 'Your most recently uploaded deploy results are listed here.' + ctrl.isPublic = false; + + function encodeFilter(){ + ctrl.filterText = '' + ctrl.filterOption=[] + if(ctrl.filter=="pod" || ctrl.filter=="scenario"){ + var reqURL = testapiApiUrl +"/" + ctrl.filter + "s" + ctrl.datasRequest = + $http.get(reqURL).success(function (data) { + ctrl.filterData = data; + for(var index in ctrl.filterData[ctrl.filter + "s"]){ + if( ctrl.filterOption.indexOf(ctrl.filterData[ctrl.filter + "s"][index]["name"]) < 0){ + ctrl.filterOption.push(ctrl.filterData[ctrl.filter + "s"][index]["name"]) + } + } + }).catch(function (data) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = data.statusText; + }); + + } + } + + function deleteTag(index){ + delete ctrl.tagArray[index]; + ctrl.filterList(); + } + + function testFilter(text){ + for (var filter in ctrl.tagArray){ + if(text==filter){ + return true; + } + } + return false; + } + + function viewDeployResult(_id){ + $state.go('deployresult', {'_id':_id}, {reload: true}); + } + + /** + * This will contact the TestAPI API to get a listing of deploy + * results. + */ + function filterList(){ + if(ctrl.filter && ctrl.filterText!="" && ctrl.filterText!=undefined){ + ctrl.tagArray[ctrl.filter] = ctrl.filterText; + if(!keepState.filter.deployResultFilter){ + keepState.filter.deployResultFilter = {} + } + keepState.filter.deployResultFilter[ctrl.filter] = ctrl.filterText + } + else if(Object.keys(ctrl.tagArray).length==0){ + if(keepState.filter.deployResultFilter){ + ctrl.tagArray = keepState.filter.deployResultFilter + } + } + ctrl.showError = false; + var content_url = testapiApiUrl + '/deployresults' + + '?page=' + ctrl.currentPage; + for(var key in ctrl.tagArray){ + if(key=="start_date"){ + var start = $filter('date')(ctrl.tagArray[key], 'yyyy-MM-dd'); + if (start) { + content_url = + content_url + '&from=' + start + ' 00:00:00'; + } + } + else if(key=="end_date"){ + var end = $filter('date')(ctrl.tagArray[key], 'yyyy-MM-dd'); + if (end) { + content_url = content_url + '&to=' + end + ' 23:59:59'; + } + } + else{ + content_url = content_url + "&" + key + "=" + ctrl.tagArray[key] + } + if (ctrl.isUserResults) { + content_url = content_url + '&signed'; + } + } + ctrl.resultsRequest = + $http.get(content_url).success(function (data) { + ctrl.data = data; + ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage; + ctrl.currentPage = ctrl.data.pagination.current_page; + ctrl.encodeFilter(); + }).catch(function (error) { + ctrl.data = null; + ctrl.totalItems = 0; + ctrl.showError = true; + ctrl.error = error.statusText + }); + ctrl.filterText = '' + } + ctrl.filterList(); + + + /** + * This is called when the date filter calendar is opened. It + * does some event handling, and sets a scope variable so the UI + * knows which calendar was opened. + * @param {Object} $event - The Event object + * @param {String} openVar - Tells which calendar was opened + */ + function open($event, openVar) { + $event.preventDefault(); + $event.stopPropagation(); + ctrl[openVar] = true; + } + + /** + * This function will clear all filters and update the results + * listing. + */ + function clearFilters() { + ctrl.tagArray = {} + ctrl.filter = undefined + ctrl.filterList(); + } + } +})(); diff --git a/testapi/opnfv_testapi/ui/components/pods/pod/pod.html b/testapi/opnfv_testapi/ui/components/pods/pod/pod.html index b78eb2d..f9ab7c1 100644 --- a/testapi/opnfv_testapi/ui/components/pods/pod/pod.html +++ b/testapi/opnfv_testapi/ui/components/pods/pod/pod.html @@ -3,33 +3,11 @@ <div class="table-responsive"> <table class="table" ng-data="ctrl.data.pods"> <tbody> - <tr style="padding:9px"> - <td class="podsTableTd">Id :</td> - <td class="podsTableLeftTd">{{ctrl.data._id}}</td> + <tr ng-repeat-start="(index, detail) in ctrl.data_field" style="padding:9px"> + <td class="podsTableTd">{{detail | capitalize}} :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data[index]}}</td> </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Name :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Owner :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.owner}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Role :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.role}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Mode :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.mode}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Created at :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data['creation_date']}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Details :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.details}}</td> + <tr ng-repeat-end=> </tr> </tbody> </table> diff --git a/testapi/opnfv_testapi/ui/components/pods/pod/podController.js b/testapi/opnfv_testapi/ui/components/pods/pod/podController.js index a2e18e8..39ba599 100644 --- a/testapi/opnfv_testapi/ui/components/pods/pod/podController.js +++ b/testapi/opnfv_testapi/ui/components/pods/pod/podController.js @@ -21,7 +21,7 @@ PodController.$inject = [ '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', - 'confirmModal' + 'confirmModal', 'dataFieldService' ]; /** @@ -30,11 +30,12 @@ * through pod declared in TestAPI. */ function PodController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, - raiseAlert, confirmModal) { + raiseAlert, confirmModal, dataFieldService) { var ctrl = this; ctrl.url = testapiApiUrl + '/pods'; ctrl.name = $state.params['name']; - ctrl.loadDetails = loadDetails + ctrl.loadDetails = loadDetails; + ctrl.data_field = {}; /** *Contact the testapi and retrevie the pod details @@ -45,6 +46,7 @@ ctrl.podsRequest = $http.get(podUrl).success(function (data) { ctrl.data = data; + ctrl.data_field = dataFieldService.dataFunction(ctrl.data, ctrl.data_field) }).catch(function (error) { ctrl.data = null; ctrl.showError = true; diff --git a/testapi/opnfv_testapi/ui/components/pods/pods.html b/testapi/opnfv_testapi/ui/components/pods/pods.html index e092699..b5dadf5 100644 --- a/testapi/opnfv_testapi/ui/components/pods/pods.html +++ b/testapi/opnfv_testapi/ui/components/pods/pods.html @@ -19,29 +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: </span> - <input type="text" class="form-control search" ngModel="filter" placeholder="Search String"> - </div> - <div class="col-md-3 row pull-right"> - <span style="margin-top:6px">Filter: </span> - <select ng-model="ctrl.filter" class="form-control"> - <option value="name">Name</option> - <option value="owner">Owner</option> - </select> + <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"> {{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"> {{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"> @@ -49,9 +38,30 @@ <tr style=" text-align: center;"> <th style="width:1%">Bulk Select</th> - <th>Name</th> - <th>Role</th> - <th>Mode</th> + <th>Name + <a class="text-danger" ng-click="ctrl.sortBy('name')" ng-class="{ 'hidden': !ctrl.sorting['name'] }" > + <span class="glyphicon glyphicon-sort-by-alphabet pull-right" aria-hidden="true"></span> + </a> + <a class="text-danger" ng-click="ctrl.sortBy('name')" ng-class="{ 'hidden': ctrl.sorting['name'] }" > + <span class="glyphicon glyphicon-sort-by-alphabet-alt pull-right" aria-hidden="true"></span> + </a> + </th> + <th>Role + <a class="text-danger" ng-click="ctrl.sortBy('role')" ng-class="{ 'hidden': !ctrl.sorting['role']}" > + <span class="glyphicon glyphicon-sort-by-alphabet pull-right" aria-hidden="true"></span> + </a> + <a class="text-danger" ng-click="ctrl.sortBy('role')" ng-class="{ 'hidden': ctrl.sorting['role']}" > + <span class="glyphicon glyphicon-sort-by-alphabet-alt pull-right" aria-hidden="true"></span> + </a> + </th> + <th>Mode + <a class="text-danger" ng-click="ctrl.sortBy('mode')" ng-class="{ 'hidden': !ctrl.sorting['mode'] }" > + <span class="glyphicon glyphicon-sort-by-alphabet pull-right" aria-hidden="true"></span> + </a> + <a class="text-danger" ng-click="ctrl.sortBy('mode')" ng-class="{ 'hidden': ctrl.sorting['mode'] }" > + <span class="glyphicon glyphicon-sort-by-alphabet-alt pull-right" aria-hidden="true"></span> + </a> + </th> <th ng-class="{ 'hidden': !auth.isAuthenticated }">Operation</th> </tr> </thead> @@ -67,9 +77,7 @@ <td>{{pod.mode}}</td> <td ng-class="{ 'hidden': !auth.isAuthenticated }"> <span class="podsTable-col"> - <a class="text-warning" title="Edit"> - <i class="fa fa-pencil-square-o"></i></a> - <a class="text-danger" ng-click="ctrl.openDeleteModal(pod.name)" title="Delete"> + <a class="text-danger" ng-click="ctrl.openDeleteModal(pod.name)" title="Delete" ng-class="{ 'hidden': !auth.isAuthenticated }"> <i class="fa fa-trash-o"></i></a> </span> </td> diff --git a/testapi/opnfv_testapi/ui/components/pods/podsController.js b/testapi/opnfv_testapi/ui/components/pods/podsController.js index c66873a..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' + 'confirmModal', 'keepState', 'sortService', '$timeout' ]; /** @@ -30,23 +30,41 @@ * through pods declared in TestAPI. */ function PodsController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, - raiseAlert, confirmModal) { + raiseAlert, confirmModal, keepState, sortService, $timeout) { var ctrl = this; ctrl.url = testapiApiUrl + '/pods'; ctrl.checkBox = [] ctrl.checkBoxList = []; + ctrl.sorting = {}; ctrl.create = create; ctrl.listPods = listPods; ctrl.open = open; ctrl.filter = 'name' - ctrl.clearFilters = clearFilters; ctrl.openDeleteModal = openDeleteModal ctrl.openBatchDeleteModal = openBatchDeleteModal ctrl.openCreateModal = openCreateModal ctrl.podDelete = podDelete 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] ) + ctrl.sorting[field]=!ctrl.sorting[field] + } /** * This is called when the date filter calendar is opened. It @@ -61,13 +79,6 @@ ctrl[openVar] = true; } - /** - * This function will clear all filters and update the results - * listing. - */ - function clearFilters() { - ctrl.listPods(); - } /** * This will contact the TestAPI to create a new pod. @@ -75,7 +86,6 @@ function create(pod) { ctrl.showError = false; ctrl.showSuccess = false; - console.log(pod); if(pod.name != ""){ var pods_url = ctrl.url; var body = { @@ -84,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 = "Error creating the new pod from server: " + data.statusText; + ctrl.error = data.statusText; + ctrl.toastError() }); + return ctrl.podsRequest } else{ - ctrl.showError = true; ctrl.error = 'Name is missing.' + ctrl.toastError() } } @@ -105,21 +117,33 @@ */ function listPods() { ctrl.showError = false; + var reqURL = ctrl.url; + if(ctrl.filterText!=undefined){ + reqURL = ctrl.url + "?name=" + ctrl.filterText + } + else if(keepState.filter.podFilter){ + for (var filter in keepState.filter.podFilter){ + reqURL = ctrl.url + '?' + filter + '=' + keepState.filter.podFilter[filter] + ctrl.filterText = keepState.filter.podFilter[filter] + } + } ctrl.podsRequest = - $http.get(ctrl.url).success(function (data) { + $http.get(reqURL).success(function (data) { ctrl.data = data; - // mapNametoRandom - }).error(function (error) { + ctrl.sortBy("name") + if(ctrl.filterText != undefined){ + keepState.filter.podFilter = { + 'name': ctrl.filterText + } + } + }).catch(function (data) { ctrl.data = null; - ctrl.showError = true; - ctrl.error = - 'Error retrieving pods from server: ' + - angular.toJson(error); + ctrl.error = data.statusText; + ctrl.toastError() }); } function viewPod(name){ - console.log('hello'); $state.go('pod', {'name':name}, {reload: true}); } /** @@ -129,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() }); } @@ -144,7 +168,6 @@ function batchDelete(){ var index; var checkedBox = []; - console.log(ctrl.checkBox) for(index in ctrl.checkBox){ if(!ctrl.showError){ if(ctrl.checkBox[index]){ @@ -160,7 +183,13 @@ * message */ function openBatchDeleteModal() { - confirmModal("Delete",ctrl.batchDelete); + var deleteObjects = [] + for(var index in ctrl.checkBox){ + if(ctrl.checkBox[index]){ + deleteObjects.push(ctrl.data.pods[index].name) + } + } + confirmModal("Delete", 'pods', ctrl.batchDelete, deleteObjects); } /** @@ -168,8 +197,7 @@ * message */ function openDeleteModal(name) { - console.log(name) - confirmModal("Delete", ctrl.podDelete, name); + confirmModal("Delete", 'pod', ctrl.podDelete, name); } /** @@ -192,22 +220,6 @@ }); } - // function openUpdateModal(podName){ - // $uibModal.open({ - // templateUrl: 'testapi-ui/components/pods/modals/createModal.html', - // controller: 'PodModalCtrl as PodModalCtrl', - // size: 'md', - // resolve: { - // data: function () { - // return { - // text: "Update", - // successHandler: ctrl.update, - // // pod: ctrl. - // }; - // } - // } - // }); - // } ctrl.listPods(); } @@ -249,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/profile/profileController.js b/testapi/opnfv_testapi/ui/components/profile/profileController.js index 5dbdf7b..e34e745 100644 --- a/testapi/opnfv_testapi/ui/components/profile/profileController.js +++ b/testapi/opnfv_testapi/ui/components/profile/profileController.js @@ -35,7 +35,7 @@ ProfileController.$inject = [ '$scope', '$http', 'testapiApiUrl', 'PubKeys', - '$uibModal', 'raiseAlert', '$state' + '$uibModal', 'raiseAlert', '$state' , 'authenticate' ]; /** @@ -44,7 +44,7 @@ * account-specific information. */ function ProfileController($scope, $http, testapiApiUrl, - PubKeys, $uibModal, raiseAlert, $state) { + PubKeys, $uibModal, raiseAlert, $state, authenticate) { var ctrl = this; @@ -53,7 +53,7 @@ ctrl.openShowPubKeyModal = openShowPubKeyModal; // Must be authenticated to view this page. - if (!$scope.auth.isAuthenticated) { + if (!$scope.auth.isAuthenticated || !authenticate) { $state.go('home'); } @@ -115,8 +115,9 @@ ctrl.updatePubKeys(); }); } - - ctrl.authRequest = $scope.auth.doSignCheck().then(ctrl.updatePubKeys); + if(authenticate){ + ctrl.authRequest = $scope.auth.doSignCheck().then(ctrl.updatePubKeys); + } } angular diff --git a/testapi/opnfv_testapi/ui/components/projects/project/project.html b/testapi/opnfv_testapi/ui/components/projects/project/project.html index 2921bd9..2762fff 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/project.html +++ b/testapi/opnfv_testapi/ui/components/projects/project/project.html @@ -16,25 +16,11 @@ <div class="table-responsive"> <table class="table" ng-data="ctrl.data.pods"> <tbody> - <tr style="padding:9px"> - <td class="podsTableTd">Id :</td> - <td class="podsTableLeftTd">{{ctrl.data._id}}</td> + <tr ng-repeat-start="(index, detail) in ctrl.data_field" style="padding:9px"> + <td class="podsTableTd">{{detail | capitalize}} :</td> + <td width="85%" class="podsTableLeftTd">{{ctrl.data[index]}}</td> </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Name :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Owner :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.owner}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Created at :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data['creation_date']}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Description :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.description}}</td> + <tr ng-repeat-end=> </tr> </tbody> </table> diff --git a/testapi/opnfv_testapi/ui/components/projects/project/projectController.js b/testapi/opnfv_testapi/ui/components/projects/project/projectController.js index 78b805d..9dc9e27 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/projectController.js +++ b/testapi/opnfv_testapi/ui/components/projects/project/projectController.js @@ -21,7 +21,7 @@ ProjectController.$inject = [ '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', - 'confirmModal' + 'confirmModal', 'dataFieldService' ]; /** @@ -30,12 +30,13 @@ * through projects declared in TestAPI. */ function ProjectController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, - raiseAlert, confirmModal) { + raiseAlert, confirmModal, dataFieldService) { var ctrl = this; ctrl.name = $state.params['name']; ctrl.url = testapiApiUrl + '/projects/' + ctrl.name; ctrl.loadDetails = loadDetails; + ctrl.data_field = {} /** * This will contact the TestAPI to get a listing of declared projects. @@ -45,6 +46,7 @@ ctrl.projectsRequest = $http.get(ctrl.url).success(function (data) { ctrl.data = data; + ctrl.data_field = dataFieldService.dataFunction(ctrl.data, ctrl.data_field) }).catch(function (error) { ctrl.data = null; ctrl.showError = true; diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html index 70c026a..509f39a 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCase.html @@ -3,69 +3,11 @@ <div class="table-responsive"> <table class="table" ng-data="ctrl.data"> <tbody> - <tr style="padding:9px"> - <td class="podsTableTd">Id :</td> - <td class="podsTableLeftTd">{{ctrl.data._id}}</td> + <tr ng-repeat-start="(index, detail) in ctrl.data_field" style="padding:9px"> + <td class="podsTableTd" >{{detail | capitalize}} :</td> + <td width="86%" class="podsTableLeftTd">{{ctrl.data[index]}}</td> </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Name :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Project :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.project_name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Tier :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.tier}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Blocking :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.blocking}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">CI Loop :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.ci_loop}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Tags :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.tags}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Version :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.version}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Created at :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data['creation_date']}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Dependencies :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.dependencies}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Trust :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.trust}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Criteria :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.criteria}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Catalog Description :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.catalog_description}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">URL :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.url}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Run :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.run}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Description :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.description}}</td> + <tr ng-repeat-end=> </tr> </tbody> </table> diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js index a38b633..6f93fc1 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCase/testCaseController.js @@ -21,7 +21,7 @@ TestCaseController.$inject = [ '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', - 'confirmModal' + 'confirmModal', 'dataFieldService' ]; /** @@ -30,13 +30,14 @@ * through projects declared in TestAPI. */ function TestCaseController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, - raiseAlert, confirmModal) { + raiseAlert, confirmModal, dataFieldService) { var ctrl = this; ctrl.name = $state.params['name']; ctrl.projectName = $state.params['project_name']; ctrl.url = testapiApiUrl + '/projects/' + ctrl.projectName + "/cases/" + ctrl.name; ctrl.loadDetails = loadDetails; + ctrl.data_field = {} /** * This will contact the TestAPI to get a listing of declared projects. @@ -46,6 +47,7 @@ ctrl.projectsRequest = $http.get(ctrl.url).success(function (data) { ctrl.data = data; + ctrl.data_field = dataFieldService.dataFunction(ctrl.data, ctrl.data_field) }).catch(function (error) { ctrl.data = null; ctrl.showError = true; diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html index 00dd0f1..04baa9c 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCases.html @@ -1,68 +1,64 @@ <div ng-controller="TestCasesController as testCasesCtrl" class="col-md-12"> -<div class="row podsTable" style="vertical-align:middle"> - <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && - auth.isAuthenticated) }" > - <button type="button" class="btn btn-danger" ng-click="testCasesCtrl.openBatchDeleteModal()"> - <i class="fa fa-minus"></i> Delete</button> - </div> - <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && - auth.isAuthenticated) }"> - <button type="button" class="btn btn-success" ng-click="testCasesCtrl.openCreateModal()"> - <i class="fa fa-plus"></i> Create</button> - </div> -</div> -<div class='clo-md-12'> - <div ng-show="testCasesCtrl.showError" class="alert alert-danger" role="alert"> - <span class="pull-right"> {{testCasesCtrl.error}}</span> - <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> - </div> - <div ng-show="testCasesCtrl.showSuccess" class="alert alert-success" role="alert"> - <span class="pull-right"> {{testCasesCtrl.successMessage}}</span> - <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span> - </div> -</div> -<div class='clo-md-12' style="padding-right:0px"> - <div class="table-responsive"> - <table class="table table-bordered table-hover" ng-data="testCasesCtrl.data.testcases"> - <thead> - <tr style=" - text-align: center;"> - <th style="width: 1%;">Bulk Select</th> - <th style="width: 19%;">Name</th> - <th style="width: 20%;">Tier</th> - <th style="width: 20%;">Blocking</th> - <th style="width: 20%;">CI Loop</th> - <th style="width: 20%;" ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated)}">Operations</th> - </tr> - </thead> - <tbody> - <tr ng-repeat-start="(index, testcase) in testCasesCtrl.data.testcases" style="padding:9px"> - <td> - <div class="text-center"> - <input type="checkbox" value="{{project.name}}" ng-model="testCasesCtrl.checkBox[index]" > - </div> - </td> - <td> - <a class="text-info" ng-click="testCasesCtrl.viewTestCase(testcase.name, testcase.project_name)"> - {{testcase.name}} - </a> - </td> - <td>{{ ctrl.data.tier}}</td> - <td>{{ctrl.data.blocking}}</td> - <td>{{ctrl.data.ci_loop}}</td> - <td ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated)}"> - <span class="podsTable-col"> - <a class="text-warning" ng-click="testCasesCtrl.openUpdateTestModal(testcase.name)" title="Edit"> - <i class="fa fa-pencil-square-o"></i></a> - <a class="text-danger" ng-click="testCasesCtrl.openDeleteTestModal(testcase.name)" title="Delete"> - <i class="fa fa-trash-o"></i></a> - </span> - </td> - </tr> - <tr ng-repeat-end=> - </tr> - </tbody> - </table> - </div> -</div> -</div> + <div class="row podsTable" style="vertical-align:middle"> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': !((auth.projectNames.length>0) && + auth.isAuthenticated) && authenticate }" > + <button type="button" class="btn btn-danger" ng-click="testCasesCtrl.openBatchDeleteModal()"> + <i class="fa fa-minus"></i> Delete</button> + </div> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && + auth.isAuthenticated) && authenticate }"> + <button type="button" class="btn btn-success" ng-click="testCasesCtrl.openCreateModal()"> + <i class="fa fa-plus"></i> Create</button> + </div> + </div> + <div ng-class="{'show': testCasesCtrl.showError}" id="toast" class="error"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true" ></span> + {{testCasesCtrl.error}}</div> + <div ng-class="{'show': testCasesCtrl.showSuccess}" id="toast" class="success"> + <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> + {{testCasesCtrl.success}}</div> + <div class='clo-md-12' style="padding-right:0px"> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="testCasesCtrl.data.testcases"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 1%;">Bulk Select</th> + <th style="width: 19%;">Name</th> + <th style="width: 20%;">Tier</th> + <th style="width: 20%;">Blocking</th> + <th style="width: 20%;">CI Loop</th> + <th style="width: 20%;" ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated) && authenticate}">Operations</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="(index, testcase) in testCasesCtrl.data.testcases" style="padding:9px"> + <td> + <div class="text-center"> + <input type="checkbox" value="{{project.name}}" ng-model="testCasesCtrl.checkBox[index]" > + </div> + </td> + <td> + <a class="text-info" ng-click="testCasesCtrl.viewTestCase(testcase.name, testcase.project_name)"> + {{testcase.name}} + </a> + </td> + <td>{{testcase.tier}}</td> + <td>{{testcase.blocking}}</td> + <td>{{testcase.ci_loop}}</td> + <td ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated) && authenticate}"> + <span class="podsTable-col"> + <a class="text-warning" ng-click="testCasesCtrl.openUpdateTestModal(testcase.name)" title="Edit"> + <i class="fa fa-pencil-square-o"></i></a> + <a class="text-danger" ng-click="testCasesCtrl.openDeleteTestModal(testcase.name)" title="Delete"> + <i class="fa fa-trash-o"></i></a> + </span> + </td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js index 0045284..ea0498a 100644 --- a/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js +++ b/testapi/opnfv_testapi/ui/components/projects/project/testCases/testCasesController.js @@ -21,7 +21,7 @@ TestCasesController.$inject = [ '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', - 'confirmModal' + 'confirmModal', 'authenticate', '$timeout' ]; /** @@ -31,7 +31,7 @@ * in them. */ function TestCasesController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, - raiseAlert, confirmModal) { + raiseAlert, confirmModal, authenticate, $timeout) { var ctrl = this; ctrl.loadDetails = loadDetails; ctrl.name = $state.params['name']; @@ -49,29 +49,40 @@ ctrl.checkBox = []; ctrl.checkBoxList = []; + 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); + } /** * This will contact the TestAPI to create a new test case. */ function createTestCase(name, testcase) { - ctrl.showError = false; - ctrl.showSuccess = false; if(testcase.name != "" && testcase.name!=null){ var testCase_url = ctrl.requestUrl; - ctrl.testCasesRequest = - $http.post(testCase_url, testcase).success(function (data){ - ctrl.showSuccess = true ; - ctrl.successMessage = "Testcase is successfully created." + ctrl.testCasesRequest = $http.post(testCase_url, testcase) + ctrl.testCasesRequest.success(function (data){ + ctrl.success = "Testcase is successfully created." loadDetails(); + ctrl.toastSuccess() }) .catch(function (data) { - ctrl.showError = true; ctrl.error = data.statusText; + ctrl.toastError(); }); + return ctrl.testCasesRequest; } else{ - ctrl.showError = true; ctrl.error = 'Name is missing.' + ctrl.toastError(); } } @@ -84,7 +95,15 @@ * message */ function openBatchDeleteModal() { - confirmModal("Delete",ctrl.batchDelete); + var deleteObjects = [] + ctrl.checkBox.forEach(function(testcase, index){ + if(!ctrl.showError){ + if(testcase){ + deleteObjects.push(ctrl.data.testcases[index].name) + } + } + }); + confirmModal("Delete", 'testcases', ctrl.batchDelete, deleteObjects); } /** @@ -106,24 +125,23 @@ * This will contact the TestAPI to update an existing test case. */ function updateTestCase(name, testCase) { - ctrl.showError = false; - ctrl.showSuccess = false; if(testCase.name != ""){ var testCase_url = ctrl.requestUrl + '/' + name; - ctrl.testCasesRequest = - $http.put(testCase_url, testCase).success(function (data){ - ctrl.showSuccess = true ; - ctrl.successMessage = "Test case is successfully updated" + ctrl.testCasesRequest = $http.put(testCase_url, testCase) + ctrl.testCasesRequest.success(function (data){ + ctrl.success = "Test case is successfully updated" loadDetails(); + ctrl.toastSuccess(); }) .catch(function (data) { - ctrl.showError = true; ctrl.error = data.statusText; + ctrl.toastError() }); + return ctrl.testCasesRequest; } else{ - ctrl.showError = true; ctrl.error = 'Name is missing.' + ctrl.toastError() } } @@ -131,16 +149,14 @@ * This will contact the TestAPI to delete an existing test case. */ function deleteTestCase(name) { - ctrl.showError = false; - ctrl.showSuccess = false; ctrl.testCasesRequest = $http.delete(ctrl.requestUrl+"/"+name).success(function (data) { loadDetails(); - ctrl.showSuccess = true ; - ctrl.successMessage = "Test case is successfully deleted" + ctrl.success = "Test case is successfully deleted"; + ctrl.toastSuccess(); }).catch(function (error) { - ctrl.showError = true; - ctrl.error = data.statusText; + ctrl.error = error.statusText; + ctrl.toastError(); }); } @@ -149,7 +165,7 @@ * message */ function openDeleteTestModal(name) { - confirmModal("Delete", ctrl.deleteTestCase, name); + confirmModal("Delete", 'testcases', ctrl.deleteTestCase, name); } /** @@ -210,8 +226,8 @@ ctrl.data = data; }).catch(function (error) { ctrl.data = null; - ctrl.showError = true; ctrl.error = error.statusText; + ctrl.toastError() }); } ctrl.loadDetails(); @@ -244,12 +260,13 @@ {label: 'blocking', type: 'text', required: false}, {label: 'criteria', type: 'text', required: false}, {label: 'domains', type: 'text', required: false}, + {label: 'ci_loop', type: 'text', required: false}, {label: 'catalog_description', type: 'text', required: false} ]; ctrl.testcase = {name : null, description : null,version : null, tier : null, tags : null, run : null, dependencies : null, trust : null, url : null, blocking : null, - criteria : null, domains : null, catalog_description : null}; + criteria : null, domains : null, ci_loop: null, catalog_description : null}; if(ctrl.data.text=="Update"){ ctrl.testcase = ctrl.data.testCase @@ -262,9 +279,15 @@ * inputs. */ function confirm() { - $uibModalInstance.close(); if (angular.isDefined(ctrl.data.successHandler)) { - ctrl.data.successHandler(ctrl.name, ctrl.testcase); + if(ctrl.testcase.name){ + ctrl.data.successHandler(ctrl.name, ctrl.testcase).success( function(){ + $uibModalInstance.close(); + }) + } + else{ + ctrl.data.successHandler(ctrl.name, ctrl.testcase) + } } } diff --git a/testapi/opnfv_testapi/ui/components/projects/projects.html b/testapi/opnfv_testapi/ui/components/projects/projects.html index 8a27a57..3098aa2 100644 --- a/testapi/opnfv_testapi/ui/components/projects/projects.html +++ b/testapi/opnfv_testapi/ui/components/projects/projects.html @@ -2,13 +2,13 @@ <div class="row" style="margin-bottom:24px;"></div> <div class="row podsTable" style="vertical-align:middle"> - <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && - auth.isAuthenticated) }" > + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': !((auth.projectNames.length>0) && + auth.isAuthenticated) && authenticate}" > <button type="button" class="btn btn-danger" ng-click="ctrl.openBatchDeleteModal()"> <i class="fa fa-minus"></i> Delete</button> </div> <div class="col-sm-1 pull-right" ng-class="{ 'hidden': ! ((auth.projectNames.length>0) && - auth.isAuthenticated) }"> + auth.isAuthenticated) && authenticate}"> <button type="button" class="btn btn-success" ng-click="ctrl.openCreateModal()"> <i class="fa fa-plus"></i> Create</button> </div> @@ -18,19 +18,15 @@ </div> <div class="col-sm-3 pull-right"> <span style="margin-top:6px">Search: </span> - <input type="text" class="form-control search" ng-Model="ctrl.filterText" style="width:80%;" placeholder="Search By Name"> - </div> -</div> -<div class='clo-md-12'> - <div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> - <span class="pull-right"> {{ctrl.error}}</span> - <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> - </div> - <div ng-show="ctrl.showCreateSuccess" class="alert alert-success" role="alert"> - <span class="pull-right"> {{ctrl.success}}</span> - <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span> + <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 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"> @@ -38,9 +34,16 @@ <tr style=" text-align: center;"> <th style="width: 1%;">Bulk Select</th> - <th style="width: 19%;">Name</th> + <th style="width: 19%;">Name + <a class="text-danger" ng-click="ctrl.sortByName()" ng-class="{ 'hidden': !ctrl.sortName }" > + <span class="glyphicon glyphicon-sort-by-alphabet pull-right" aria-hidden="true"></span> + </a> + <a class="text-danger" ng-click="ctrl.sortByName()" ng-class="{ 'hidden': ctrl.sortName }" > + <span class="glyphicon glyphicon-sort-by-alphabet-alt pull-right" aria-hidden="true"></span> + </a> + </th> <th style="width: 70%;">Description</th> - <th style="width: 10%;" ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated)}">Operations</th> + <th style="width: 10%;" ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated) && authenticate}">Operations</th> </tr> </thead> <tbody> @@ -52,7 +55,7 @@ </td> <td><a class="text-info" ng-click="ctrl.viewProject(project.name)">{{project.name}}</a></td> <td>{{project.description}}</td> - <td ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated)}"> + <td ng-class="{'hidden': ! ((auth.projectNames.length>0) && auth.isAuthenticated) && authenticate}"> <span class="podsTable-col"> <a class="text-warning" ng-click="ctrl.openUpdateModal(project.name)" title="Edit" > <i class="fa fa-pencil-square-o"></i></a> diff --git a/testapi/opnfv_testapi/ui/components/projects/projectsController.js b/testapi/opnfv_testapi/ui/components/projects/projectsController.js index 38764ea..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' + '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) { + raiseAlert, confirmModal, authenticate, keepState, sortService, $timeout) { var ctrl = this; ctrl.url = testapiApiUrl + '/projects'; @@ -44,33 +44,53 @@ ctrl.openBatchDeleteModal = openBatchDeleteModal; ctrl.projectDelete = projectDelete; ctrl.batchDelete = batchDelete; + ctrl.sortByName = sortByName ctrl.checkBox = []; ctrl.checkBoxList = []; ctrl.name = ''; ctrl.details = ''; - ctrl.filterText=''; + 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.showCreateSuccess = 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.showCreateSuccess = 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(){ + ctrl.data.projects = sortService.sortFunction(ctrl.data.projects, 'name', ctrl.ascending) + ctrl.ascending = !ctrl.ascending } /** @@ -129,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 } /** @@ -148,17 +169,28 @@ ctrl.showError = false; var content_url = ctrl.url + '?'; var filterText = ctrl.filterText; - if(filterText != ''){ + if(filterText != undefined){ content_url = content_url + 'name=' + filterText; } + else if(keepState.filter.projectFilter){ + for (var filter in keepState.filter.projectFilter){ + content_url = content_url + filter + '=' + keepState.filter.projectFilter[filter] + ctrl.filterText = keepState.filter.projectFilter[filter] + } + } ctrl.resultsRequest = $http.get(content_url).success(function (data) { ctrl.data = data; + if(ctrl.filterText != undefined){ + keepState.filter.projectFilter = { + 'name': ctrl.filterText + } + } }).catch(function (data) { ctrl.data = null; - ctrl.showError = true; ctrl.error = data.statusText; + ctrl.toastError(); }); } @@ -173,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(); }); } @@ -204,7 +235,15 @@ * message */ function openBatchDeleteModal() { - confirmModal("Delete",ctrl.batchDelete); + var deleteObjects = [] + ctrl.checkBox.forEach(function(project, index){ + if(!ctrl.showError){ + if(project){ + deleteObjects.push(ctrl.data.projects[index].name) + } + } + }); + confirmModal("Delete", 'projects', ctrl.batchDelete, deleteObjects); } /** @@ -212,7 +251,7 @@ * message */ function openDeleteModal(name) { - confirmModal("Delete", ctrl.projectDelete, name); + confirmModal("Delete",'projects', ctrl.projectDelete, name); } ctrl.listProjects(); @@ -252,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/components/results/result/result.html b/testapi/opnfv_testapi/ui/components/results/result/result.html index b435dce..041f244 100644 --- a/testapi/opnfv_testapi/ui/components/results/result/result.html +++ b/testapi/opnfv_testapi/ui/components/results/result/result.html @@ -3,100 +3,16 @@ <div class="table-responsive"> <table class="table" ng-data="ctrl.data.pods"> <tbody> - <tr style="padding:9px"> - <td class="podsTableTd">Id :</td> - <td class="podsTableLeftTd">{{ctrl.data._id}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Pod Name:</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.pod_name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Project Name:</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.project_name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Case Name :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.case_name}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Installer :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.installer}}</td> + <tr ng-repeat-start="(index, detail) in ctrl.data_field" style="padding:9px"> + <td class="podsTableTd">{{detail | capitalize}} :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data[index]}}</td> </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Version :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.version}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Scenario :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.scenario}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Build tag :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data['build_tag']}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Criteria :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.criteria}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Start Date:</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.start_date}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Stop Date :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.stop_date}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Trust Indicator :</td> - <td width="90%" class="podsTableLeftTd"> - <a ng-click="ctrl.showTrustIndicator()"> - <p ng-if="ctrl.trust_indicator">Hide</p> - <p ng-if="!ctrl.trust_indicator">Show</p> - </a> - <table class="table" ng-class="{'hidden' : !ctrl.trust_indicator}" style="margin:10px"> - <tbody> - <tr style="padding:9px"></tr> - <tr style="padding:9px" > - <td class="podsTableTd">Current :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.trust_indicator.current}}</td> - </tr> - <tr style="padding:9px" > - <td class="podsTableTd">Histories :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.trust_indicator.histories}}</td> - </tr> - </tbody> - </table> - </td> + <tr ng-repeat-end=> </tr> <tr style="padding:9px"> <td class="podsTableTd">Details :</td> <td width="90%" class="podsTableLeftTd"> - <a ng-click="ctrl.showDetails()"> - <p ng-if="ctrl.details">Hide</p> - <p ng-if="!ctrl.details">Show</p> - </a> - <table class="table" ng-class="{'hidden' : !ctrl.details}" style="margin:10px"> - <tbody> - <tr style="padding:9px"></tr> - <tr style="padding:9px"> - <td class="podsTableTd">Failures :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.details.failures}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Details :</td> - <td width="90%" class="podsTableLeftTd">{{ctrl.data.details.errors}}</td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">Stream :</td> - <td width="90%" class="podsTableLeftTd"><p>{{ctrl.data.details.stream}}</p></td> - </tr> - <tr style="padding:9px"> - <td class="podsTableTd">TestsRun :</td> - <td width="90%" class="podsTableLeftTd"><p>{{ctrl.data.details.testsRun}}</p></td> - </tr> - </tbody> - </table> + <json-tree object="ctrl.json.object" root-name="object" start-expanded="true"></json-tree> </td> </tr> </tbody> diff --git a/testapi/opnfv_testapi/ui/components/results/result/resultController.js b/testapi/opnfv_testapi/ui/components/results/result/resultController.js index 028e5d8..2726704 100644 --- a/testapi/opnfv_testapi/ui/components/results/result/resultController.js +++ b/testapi/opnfv_testapi/ui/components/results/result/resultController.js @@ -21,7 +21,7 @@ ResultController.$inject = [ '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', - 'confirmModal' + 'confirmModal', 'dataFieldService' ]; /** @@ -30,13 +30,16 @@ * through result declared in TestAPI. */ function ResultController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, - raiseAlert, confirmModal) { + raiseAlert, confirmModal, dataFieldService) { var ctrl = this; ctrl.url = testapiApiUrl + '/results'; ctrl._id = $state.params['_id']; ctrl.loadDetails = loadDetails - ctrl.showTrustIndicator = showTrustIndicator - ctrl.showDetails = showDetails + ctrl.data_field = {} + + ctrl.json = {}; + ctrl.json.string = '{"id": ""}'; + ctrl.json.object = JSON.parse(ctrl.json.string); /** *Contact the testapi and retrevie the result details @@ -47,6 +50,10 @@ ctrl.podsRequest = $http.get(resultUrl).success(function (data) { ctrl.data = data; + ctrl.object=JSON.stringify(ctrl.data.details) + ctrl.json.object = JSON.parse(ctrl.object) + delete ctrl.data.details; + ctrl.data_field = dataFieldService.dataFunction(ctrl.data, ctrl.data_field) }).catch(function (error) { ctrl.data = null; ctrl.showError = true; @@ -54,21 +61,6 @@ }); } - function showTrustIndicator(){ - if(ctrl.trust_indicator){ - ctrl.trust_indicator = false - }else{ - ctrl.trust_indicator = true - } - } - - function showDetails(){ - if(ctrl.details){ - ctrl.details = false - }else{ - ctrl.details = true - } - } ctrl.loadDetails(); } })();
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/results/results.html b/testapi/opnfv_testapi/ui/components/results/results.html index 7809065..a433525 100644 --- a/testapi/opnfv_testapi/ui/components/results/results.html +++ b/testapi/opnfv_testapi/ui/components/results/results.html @@ -1,22 +1,5 @@ <h3>{{ctrl.pageHeader}}</h3> <p>{{ctrl.pageParagraph}}</p> -<form class="form-inline" ng-show="ctrl.isUserResults"> -<h4>Upload Results</h4> -<div class="form-group col-m-3"> - <input class="form-contrl btn btn-default" type = "file" file-model = "resultFile"/> -</div> -<div class="checkbox col-m-1"> - <label> - <input type="checkbox" ng-model="ctrl.isPublic">public - </label> -</div> -<div class="form-group col-m-3"> - <button class="btn btn-primary" ng-click = "ctrl.uploadFile()">upload result</button> -</div> -<div> -<lable>{{ctrl.uploadState}}</label> -</div> -</form> <div class="row" style="margin-bottom:24px;"></div> <div class="result-filters" style="border-top: none;"> <div class="row podTable" style="vertical-align:middle"> @@ -31,13 +14,17 @@ </div> <div class="col-sm-2 pull-right" ng-class="{'hidden': ctrl.filter=='start_date' || ctrl.filter=='end_date'}"> <span style="margin-top:6px">Search: </span> - <input type="text" class="form-control search" style="display:inline;width:105px;padding-left:6px;" + <input list="filter" ng-enter="ctrl.filterList()" name="filter" class="form-control search" style="display:inline;width:105px;padding-left:6px;" ng-Model="ctrl.filterText" placeholder="Search String"> + <datalist id="filter" ng-class="{ 'hidden' : ctrl.filterOption.length<0}"> + <option ng-repeat="(index, filterValue) in ctrl.filterOption " value="{{filterValue}}">{{filterValue}}</option> + </datalist> </div> <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='start_date'}"> <span style="margin-top:6px">Start Date: </span> <p class="input-group" style="width:48%;display:inline-flex;"> <input type="text" class="form-control" + ng-enter="ctrl.filterList()" uib-datepicker-popup="{{ctrl.format}}" ng-model="ctrl.filterText" is-open="ctrl.startOpen" close-text="Close" /> @@ -52,6 +39,7 @@ <span style="margin-top:6px">End Date: </span> <p class="input-group" style="width:48%;display:inline-flex;"> <input type="text" class="form-control" + ng-enter="ctrl.filterList()" uib-datepicker-popup="{{ctrl.format}}" ng-model="ctrl.filterText" is-open="ctrl.endOpen" close-text="Close" /> @@ -64,7 +52,7 @@ </div> <div class="col-md-2 row pull-right" style="width: 20%;"> <span style="margin-top:6px">Filter: </span> - <select ng-model="ctrl.filter" class="form-control" style="display:inline; width:150px;"> + <select ng-model="ctrl.filter" ng-change="ctrl.encodeFilter()" class="form-control" style="display:inline; width:150px;"> <option value="pod" ng-disabled="ctrl.testFilter('pod')" >Pod Name</option> <option value="project" ng-disabled="ctrl.testFilter('project')" >Project Name</option> <option value="case" ng-disabled="ctrl.testFilter('case')">Case Name</option> @@ -79,9 +67,9 @@ </div> <div class='filter-box'> - <div class='filter-tag' ng-repeat="(key, tag) in ctrl.tagArray"> + <div class='filter-tag col-md-1' ng-repeat="(key, tag) in ctrl.tagArray" style="background-color: #f5f5f5;border: 1px solid #e3e3e3;/* border: 1px; */margin-top: 3px;padding: 4px;margin-left: 15px;width: 13%;"> {{key}} : {{tag}} - <div class='delete-tag' ng-click='ctrl.deleteTag(key)'> + <div class='delete-tag btn btn-danger btn-xs' ng-click='ctrl.deleteTag(key)'> × </div> </div> @@ -104,13 +92,12 @@ <th>Scenario</th> <th>Criteria</th> <th>Start Date</th> - <th>Stop Date</th> </tr> </thead> <tbody> <tr ng-repeat-start="(index, result) in ctrl.data.results"> - <td><a ng-click="ctrl.viewResult(result._id)">{{ result._id }}</a></td> + <td><a ng-click="ctrl.viewResult(result._id)">{{ result._id.substr(-8) }}</a></td> <td>{{ result.pod_name }}</td> <td>{{ result.project_name }}</td> <td>{{ result.case_name }}</td> @@ -118,8 +105,7 @@ <td>{{ result.version }}</td> <td>{{ result.scenario }}</td> <td>{{ result.criteria }}</td> - <td>{{ result.start_date }}</td> - <td>{{ result.stop_date }}</td> + <td>{{ result.start_date}}</td> </tr> <tr ng-repeat-end=> </tr> @@ -145,4 +131,4 @@ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <span class="sr-only">Error:</span> {{ctrl.error}} -</div> +</div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/results/resultsController.js b/testapi/opnfv_testapi/ui/components/results/resultsController.js index 55bf0ba..13ead6e 100644 --- a/testapi/opnfv_testapi/ui/components/results/resultsController.js +++ b/testapi/opnfv_testapi/ui/components/results/resultsController.js @@ -38,7 +38,8 @@ }]); ResultsController.$inject = [ - '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert' + '$scope', '$http', '$filter', '$state', 'testapiApiUrl', 'raiseAlert', + 'keepState' ]; /** @@ -47,24 +48,21 @@ * a listing of community uploaded results. */ function ResultsController($scope, $http, $filter, $state, testapiApiUrl, - raiseAlert) { + raiseAlert, keepState) { var ctrl = this; - ctrl.uploadFile=uploadFile; ctrl.open = open; ctrl.clearFilters = clearFilters; - ctrl.associateMeta = associateMeta; - ctrl.getVersionList = getVersionList; - ctrl.getUserProducts = getUserProducts; - ctrl.associateProductVersion = associateProductVersion; - ctrl.getProductVersions = getProductVersions; - ctrl.prepVersionEdit = prepVersionEdit; ctrl.deleteTag = deleteTag; ctrl.filterList= filterList; ctrl.testFilter = testFilter ctrl.viewResult = viewResult; + ctrl.filter = "pod" + ctrl.filterValue = "pod_name" + ctrl.encodeFilter = encodeFilter ctrl.tagArray = {} + ctrl.filterOption=[] /** Mappings of Interop WG components to marketing program names. */ ctrl.targetMappings = { @@ -102,12 +100,12 @@ /** Check to see if this page should display user-specific results. */ // ctrl.isUserResults = $state.current.name === 'userResults'; // need auth to browse - ctrl.isUserResults = $state.current.name === 'userResults'; + // ctrl.isUserResults = $state.current.name === 'userResults'; - // Should only be on user-results-page if authenticated. - if (ctrl.isUserResults && !$scope.auth.isAuthenticated) { - $state.go('home'); - } + // // Should only be on user-results-page if authenticated. + // if (ctrl.isUserResults && !$scope.auth.isAuthenticated) { + // $state.go('home'); + // } ctrl.pageHeader = "Test Results" @@ -116,16 +114,85 @@ 'The most recently uploaded community test results are listed ' + 'here.'; - ctrl.uploadState = ''; + // ctrl.uploadState = ''; ctrl.isPublic = false; - if (ctrl.isUserResults) { - ctrl.authRequest = $scope.auth.doSignCheck() - .then(ctrl.filterList); - // ctrl.getUserProducts(); - } else { - ctrl.filterList(); + // if (ctrl.isUserResults) { + // ctrl.authRequest = $scope.auth.doSignCheck() + // .then(ctrl.filterList); + // // ctrl.getUserProducts(); + // } else { + // ctrl.filterList(); + // } + + function encodeFilter(){ + ctrl.filterText = '' + ctrl.filterOption=[] + if(ctrl.filter=="pod" || ctrl.filter=="project" || ctrl.filter=="scenario"){ + var reqURL = testapiApiUrl +"/" + ctrl.filter + "s" + ctrl.datasRequest = + $http.get(reqURL).success(function (data) { + ctrl.filterData = data; + for(var index in ctrl.filterData[ctrl.filter + "s"]){ + if( ctrl.filterOption.indexOf(ctrl.filterData[ctrl.filter + "s"][index]["name"]) < 0){ + ctrl.filterOption.push(ctrl.filterData[ctrl.filter + "s"][index]["name"]) + } + } + }).catch(function (data) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = data.statusText; + }); + + } + else if(ctrl.filter=="case"){ + if("project" in ctrl.tagArray){ + var reqURL = testapiApiUrl +"/projects/"+ctrl.tagArray["project"]+"/cases" + ctrl.dataRequest = + $http.get(reqURL).success(function (data) { + ctrl.filterData = data; + for(var index in ctrl.filterData.testcases){ + if( ctrl.filterOption.indexOf(ctrl.filterData.testcases[index]["name"]) < 0){ + ctrl.filterOption.push(ctrl.filterData.testcases[index]["name"]) + } + } + }).catch(function (data) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = data.statusText; + }); + + } + else{ + var reqURL = testapiApiUrl +"/projects" + ctrl.dataRequest = + $http.get(reqURL).success(function (data) { + ctrl.projectsData = data; + for(var indexP in ctrl.projectsData.projects){ + reqURL = testapiApiUrl +"/projects/" + ctrl.projectsData.projects[indexP]["name"] +"/cases" + ctrl.datasRequest = + $http.get(reqURL).success(function (data) { + ctrl.filterData = data; + for(var index in ctrl.filterData.testcases){ + if( ctrl.filterOption.indexOf(ctrl.filterData.testcases[index]["name"]) < 0){ + ctrl.filterOption.push(ctrl.filterData.testcases[index]["name"]) + } + } + }).catch(function (data) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = data.statusText; + }); + } + }).catch(function (data) { + ctrl.data = null; + ctrl.showError = true; + ctrl.error = data.statusText; + }); + } + + } } function viewResult(_id){ @@ -146,44 +213,22 @@ return false; } - - function uploadFileToUrl(file, uploadUrl){ - var fd = new FormData(); - fd.append('file', file); - fd.append('public', ctrl.isPublic) - - $http.post(uploadUrl, fd, { - transformRequest: angular.identity, - headers: {'Content-Type': undefined} - }) - - .success(function(data){ - var id = data.href.substr(data.href.lastIndexOf('/')+1); - ctrl.uploadState = "Upload succeed. Result id is " + id; - ctrl.filterList(); - }) - - .error(function(data, status){ - ctrl.uploadState = "Upload failed. Error code is " + status; - }); - } - - function uploadFile(){ - var file = $scope.resultFile; - console.log('file is ' ); - console.dir(file); - - var uploadUrl = testapiApiUrl + "/results/upload"; - uploadFileToUrl(file, uploadUrl); - }; - /** * This will contact the TestAPI API to get a listing of test run * results. */ function filterList(){ - if(ctrl.filter && ctrl.filterText!=""){ + if(ctrl.filter && ctrl.filterText!="" && ctrl.filterText!=undefined){ ctrl.tagArray[ctrl.filter] = ctrl.filterText; + if(!keepState.filter.resultFilter){ + keepState.filter.resultFilter = {} + } + keepState.filter.resultFilter[ctrl.filter] = ctrl.filterText + } + else if(Object.keys(ctrl.tagArray).length==0){ + if(keepState.filter.resultFilter){ + ctrl.tagArray = keepState.filter.resultFilter + } } ctrl.showError = false; var content_url = testapiApiUrl + '/results' + @@ -214,6 +259,7 @@ ctrl.data = data; ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage; ctrl.currentPage = ctrl.data.pagination.current_page; + ctrl.encodeFilter(); }).error(function (error) { ctrl.data = null; ctrl.totalItems = 0; @@ -224,8 +270,10 @@ }); ctrl.filterText = '' } + ctrl.filterList(); + /** * This is called when the date filter calendar is opened. It * does some event handling, and sets a scope variable so the UI @@ -246,160 +294,8 @@ function clearFilters() { ctrl.tagArray = {} ctrl.filter = undefined + keepState.filter.resultFilter = {} ctrl.filterList(); } - - /** - * This will send an API request in order to associate a metadata - * key-value pair with the given testId - * @param {Number} index - index of the test object in the results list - * @param {String} key - metadata key - * @param {String} value - metadata value - */ - function associateMeta(index, key, value) { - var testId = ctrl.data.results[index].id; - var metaUrl = [ - testapiApiUrl, '/results/', testId, '/meta/', key - ].join(''); - - var editFlag = key + 'Edit'; - if (value) { - ctrl.associateRequest = $http.post(metaUrl, value) - .success(function () { - ctrl.data.results[index][editFlag] = false; - }).error(function (error) { - raiseAlert('danger', error.title, error.detail); - }); - } - else { - ctrl.unassociateRequest = $http.delete(metaUrl) - .success(function () { - ctrl.data.results[index][editFlag] = false; - }).error(function (error) { - if (error.code == 404) { - // Key doesn't exist, so count it as a success, - // and don't raise an alert. - ctrl.data.results[index][editFlag] = false; - } - else { - raiseAlert('danger', error.title, error.detail); - } - }); - } - } - - /** - * Retrieve an array of available capability files from the TestAPI - * API server, sort this array reverse-alphabetically, and store it in - * a scoped variable. - * Sample API return array: ["2015.03.json", "2015.04.json"] - */ - function getVersionList() { - if (ctrl.versionList) { - return; - } - var content_url = testapiApiUrl + '/guidelines'; - ctrl.versionsRequest = - $http.get(content_url).success(function (data) { - ctrl.versionList = data.sort().reverse(); - }).error(function (error) { - raiseAlert('danger', error.title, - 'Unable to retrieve version list'); - }); - } - - /** - * Get products user has management rights to or all products depending - * on the passed in parameter value. - */ - function getUserProducts() { - if (ctrl.products) { - return; - } - var contentUrl = testapiApiUrl + '/products'; - ctrl.productsRequest = - $http.get(contentUrl).success(function (data) { - ctrl.products = {}; - angular.forEach(data.products, function(prod) { - if (prod.can_manage) { - ctrl.products[prod.id] = prod; - } - }); - }).error(function (error) { - ctrl.products = null; - ctrl.showError = true; - ctrl.error = - 'Error retrieving Products listing from server: ' + - angular.toJson(error); - }); - } - - /** - * Send a PUT request to the API server to associate a product with - * a test result. - */ - function associateProductVersion(result) { - var verId = (result.selectedVersion ? - result.selectedVersion.id : null); - var testId = result.id; - var url = testapiApiUrl + '/results/' + testId; - ctrl.associateRequest = $http.put(url, {'product_version_id': - verId}) - .success(function (data) { - result.product_version = result.selectedVersion; - if (result.selectedVersion) { - result.product_version.product_info = - result.selectedProduct; - } - result.productEdit = false; - }).error(function (error) { - raiseAlert('danger', error.title, error.detail); - }); - } - - /** - * Get all versions for a product. - */ - function getProductVersions(result) { - if (!result.selectedProduct) { - result.productVersions = []; - result.selectedVersion = null; - return; - } - - var url = testapiApiUrl + '/products/' + - result.selectedProduct.id + '/versions'; - ctrl.getVersionsRequest = $http.get(url) - .success(function (data) { - result.productVersions = data; - - // If the test result isn't already associated to a - // version, default it to the null version. - if (!result.product_version) { - angular.forEach(data, function(ver) { - if (!ver.version) { - result.selectedVersion = ver; - } - }); - } - }).error(function (error) { - raiseAlert('danger', error.title, error.detail); - }); - } - - /** - * Instantiate variables needed for editing product/version - * associations. - */ - function prepVersionEdit(result) { - result.productEdit = true; - if (result.product_version) { - result.selectedProduct = - ctrl.products[result.product_version.product_info.id]; - } - result.selectedVersion = result.product_version; - ctrl.getProductVersions(result); - } - } -})(); +})();
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/customModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/customModal.html new file mode 100644 index 0000000..4225749 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/customModal.html @@ -0,0 +1,38 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{customModalCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <label for="cpid" class="control-label col-sm-2">Custom: </label> + <table cellpadding="0" cellspacing="0"> + <tbody> + <tr> + <td> + <div class="col-sm-12"> + <input type="text" class="form-control" ng-model="customModalCtrl.custom"/> + <p class="help-block"> + Please separate multiple customs with space or comma + </p> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <div ng-show="customModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{customModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="customModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="customModalCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/installerModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/installerModal.html new file mode 100644 index 0000000..4be1375 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/installerModal.html @@ -0,0 +1,59 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{installerModalCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Name: </label> + </div> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="installerModalCtrl.installer.installer"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Version: </label> + </div> + <div class="col-sm-6"> + <button class="btn btn-primary" ng-click="installerModalCtrl.openVersionModal()">Add Version</button> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </fieldset> + </div> + <div class='clo-md-12' style="padding-right:0px"> + <h3>Version</h3> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="installerModalCtrl.installer.versions"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 19%;">Version</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="(index, version) in installerModalCtrl.installer.versions" style="padding:9px"> + <td>{{version.version}}</td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="modal-footer"> + <div ng-show="installerModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{installerModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="installerModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="installerModalCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/projectModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/projectModal.html new file mode 100644 index 0000000..171cc33 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/projectModal.html @@ -0,0 +1,59 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{projectModalCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label"> Project Name: </label> + </div> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="projectModalCtrl.project.project"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label"> Custom: </label> + </div> + <div class="col-sm-6"> + <button class="btn btn-primary" ng-click="projectModalCtrl.openCustomModal()">Add Custom</button> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </fieldset> + </div> + <div class='clo-md-12' style="padding-right:0px"> + <h3>Customs</h3> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="projectModalCtrl.project.customs"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 1%;">Custom</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="custom in projectModalCtrl.project.customs" style="padding:9px"> + <td>{{custom}}</td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="modal-footer"> + <div ng-show="projectModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{projectModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="projectModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="projectModalCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/scenarioModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/scenarioModal.html new file mode 100644 index 0000000..2b5ba9e --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/scenarioModal.html @@ -0,0 +1,59 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{scenarioModalController.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Name: </label> + </div> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="scenarioModalController.scenario.name"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Installer: </label> + </div> + <div class="col-sm-6"> + <button class="btn btn-primary" ng-click="scenarioModalController.openInstallerModal()">Add Installer</button> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </fieldset> + </div> + <div class='clo-md-12' style="padding-right:0px"> + <h3>Installers</h3> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="scenarioModalController.scenario"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 19%;">Installers</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="(index, installer) in scenarioModalController.scenario.installers" style="padding:9px"> + <td>{{installer.installer}}</td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="modal-footer"> + <div ng-show="scenarioModalController.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{scenarioModalController.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="scenarioModalController.confirm()">Ok</button> + <button class="btn btn-default" ng-click="scenarioModalController.cancel()">Cancel</button> + </div> +</div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/scenarioNameUpdate.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/scenarioNameUpdate.html new file mode 100644 index 0000000..e793c05 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/scenarioNameUpdate.html @@ -0,0 +1,28 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{ScenarioNameUpdateCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <label for="cpid" class="control-label col-sm-4">Name: </label> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="ScenarioNameUpdateCtrl.name"/> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <div ng-show="ScenarioNameUpdateCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{ScenarioNameUpdateCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="ScenarioNameUpdateCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="ScenarioNameUpdateCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/scoreModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/scoreModal.html new file mode 100644 index 0000000..55b5cfd --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/scoreModal.html @@ -0,0 +1,51 @@ + + + <div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{scoreModalCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <label for="cpid" class="control-label col-sm-4">Score: </label> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="scoreModalCtrl.score.score"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div> + <div class="col-md-4" style="text-align:right;"> + <span style="margin-top:6px;">Date: </span> + </div> + <div class="col-md-6"> + <p class="input-group" style="display:inline-flex;"> + <input type="text" class="form-control" + uib-datepicker-popup="{{scoreModalCtrl.format}}" + ng-model="scoreModalCtrl.score.date" is-open="scoreModalCtrl.endOpen" + close-text="Close" /> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="scoreModalCtrl.open($event, 'endOpen')"> + <i class="glyphicon glyphicon-calendar"></i> + </button> + </span> + </p> + </div> + </div> + </div> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <div ng-show="scoreModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{scoreModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="scoreModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="scoreModalCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/trustIndicatorModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/trustIndicatorModal.html new file mode 100644 index 0000000..b84bd5d --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/trustIndicatorModal.html @@ -0,0 +1,49 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{trustIndicatorModalCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <label for="cpid" class="control-label col-sm-4">Status: </label> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="trustIndicatorModalCtrl.ti.status"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div> + <div class="col-md-4" style="text-align:right;"> + <span style="margin-top:6px;">Date: </span> + </div> + <div class="col-md-6"> + <p class="input-group" style="display:inline-flex;"> + <input type="text" class="form-control" + uib-datepicker-popup="{{trustIndicatorModalCtrl.format}}" + ng-model="trustIndicatorModalCtrl.ti.date" is-open="trustIndicatorModalCtrl.endOpen" + close-text="Close" /> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="trustIndicatorModalCtrl.open($event, 'endOpen')"> + <i class="glyphicon glyphicon-calendar"></i> + </button> + </span> + </p> + </div> + </div> + </div> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <div ng-show="trustIndicatorModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{trustIndicatorModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="trustIndicatorModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="trustIndicatorModalCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/modals/versionModal.html b/testapi/opnfv_testapi/ui/components/scenarios/modals/versionModal.html new file mode 100644 index 0000000..f679f0b --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/modals/versionModal.html @@ -0,0 +1,68 @@ +<div class="ball" style="padding:5px;"> + <div class="modal-body"> + <div class="form-horizontal"> + <fieldset> + <div class="form-group"> + <legend>{{versionModalCtrl.data.text}}</legend> + <div class="row"> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Version: </label> + </div> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="versionModalCtrl.version.version"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Owner: </label> + </div> + <div class="col-sm-6"> + <input type="text" class="form-control" ng-model="versionModalCtrl.version.owner"/> + <p class="help-block"></p> + </div> + </div> + <div class="update-project"> + <div class="col-sm-4"> + <label for="cpid" class="control-label col-sm-2"> Project: </label> + </div> + <div class="col-sm-4"> + <button class="btn btn-primary" ng-click="versionModalCtrl.openProjectModal()">Add Project:</button> + <p class="help-block"></p> + </div> + </div> + </div> + </div> + </fieldset> + </div> + <div class='clo-md-12' style="padding-right:0px"> + <h3>Projects</h3> + <div class="table-responsive"> + <table class="table table-bordered table-hover" ng-data="versionModalCtrl.version.projects"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 19%;">Project</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="(index, project) in versionModalCtrl.version.projects" style="padding:9px"> + <td>{{project.project}}</td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div class="modal-footer"> + <div ng-show="versionModalCtrl.showCreateError" style="padding:0px;" class="col-md-6 alert alert-danger" role="alert"> + <span class="pull-right"> {{versionModalCtrl.error}}</span> + <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span> + </div> + <button class="btn btn-primary" ng-click="versionModalCtrl.confirm()">Ok</button> + <button class="btn btn-default" ng-click="versionModalCtrl.cancel()">Cancel</button> + </div> + </div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/scenario/scenario.html b/testapi/opnfv_testapi/ui/components/scenarios/scenario/scenario.html new file mode 100644 index 0000000..d6d4257 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/scenario/scenario.html @@ -0,0 +1,234 @@ +<legend>Scenario</legend> +<div style="padding-right:0px" class="col-md-12"> + <div class="table-responsive"> + <table class="table" ng-data="ctrl.data"> + <tbody> + <tr style="padding:9px"> + <td class="podsTableTd">Id :</td> + <td class="podsTableLeftTd">{{ctrl.data.scenarios[0]._id}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Name :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.scenarios[0].name}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Creator :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.scenarios[0].creator}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Created at :</td> + <td width="90%" class="podsTableLeftTd">{{ctrl.data.scenarios[0].creation_date}}</td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd">Installers</td> + <td width="90%" class="podsTableLeftTd"> + <div class="col-md-1" style="padding:0px"> + <a ng-click="ctrl.expandInstallers()"> + <p ng-if="ctrl.collapeInstallers">Hide</p> + <p ng-if="!ctrl.collapeInstallers">Show</p> + </a> + </div> + <div class="col-md-1" style="padding:0px" ng-class="{'hidden' : !ctrl.buttonInstaller || (!auth.isAuthenticated && authenticate)}"> + <button type="button" class="btn btn-success btn-xs" ng-click="ctrl.openAddInstaller()" ><i class="fa fa-plus"></i>Add</button> + </div> + <div ng-class="{ 'hidden' : ! ctrl.collapeInstallers } "> + <div class="table-responsive"> + <table class="table " ng-data="ctrl.data.scenarios[0].installers"> + <tbody ng-repeat="(indexI, installer) in ctrl.data.scenarios[0].installers"> + <tr style="padding:9px"> + <td class="podsTableTd"> + {{indexI+1}}. Installer: + </td> + <td class="podsTableLeftTd" style="width:10%;padding-top: 7px;"> + <a ng-click="ctrl.expandInstaller(indexI)">{{installer.installer}}</a> + </td> + <td style="width:80%;border: none; padding: 0px;" ng-class="{'hidden' : !ctrl.buttonInstaller}"> + <button type="button" ng-class="{'hidden' : !auth.isAuthenticated && authenticate}" class="btn btn-danger btn-xs" ng-click="ctrl.openDeleteInstallerModal(installer.installer)" ><i class="fa fa-minus"></i>Delete</button> + </td> + </tr> + <tr ng-class="{ 'hidden' : ! ctrl.collapeInstaller[indexI] }"> + <td class="podsTableTd"> + Versions: + </td> + <td width="90%" class="podsTableLeftTd"> + <div class="col-md-1" style="padding:0px"> + <a ng-click="ctrl.expandVersions(indexI)"> + <p ng-if="ctrl.collapeVersions[indexI]">Hide</p> + <p ng-if="!ctrl.collapeVersions[indexI]">Show</p> + </a> + </div> + <div class="col-md-1" style="padding:0px" ng-class="{'hidden' : !ctrl.buttonVersion}"> + <button type="button" class="btn btn-success btn-xs" ng-class="{'hidden' : !auth.isAuthenticated && authenticate}" ng-click="ctrl.openAddVersionModal(installer.installer)" ><i class="fa fa-plus"></i>Add</button> + </div> + <div ng-class="{ 'hidden' : ! ctrl.collapeVersions[indexI] } " class="col-md-12"> + <div class="table-responsive"> + <table class="table " ng-data="inctrl.data.scenarios[0].installers"> + <tbody ng-repeat="(indexV, version) in installer.versions"> + <tr style="padding:9px"> + <td class="podsTableTd"> + {{indexV+1}}. Version: + </td> + <td class="podsTableLeftTd" style="width:10%;padding-top: 7px;"> + <a ng-click="ctrl.expandVersion(indexI,indexV)">{{version.version}}</a> + </td> + <td style="width:80%;border: none; padding: 0px;" ng-class="{'hidden' : !ctrl.buttonVersion}"> + <button type="button" class="btn btn-danger btn-xs" ng-class="{'hidden' : !auth.isAuthenticated && authenticate}" ng-click="ctrl.openDeleteVersionModal(version.version, installer.installer)" ><i class="fa fa-minus"></i>Delete</button> + </td> + </tr> + <tr style="padding:9px" ng-class="{ 'hidden' : ! ctrl.collapeVersion[indexI][indexV] } "> + <td class="podsTableTd"> + Owner: + </td> + <td class="podsTableLeftTd" style="width:90%">{{version.owner}}</td> + </tr> + <tr style="padding:9px" ng-class="{ 'hidden' : ! ctrl.collapeVersion[indexI][indexV] }"> + <td class="podsTableTd"> + Projects: + </td> + <td width="90%" class="podsTableLeftTd"> + <div class="col-md-1" style="padding:0px"> + <a ng-click="ctrl.expandProjects(indexI,indexV)"> + <p style="width:50%" ng-if="ctrl.collapeProjects[indexI][indexV]">Hide</p> + <p style="width:50%" ng-if="!ctrl.collapeProjects[indexI][indexV]">Show</p> + </a> + </div> + <div class="col-md-1" style="padding:0px" ng-class="{'hidden' : !ctrl.buttonProject }"> + <button type="button" ng-class="{'hidden' : !auth.isAuthenticated && authenticate}" class="btn btn-success btn-xs" ng-click="ctrl.openAddProjectModal(version.version,installer.installer)" ><i class="fa fa-plus"></i>Add</button> + </div> + <div ng-class="{ 'hidden' : ! ctrl.collapeProjects[indexI][indexV] } " class="col-md-12"> + <div class="table-responsive"> + <table class="table " ng-data="version.projects"> + <tbody ng-repeat="(indexP, project) in version.projects" > + <tr style="padding:9px"> + <td class="podsTableTd"> + {{indexP+1}}. Project: + </td> + <td class="podsTableLeftTd" style="width:10%;padding-top: 7px;"> + <a ng-click="ctrl.expandProject(indexI, indexV, indexP)">{{project.project}}</a> + </td> + <td style="width:80%;border: none; padding: 0px;" ng-class="{'hidden' : !ctrl.buttonProject}"> + <button type="button" class="btn btn-danger btn-xs" ng-class="{'hidden' : !auth.isAuthenticated && authenticate}" ng-click="ctrl.openDeleteProjectModal(project,version.version, installer.installer)" ><i class="fa fa-minus"></i>Delete</button> + </td> + </tr> + <tr ng-class="{ 'hidden' : ! ctrl.collapeProject[indexI][indexV][indexP] }"> + <td class="podsTableTd"> + Trust Indicators: + </td> + <td class="podsTableLeftTd" style="width:90%"> + <a ng-click="ctrl.expandTrustIndicator(indexI, indexV, indexP)"> + <p ng-if="ctrl.collapeTrustIndicator[indexI][indexV][indexP]">Hide</p> + <p ng-if="!ctrl.collapeTrustIndicator[indexI][indexV][indexP]">Show</p> + </a> + <table class="table " ng-class="{ 'hidden' : ! ctrl.collapeTrustIndicator[indexI][indexV][indexP] } " ng-data="project.trust_indicators"> + <tbody ng-repeat="(indexTI, trust_indicator) in project.trust_indicators" > + <tr style="padding:9px"> + <td class="podsTableTd"> + Status: + </td> + <td width="90%" class="podsTableLeftTd"> + {{trust_indicator.status}} + </td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd"> + Date: + </td> + <td width="90%" class="podsTableLeftTd"> + {{trust_indicator.date}} + </td> + </tr> + </tbody> + </table> + </td> + </tr> + <tr ng-class="{ 'hidden' : ! ctrl.collapeProject[indexI][indexV][indexP] }"> + <td class="podsTableTd"> + Scores: + </td> + <td class="podsTableLeftTd" style="width:90%"> + <a ng-click="ctrl.expandScore(indexI, indexV, indexP)"> + <p ng-if="ctrl.collapeScore[indexI][indexV][indexP]">Hide</p> + <p ng-if="!ctrl.collapeScore[indexI][indexV][indexP]">Show</p> + </a> + <table class="table" ng-class="{ 'hidden' : ! ctrl.collapeScore[indexI][indexV][indexP] } " ng-data="project.scores"> + <tbody ng-repeat="(indexSC, score) in project.scores" > + <tr style="padding:9px"> + <td class="podsTableTd"> + Score: + </td> + <td width="90%" class="podsTableLeftTd"> + {{score.score}} + </td> + </tr> + <tr style="padding:9px"> + <td class="podsTableTd"> + Date: + </td> + <td width="90%" class="podsTableLeftTd"> + {{score.date}} + </td> + </tr> + </tbody> + </table> + </td> + </tr> + <tr ng-class="{ 'hidden' : ! ctrl.collapeProject[indexI][indexV][indexP] }"> + <td class="podsTableTd"> + Customs: + </td> + <td class="podsTableLeftTd" style="width:90%"> + <a ng-click="ctrl.expandCustom(indexI, indexV, indexP)"> + <p ng-if="ctrl.collapeCustom[indexI][indexV][indexP]">Hide</p> + <p ng-if="!ctrl.collapeCustom[indexI][indexV][indexP]">Show</p> + </a> + <table class="table" ng-class="{ 'hidden' : ! ctrl.collapeCustom[indexI][indexV][indexP] } " ng-data="project.customs"> + <tbody> + <tr ng-repeat-start="(indexCU, custom) in project.customs" style="padding:9px"> + <td class="podsTableTd" style="float: none!important;"> + {{custom}} + </td> + <td width="90%" class="podsTableLeftTd"> + <button type="button" class="btn btn-danger btn-xs" ng-click="ctrl.openDeleteCustomModal(custom,project.project,version.version,installer.installer)" ng-class="{'hidden' : !auth.isAuthenticated && authenticate}" ><i class="fa fa-minus"></i>Delete</button> + </td> + </tr> + <tr ng-repeat-end=> + </tr> + <tr> + <button type="button" class="btn btn-success btn-xs" ng-click="ctrl.openAddCustomModal(project.project,version.version,installer.installer)" ng-class="{ 'hidden' : !ctrl.collapeCustom[indexI][indexV][indexP] || (!auth.isAuthenticated && authenticate)}"><i class="fa fa-plus"></i>Add</button> + </tr> + </tbody> + </table> + </td> + </tr> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + </td> + </tr> + </tbody> + </table> + </div> +</div> +<div class="row" style="margin-bottom:24px;"></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="row" style="margin-bottom:24px;"></div>
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/components/scenarios/scenario/scenarioController.js b/testapi/opnfv_testapi/ui/components/scenarios/scenario/scenarioController.js new file mode 100644 index 0000000..e17718f --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/scenario/scenarioController.js @@ -0,0 +1,605 @@ +/* + * 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('ScenarioController', ScenarioController); + + ScenarioController.$inject = [ + '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert', + 'confirmModal', 'authenticate', '$timeout' + ]; + + /** + * TestAPI Scenario Controller + * This controller is for the '/Scenario/:name' page where a user can browse + * through Scenario declared in TestAPI. + */ + function ScenarioController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal, authenticate, $timeout) { + var ctrl = this; + ctrl.name = $state.params['name']; + ctrl.url = testapiApiUrl + '/scenarios?name=' + ctrl.name; + ctrl.expandInstallers = expandInstallers; + ctrl.expandInstaller = expandInstaller; + ctrl.expandInstaller = ctrl.expandInstaller; + ctrl.expandVersion = expandVersion; + ctrl.expandVersions = expandVersions; + ctrl.loadDetails = loadDetails; + ctrl.expandProjects = expandProjects + ctrl.expandProject = expandProject + ctrl.expandTrustIndicator = expandTrustIndicator; + ctrl.expandScore = expandScore; + ctrl.expandCustom = expandCustom; + ctrl.collapeVersion = []; + ctrl.collapeVersions = []; + ctrl.collapeProjects = []; + ctrl.collapeProject = []; + ctrl.collapeTrustIndicator = []; + ctrl.collapeScore = []; + ctrl.collapeCustom = []; + ctrl.collapeInstaller = []; + ctrl.addCustom = addCustom; + ctrl.openAddCustomModal = openAddCustomModal; + ctrl.openDeleteCustomModal = openDeleteCustomModal; + ctrl.deleteCustom = deleteCustom; + ctrl.addProject = addProject + ctrl.openAddProjectModal = openAddProjectModal + ctrl.openAddVersionModal = openAddVersionModal + ctrl.addVersion = addVersion + ctrl.openDeleteVersionModal = openDeleteVersionModal + ctrl.deleteVersion = deleteVersion + ctrl.openAddInstaller = openAddInstaller + ctrl.addInstaller = addInstaller + ctrl.openDeleteInstallerModal = openDeleteInstallerModal + ctrl.deleteInstaller = deleteInstaller + ctrl.openDeleteProjectModal = openDeleteProjectModal + ctrl.deleteProject = deleteProject + + ctrl.buttonInstaller = true + ctrl.buttonVersion = true + ctrl.buttonProject = true + 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); + } + /** + * This will contact the TestAPI to get a listing of declared projects. + */ + function loadDetails() { + ctrl.showError = false; + ctrl.projectsRequest = + $http.get(ctrl.url).success(function (data) { + ctrl.data = data; + }).catch(function (error) { + ctrl.data = null; + ctrl.error = error.statusText + ctrl.toastError() + }); + } + + function expandTrustIndicator(indexI, indexV, indexP){ + if(ctrl.collapeTrustIndicator[indexI]==undefined){ + ctrl.collapeTrustIndicator[indexI] = [] + if(ctrl.collapeTrustIndicator[indexI][indexV]==undefined){ + ctrl.collapeTrustIndicator[indexI][indexV] = [] + } + } + if(ctrl.collapeTrustIndicator[indexI][indexV][indexP]){ + ctrl.collapeTrustIndicator[indexI][indexV][indexP] = false; + }else{ + ctrl.collapeTrustIndicator[indexI][indexV][indexP] = true; + } + } + + function expandScore(indexI, indexV, indexP){ + if(ctrl.collapeScore[indexI]==undefined){ + ctrl.collapeScore[indexI] = [] + if(ctrl.collapeScore[indexI][indexV]==undefined){ + ctrl.collapeScore[indexI][indexV] = [] + } + } + if(ctrl.collapeScore[indexI][indexV][indexP]){ + ctrl.collapeScore[indexI][indexV][indexP] = false; + }else{ + ctrl.collapeScore[indexI][indexV][indexP] = true; + } + } + + function expandCustom(indexI, indexV, indexP){ + if(ctrl.collapeCustom[indexI]==undefined){ + ctrl.collapeCustom[indexI] = [] + if(ctrl.collapeCustom[indexI][indexV]==undefined){ + ctrl.collapeCustom[indexI][indexV] = [] + } + } + if(ctrl.collapeCustom[indexI][indexV][indexP]){ + ctrl.collapeCustom[indexI][indexV][indexP] = false; + ctrl.buttonProject = true + }else{ + ctrl.collapeCustom[indexI][indexV][indexP] = true; + ctrl.buttonProject = false + } + } + + function expandVersion(indexI, indexV){ + if(ctrl.collapeVersion[indexI]==undefined){ + ctrl.collapeVersion[indexI] = [] + } + if(ctrl.collapeVersion[indexI][indexV]){ + ctrl.collapeVersion[indexI][indexV] = false; + }else{ + ctrl.collapeVersion[indexI][indexV] = true; + } + } + + function expandVersions(index){ + if(ctrl.collapeVersions[index]){ + ctrl.collapeVersions[index] = false; + ctrl.buttonInstaller = true + }else{ + ctrl.collapeVersions[index] = true; + ctrl.buttonInstaller = false + } + } + + function expandProjects(indexI, indexV){ + if(ctrl.collapeProjects[indexI]==undefined){ + ctrl.collapeProjects[indexI] = [] + } + if(ctrl.collapeProjects[indexI][indexV]){ + ctrl.collapeProjects[indexI][indexV] = false; + ctrl.buttonVersion = true + } + else{ + ctrl.collapeProjects[indexI][indexV]= true; + ctrl.buttonVersion = false + } + } + + function expandProject(indexI, indexV, indexP){ + if(ctrl.collapeProject[indexI]==undefined){ + ctrl.collapeProject[indexI] = [] + if(ctrl.collapeProject[indexI][indexV]==undefined){ + ctrl.collapeProject[indexI][indexV] = [] + } + } + if(ctrl.collapeProject[indexI][indexV][indexP]){ + ctrl.collapeProject[indexI][indexV][indexP] = false; + } + else{ + ctrl.collapeProject[indexI][indexV][indexP]= true; + } + } + + function expandInstaller(index){ + if(ctrl.collapeInstaller[index]){ + ctrl.collapeInstaller[index] = false; + } + else{ + ctrl.collapeInstaller[index]= true; + } + } + + function expandInstallers(){ + if(ctrl.collapeInstallers){ + ctrl.collapeInstallers= false + }else{ + ctrl.collapeInstallers= true + } + } + + function deleteInstaller(data){ + ctrl.installerReqest = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/installers" + $http.delete(ctrl.installerReqest, {data: data.installers, headers: {'Content-Type': 'application/json'}}).success(function (data){ + ctrl.success = "Installer is successfully deleted." + ctrl.toastSuccess(); + ctrl.loadDetails(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError() + }); + } + + function openDeleteInstallerModal(installer){ + var installers = [] + installers.push(installer) + var data = { + "installers": installers + } + confirmModal("Delete", 'installers', ctrl.deleteInstaller, data); + } + + function addInstaller(installer){ + var installers = [] + installers.push(installer) + ctrl.installerRequestUrl = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/installers" + ctrl.installerRequest = $http.post(ctrl.installerRequestUrl, installers) + ctrl.installerRequest.success(function (data){ + ctrl.success = "Installers are successfully updated." + ctrl.loadDetails(); + ctrl.toastSuccess(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + return ctrl.installerRequest + } + + function openAddInstaller(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/installerModal.html', + controller: 'installerModalCtrl as installerModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Add Installer", + successHandler: ctrl.addInstaller + }; + } + } + }); + } + + function addVersion(versions, installer){ + ctrl.versionRequestUrl = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/versions?installer="+installer + ctrl.versionRequest = $http.post(ctrl.versionRequestUrl, versions) + ctrl.versionRequest.success(function (data){ + ctrl.success = "Versions are successfully updated." + ctrl.loadDetails(); + ctrl.toastSuccess() + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError() + }); + return ctrl.versionRequest; + } + + function openDeleteVersionModal(version, installer){ + var versions = [] + versions.push(version) + var data = { + "version": versions, + "installer": installer + } + confirmModal("Delete", "version", ctrl.deleteVersion, data); + } + + function deleteVersion(data){ + ctrl.versionReqest = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/versions?installer="+data.installer + $http.delete(ctrl.versionReqest, {data: data.version, headers: {'Content-Type': 'application/json'}}).success(function (data){ + ctrl.success = "Versions are successfully deleted." + ctrl.loadDetails(); + ctrl.toastSuccess(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + } + + function openAddVersionModal(installer){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/versionModal.html', + controller: 'versionAddModalCtrl as versionModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Add Version", + successHandler: ctrl.addVersion, + installer: installer + }; + } + } + }); + } + + function addProject(project, version, installer){ + ctrl.projectRequestUrl = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/projects?installer="+installer+"&version="+version + ctrl.projectRequest= $http.post(ctrl.projectRequestUrl, project) + ctrl.projectRequest.success(function (data){ + ctrl.success = "Projects are successfully updated." + ctrl.loadDetails(); + ctrl.toastSuccess(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + return ctrl.projectRequest; + } + + function openAddProjectModal(version, installer){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/projectModal.html', + controller: 'projectAddModalCtrl as projectModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Add Project", + successHandler: ctrl.addProject, + version: version, + installer: installer + }; + } + } + }); + } + + function addCustom(custom,project,version,installer){ + ctrl.customRequestUrl = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/customs?installer="+installer+"&version="+version+"&project="+ project + ctrl.customRequest = $http.post(ctrl.customRequestUrl, custom) + ctrl.customRequest.success(function (data){ + ctrl.success = "Customs are successfully updated." + ctrl.loadDetails(); + ctrl.toastSuccess(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + return ctrl.customRequest + } + + function openDeleteCustomModal(custom,project,version,installer){ + var customs = [] + customs.push(custom) + var data = { + "customs": customs, + "project": project, + "version": version, + "installer": installer + } + confirmModal("Delete", 'customs', ctrl.deleteCustom, data); + } + + function deleteCustom(data){ + ctrl.customReqest = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/customs?installer="+data.installer+"&version="+data.version+"&project="+ data.project + $http.delete(ctrl.customReqest, {data: data.customs, headers: {'Content-Type': 'application/json'}}).success(function (data){ + ctrl.success = "Customs are successfully deleted." + ctrl.loadDetails(); + ctrl.toastSuccess(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + } + + function openAddCustomModal(project,version,installer){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/customModal.html', + controller: 'customAddModalCtrl as customModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Add Custom", + successHandler: ctrl.addCustom, + project: project, + version: version, + installer: installer + }; + } + } + }); + } + + function openDeleteProjectModal(project, version, installer){ + var projects = [] + projects.push(project.project) + var data = { + "projects": projects, + "version": version, + "installer": installer + } + confirmModal("Delete", 'projects', ctrl.deleteProject, data); + } + + function deleteProject(data){ + ctrl.projectReqest = testapiApiUrl+ "/scenarios/"+ ctrl.name + "/projects?installer="+data.installer+"&version="+data.version + $http.delete(ctrl.projectReqest, {data: data.projects, headers: {'Content-Type': 'application/json'}}).success(function (data){ + ctrl.success = "Projects are successfully Deleted."; + ctrl.toastSuccess(); + ctrl.loadDetails(); + }) + .catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + } + + ctrl.loadDetails(); + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('customAddModalCtrl', customAddModalCtrl); + customAddModalCtrl.$inject = ['$scope', '$uibModalInstance', 'data']; + function customAddModalCtrl($scope, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + + ctrl.customs = []; + + function confirm() { + var custom = ctrl.custom; + if(custom!="" && custom!=undefined ){ + ctrl.customs = custom.split(/[ ,]+/).filter(Boolean); + } + console.log(ctrl.customs) + ctrl.data.successHandler( + ctrl.customs, ctrl.data.project, + ctrl.data.version,ctrl.data.installer).success(function(){ + $uibModalInstance.dismiss('cancel'); + }); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } + + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('projectAddModalCtrl', projectAddModalCtrl); + projectAddModalCtrl.$inject = ['$scope', '$uibModal', '$uibModalInstance', 'data']; + function projectAddModalCtrl($scope, $uibModal, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.openCustomModal = openCustomModal; + ctrl.handleModalCustom = handleModalCustom; + ctrl.projects = [] + ctrl.project = { + "scores": [], + "trust_indicators": [], + "customs": [] + } + + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + ctrl.projects.push(ctrl.project) + ctrl.data.successHandler( + ctrl.projects, ctrl.data.version, ctrl.data.installer).success( function(){ + $uibModalInstance.dismiss('cancel'); + }); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + + function handleModalCustom(custom){ + ctrl.project.customs.push(custom); + } + + function openCustomModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/customModal.html', + controller: 'customModalCtrl as customModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Custom", + successHandler: ctrl.handleModalCustom + }; + } + } + }); + } + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('versionAddModalCtrl', versionAddModalCtrl); + versionAddModalCtrl.$inject = ['$scope', '$uibModal','$uibModalInstance', 'data']; + function versionAddModalCtrl($scope, $uibModal, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.openProjectModal = openProjectModal; + ctrl.handleModalData = handleModalData; + ctrl.versions = [] + ctrl.version = { + "projects": [] + } + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + ctrl.versions.push(ctrl.version) + ctrl.data.successHandler(ctrl.versions, ctrl.data.installer).success(function(){ + $uibModalInstance.dismiss('cancel'); + }); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + + function handleModalData(project){ + ctrl.version.projects.push(project) + } + function openProjectModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/projectModal.html', + controller: 'projectModalCtrl as projectModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Project", + successHandler: ctrl.handleModalData, + }; + } + } + }); + } + } + +})(); diff --git a/testapi/opnfv_testapi/ui/components/scenarios/scenarios.html b/testapi/opnfv_testapi/ui/components/scenarios/scenarios.html new file mode 100644 index 0000000..8d23449 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/scenarios.html @@ -0,0 +1,63 @@ +<h3>Scenarios</h3> +<div class="row" style="margin-bottom:24px;"></div> +<div class="row podsTable" style="vertical-align:middle"> + <div class="col-sm-1 pull-right" ng-class="{ 'hidden': !auth.isAuthenticated }" > + <button type="button" class="btn btn-danger" ng-click="ctrl.openBatchDeleteModal()"> + <i class="fa fa-minus"></i> Delete</button> + </div> + <div class="col-sm-2 pull-right" ng-class="{ 'hidden': !auth.isAuthenticated}"> + <button type="button" class="btn btn-success" ng-click="ctrl.openScenarioModal()"> + <i class="fa fa-plus"></i>Create</button> + </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.scenarios"> + <thead> + <tr style=" + text-align: center;"> + <th style="width: 1%;">Bulk Select</th> + <th style="width: 80%;">Name + <a class="text-danger" ng-click="ctrl.sortBy()" ng-class="{ 'hidden': ctrl.sortName }" > + <span class="glyphicon glyphicon-sort-by-alphabet pull-right" aria-hidden="true"></span> + </a> + <a class="text-danger" ng-click="ctrl.sortBy()" ng-class="{ 'hidden': !ctrl.sortName}" > + <span class="glyphicon glyphicon-sort-by-alphabet-alt pull-right" aria-hidden="true"></span> + </a> + </th> + <th style="width: 19%;" ng-class="{'hidden': !auth.isAuthenticated}">Operations</th> + </tr> + </thead> + <tbody> + <tr ng-repeat-start="(index, scenario) in ctrl.data.scenarios" style="padding:9px"> + <td> + <div class="text-center"> + <input type="checkbox" value="{{scenario.name}}" ng-model="ctrl.checkBox[index]" > + </div> + </td> + <td> + <a class="text-info" ng-click="ctrl.viewScenario(scenario.name)"> + {{scenario.name}} + </a> + </td> + <td ng-class="{'hidden': !auth.isAuthenticated}"> + <span class="podsTable-col"> + <a class="text-warning" ng-click="ctrl.openUpdateModal(scenario.name)" title="Edit"> + <i class="fa fa-pencil-square-o"></i></a> + <a class="text-danger" ng-click="ctrl.openDeleteModal(scenario.name)" title="Delete"> + <i class="fa fa-trash-o"></i></a> + </span> + </td> + <tr ng-repeat-end=> + </tr> + </tbody> + </table> + </div> +</div> + diff --git a/testapi/opnfv_testapi/ui/components/scenarios/scenariosController.js b/testapi/opnfv_testapi/ui/components/scenarios/scenariosController.js new file mode 100644 index 0000000..0aa5bf0 --- /dev/null +++ b/testapi/opnfv_testapi/ui/components/scenarios/scenariosController.js @@ -0,0 +1,513 @@ +/* + * 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('ScenariosController', ScenariosController); + + ScenariosController.$inject = [ + '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl', + 'raiseAlert', 'confirmModal', 'sortService', '$timeout' + ]; + + /** + * TestAPI Project Controller + * This controller is for the '/projects' page where a user can browse + * through projects declared in TestAPI. + */ + function ScenariosController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl, + raiseAlert, confirmModal, sortService, $timeout) { + var ctrl = this; + ctrl.url = testapiApiUrl + '/scenarios'; + + ctrl.createScenario = createScenario; + ctrl.listScenarios = listScenarios; + ctrl.openScenarioModal = openScenarioModal; + ctrl.viewScenario = viewScenario; + ctrl.openUpdateModal = openUpdateModal; + ctrl.updateScenarioName = updateScenarioName; + ctrl.openDeleteModal = openDeleteModal; + ctrl.deleteScenario = deleteScenario; + ctrl.openBatchDeleteModal = openBatchDeleteModal; + ctrl.deleteBatchScenario = deleteBatchScenario + ctrl.sortBy = sortBy + ctrl.checkBox = []; + ctrl.sortName = false + 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 openUpdateModal(name){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/scenarioNameUpdate.html', + controller: 'ScenarioNameUpdateCtrl as ScenarioNameUpdateCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Name Change: Scenario", + successHandler: ctrl.updateScenarioName, + name: name + }; + } + } + }); + } + + function openDeleteModal(name){ + confirmModal("Delete", 'scenarios', ctrl.deleteScenario,name); + } + + function sortBy(){ + ctrl.data.scenarios = sortService.sortFunction(ctrl.data.scenarios, 'name' , ctrl.sortName) + ctrl.sortName=!ctrl.sortName + } + + function deleteScenario(name){ + var scenarioURL = ctrl.url+"/"+name; + ctrl.scenarioRequest = + $http.delete(scenarioURL).success(function (data){ + ctrl.success = "Scenario is successfully deleted."; + ctrl.listScenarios(); + ctrl.toastSuccess(); + }).catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError() + }); + } + + function openBatchDeleteModal(){ + var deleteObjects = [] + ctrl.checkBox.forEach(function(scenario, index){ + if(!ctrl.showError){ + if(scenario){ + deleteObjects.push(ctrl.data.scenarios[index].name); + } + } + }); + confirmModal("Delete", 'scenarios', ctrl.deleteBatchScenario, deleteObjects); + } + + function deleteBatchScenario(){ + var index; + var checkedBox = []; + ctrl.checkBox.forEach(function(scenario, index){ + if(!ctrl.showError){ + if(scenario){ + deleteScenario(ctrl.data.scenarios[index].name); + } + } + }); + ctrl.checkBox = [] + } + + function updateScenarioName(newName, name){ + var scenarioURL = ctrl.url+"/"+name + var body = { + "name": newName + } + if(newName){ + ctrl.scenarioRequest = $http.put(scenarioURL, body) + ctrl.scenarioRequest.success(function (data){ + ctrl.success = "Scenario is successfully Updated." + ctrl.listScenarios(); + ctrl.toastSuccess(); + }).catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + return ctrl.scenarioRequest + }else{ + ctrl.error = "Name is missing"; + ctrl.toastError(); + } + } + + function viewScenario(name){ + $state.go('scenario', {'name':name}, {reload: true}); + } + + function createScenario(scenario) { + ctrl.scenarioRequest = $http.post(ctrl.url, scenario) + + ctrl.scenarioRequest.success(function (data){ + ctrl.success = "Scenario is successfully created."; + ctrl.toastSuccess(); + }).catch(function (data) { + ctrl.error = data.statusText; + ctrl.toastError(); + }); + + return ctrl.scenarioRequest; + } + + function listScenarios() { + ctrl.showError = false; + ctrl.resultsRequest = + $http.get(ctrl.url).success(function (data) { + ctrl.data = data; + ctrl.sortBy() + }).catch(function (data) { + ctrl.data = null; + ctrl.error = data.statusText; + ctrl.toastError(); + }); + } + + function openScenarioModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/scenarioModal.html', + controller: 'scenarioModalController as scenarioModalController', + size: 'md', + resolve: { + data: function () { + return { + text: "Scenario", + successHandler: ctrl.createScenario, + }; + } + } + }); + } + + listScenarios(); + } + + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('scenarioModalController', scenarioModalController); + scenarioModalController.$inject = ['$scope', '$uibModal', '$uibModalInstance', 'data', '$q']; + function scenarioModalController($scope, $uibModal, $uibModalInstance, data, $q) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.handleModalData = handleModalData; + ctrl.openInstallerModal = openInstallerModal; + ctrl.scenario = { + "installers": [], + } + + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + ctrl.data.successHandler(ctrl.scenario).success(function(){ + $uibModalInstance.dismiss('cancel'); + }); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + + function handleModalData(installer){ + ctrl.scenario.installers.push(installer) + var deferred = $q.defer(); + deferred.resolve(); + return deferred.promise; + } + + function openInstallerModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/installerModal.html', + controller: 'installerModalCtrl as installerModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Installer", + successHandler: ctrl.handleModalData, + }; + } + } + }); + } + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('installerModalCtrl', installerModalCtrl); + installerModalCtrl.$inject = ['$scope', '$uibModal', '$uibModalInstance', 'data']; + function installerModalCtrl($scope, $uibModal, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.openVersionModal = openVersionModal; + ctrl.handleModalData = handleModalData; + ctrl.installer = { + "versions":[] + } + + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + ctrl.data.successHandler(ctrl.installer).success(function(){ + $uibModalInstance.dismiss('cancel'); + }); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + + function handleModalData(version){ + ctrl.installer.versions.push(version); + } + + function openVersionModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/versionModal.html', + controller: 'versionModalCtrl as versionModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Version", + successHandler: ctrl.handleModalData, + }; + } + } + }); + } + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('versionModalCtrl', versionModalCtrl); + versionModalCtrl.$inject = ['$scope', '$uibModal','$uibModalInstance', 'data']; + function versionModalCtrl($scope, $uibModal, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.openProjectModal = openProjectModal; + ctrl.handleModalData = handleModalData; + ctrl.version = { + "projects": [] + } + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + ctrl.data.successHandler(ctrl.version); + $uibModalInstance.dismiss('cancel'); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + + function handleModalData(project){ + ctrl.version.projects.push(project) + } + function openProjectModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/projectModal.html', + controller: 'projectModalCtrl as projectModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Project", + successHandler: ctrl.handleModalData, + }; + } + } + }); + } + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('projectModalCtrl', projectModalCtrl); + projectModalCtrl.$inject = ['$scope', '$uibModal', '$uibModalInstance', 'data']; + function projectModalCtrl($scope, $uibModal, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.openCustomModal = openCustomModal; + ctrl.handleModalCustom = handleModalCustom; + ctrl.project = { + "scores": [], + "trust_indicators": [], + "customs": [] + } + + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + + ctrl.data.successHandler(ctrl.project); + $uibModalInstance.dismiss('cancel'); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + + function handleModalCustom(customs){ + for (var custom in customs){ + ctrl.project.customs.push(customs[custom]); + } + } + + function openCustomModal(){ + $uibModal.open({ + templateUrl: 'testapi-ui/components/scenarios/modals/customModal.html', + controller: 'customModalCtrl as customModalCtrl', + size: 'md', + resolve: { + data: function () { + return { + text: "Custom", + successHandler: ctrl.handleModalCustom + }; + } + } + }); + } + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('customModalCtrl', customModalCtrl); + customModalCtrl.$inject = ['$scope', '$uibModalInstance', 'data']; + function customModalCtrl($scope, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.open = open; + ctrl.customs = [] + + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + var custom = ctrl.custom; + if(custom!="" && custom!=undefined ){ + ctrl.customs = custom.split(/[ ,]+/).filter(Boolean); + } + ctrl.data.successHandler(ctrl.customs); + $uibModalInstance.dismiss('cancel'); + + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } + + /** + * TestAPI Project Modal Controller + * This controller is for the create modal where a user can create + * the project information and for the edit modal where user can + * edit the project's details + */ + angular.module('testapiApp').controller('ScenarioNameUpdateCtrl', ScenarioNameUpdateCtrl); + ScenarioNameUpdateCtrl.$inject = ['$scope', '$uibModalInstance', 'data']; + function ScenarioNameUpdateCtrl($scope, $uibModalInstance, data) { + var ctrl = this; + ctrl.confirm = confirm; + ctrl.cancel = cancel; + ctrl.data = angular.copy(data); + ctrl.open = open; + ctrl.name = ctrl.data.name; + + + /** + * Initiate confirmation and call the success handler with the + * inputs. + */ + function confirm() { + ctrl.data.successHandler(ctrl.name,ctrl.data.name).success( function() { + $uibModalInstance.dismiss('cancel'); + }) + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } +})(); diff --git a/testapi/opnfv_testapi/ui/config.json b/testapi/opnfv_testapi/ui/config.json index 26dfd08..63e1e97 100644 --- a/testapi/opnfv_testapi/ui/config.json +++ b/testapi/opnfv_testapi/ui/config.json @@ -1,2 +1,4 @@ -{"testapiApiUrl": "http://localhost:8000/api/v1", -"authenticate": true} +{ + "testapiApiUrl": "http://localhost:8000/api/v1", + "authenticate": true +}
\ No newline at end of file diff --git a/testapi/opnfv_testapi/ui/index.html b/testapi/opnfv_testapi/ui/index.html index 5e69e87..f345197 100644 --- a/testapi/opnfv_testapi/ui/index.html +++ b/testapi/opnfv_testapi/ui/index.html @@ -28,6 +28,7 @@ <link rel="stylesheet" href="testapi-ui/assets/lib/angular-busy/dist/angular-busy.min.css"> <link rel="stylesheet" href="testapi-ui/assets/css/style.css"> <link rel="stylesheet" href="testapi-ui/assets/lib/font-awesome-4.7.0/css/font-awesome.min.css"> + <link rel="stylesheet" href="testapi-ui/assets/lib/angular-json-tree/angular-json-tree.css" /> <script src="testapi-ui/assets/lib/jquery/jquery-3.2.1.min.js"></script> <script src="testapi-ui/assets/lib/bootstrap/dist/js/bootstrap.min.js"></script> @@ -36,6 +37,7 @@ <script src="testapi-ui/assets/lib/angular-resource/angular-resource.min.js"></script> <script src="testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js"></script> <script src="testapi-ui/assets/lib/angular-busy/dist/angular-busy.min.js"></script> + <script src="testapi-ui/assets/lib/angular-json-tree/angular-json-tree.min.js"></script> <script src="testapi-ui/assets/lib/angular-confirm-modal/angular-confirm.js"></script> <script src="testapi-ui/app.js"></script> @@ -44,11 +46,15 @@ <script src="testapi-ui/shared/alerts/alertModalFactory.js"></script> <script src="testapi-ui/shared/alerts/confirmModalFactory.js"></script> <script src="testapi-ui/components/pods/podsController.js"></script> + <script src="testapi-ui/components/scenarios/scenario/scenarioController.js"></script> + <script src="testapi-ui/components/scenarios/scenariosController.js"></script> <script src="testapi-ui/components/pods/pod/podController.js"></script> <script src="testapi-ui/components/projects/projectsController.js"></script> <script src="testapi-ui/components/projects/project/projectController.js"></script> <script src="testapi-ui/components/results/resultsController.js"></script> <script src="testapi-ui/components/results/result/resultController.js"></script> + <script src="testapi-ui/components/deploy-results/deployResultsController.js"></script> + <script src="testapi-ui/components/deploy-results/deploy-result/deployResultController.js"></script> <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> 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/opnfv_testapi/tests/UI/protractor-conf.js b/testapi/opnfv_testapi/ui/protractor-conf.js index affbe5d..affbe5d 100644 --- a/testapi/opnfv_testapi/tests/UI/protractor-conf.js +++ b/testapi/opnfv_testapi/ui/protractor-conf.js diff --git a/testapi/opnfv_testapi/ui/shared/alerts/confirmModal.html b/testapi/opnfv_testapi/ui/shared/alerts/confirmModal.html index e5397e0..417af37 100644 --- a/testapi/opnfv_testapi/ui/shared/alerts/confirmModal.html +++ b/testapi/opnfv_testapi/ui/shared/alerts/confirmModal.html @@ -10,7 +10,7 @@ </div> <div class="Delete" ng-class="{ 'hidden': confirmModal.data.text!='Delete' }"> <div class="form-group"> - <label for="confirmText"> You are about to delete.</label> + <label for="confirmText"> You are about to delete following {{confirmModal.data.resource}} : {{confirmModal.deleteObjects}} </label> <br> Do you want to proceed? </div> diff --git a/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js b/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js index 5e79775..0286341 100644 --- a/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js +++ b/testapi/opnfv_testapi/ui/shared/alerts/confirmModalFactory.js @@ -11,17 +11,19 @@ * Opens confirm modal dialog with input textbox */ function confirmModal($uibModal) { - return function(text, successHandler, name) { + return function(text, resource, successHandler, name) { $uibModal.open({ - templateUrl: '/testapi-ui/shared/alerts/confirmModal.html', + templateUrl: 'testapi-ui/shared/alerts/confirmModal.html', controller: 'CustomConfirmModalController as confirmModal', size: 'md', resolve: { data: function () { return { text: text, + resource: resource, successHandler: successHandler, name: name + }; } } @@ -44,8 +46,37 @@ ctrl.confirm = confirm; ctrl.cancel = cancel; - + ctrl.buildDeleteObjects = buildDeleteObjects; ctrl.data = angular.copy(data); + + function buildDeleteObjects(){ + ctrl.deleteObjects = ''; + if (typeof ctrl.data.name === 'string') { + ctrl.deleteObjects = ctrl.data.name + } + else if (ctrl.data.name instanceof Array){ + for(var index in ctrl.data.name){ + if(index==0){ + ctrl.deleteObjects += ctrl.data.name[index] + } + else{ + ctrl.deleteObjects += ", "+ ctrl.data.name[index] + } + + } + } + else{ + for(var index in ctrl.data.name[ctrl.data.resource]){ + if(index==0){ + ctrl.deleteObjects += ctrl.data.name[ctrl.data.resource][index] + } + else{ + ctrl.deleteObjects += ", "+ ctrl.data.name[ctrl.data.resource][index] + } + + } + } + } /** * Initiate confirmation and call the success handler with the * input text. @@ -63,5 +94,7 @@ function cancel() { $uibModalInstance.dismiss('cancel'); } + + ctrl.buildDeleteObjects(); } })(); diff --git a/testapi/opnfv_testapi/ui/shared/header/header.html b/testapi/opnfv_testapi/ui/shared/header/header.html index d0501a2..2b7be2f 100644 --- a/testapi/opnfv_testapi/ui/shared/header/header.html +++ b/testapi/opnfv_testapi/ui/shared/header/header.html @@ -19,9 +19,11 @@ TestAPI <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('/projects')}"><a ui-sref="projects">Projects</a></li> - <li ng-class="{ active: header.isActive('/results')}"><a ui-sref="results">Results</a></li> + <li ng-class="{ active: header.isActive('/results')}"><a ui-sref="results">Results</a></li> + <li ng-class="{ active: header.isActive('/deployresults')}"><a ui-sref="deployresults">Deploy Results</a></li> + <li ng-class="{ active: header.isActive('/scenarios')}"><a ui-sref="scenarios">Scenarios</a></li> </ul> - <ul class="nav navbar-nav navbar-right"> + <ul class="nav navbar-nav navbar-right" ng-class="{'hidden' : !authenticate}"> <li ng-class="{ active: header.isActive('/profile')}" ng-if="auth.isAuthenticated"><a ui-sref="profile">Profile</a></li> <li ng-if="auth.isAuthenticated"><a href="" ng-click="auth.doSignOut()">Sign Out</a></li> <li ng-if="!auth.isAuthenticated"><a href="" ng-click="auth.doSignIn()">Sign In / Sign Up</a></li> diff --git a/testapi/requirements.txt b/testapi/requirements.txt index f752a64..9dba746 100644 --- a/testapi/requirements.txt +++ b/testapi/requirements.txt @@ -2,11 +2,13 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=2.0.0,!=2.1.0 # Apache-2.0 -setuptools>=16.0,!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2 # PSF/ZPL -tornado>=3.1,<=4.3 # Apache-2.0 -epydoc>=0.3.1 -six>=1.9.0 # MIT -motor # Apache-2.0 +pbr!=2.1.0 # Apache-2.0 +setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0;python_version>='3.5' # PSF/ZPL +setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,<45.0.0;python_version<='2.7' # PSF/ZPL +tornado<=4.3,>=3.1 # Apache-2.0 +epydoc +six # MIT +motor # Apache-2.0 python-cas -requests[security]
\ No newline at end of file +requests[security]!=2.20.0 # Apache-2.0 +futures!=0.17.0;python_version=='2.7' or python_version=='2.6' # PSF diff --git a/testapi/setup.py b/testapi/setup.py index f9d95a3..566d844 100644 --- a/testapi/setup.py +++ b/testapi/setup.py @@ -1,13 +1,29 @@ -import setuptools +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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. -__author__ = 'serena' +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass - setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) diff --git a/testapi/test-requirements.txt b/testapi/test-requirements.txt index 233f465..67fd8f0 100644 --- a/testapi/test-requirements.txt +++ b/testapi/test-requirements.txt @@ -2,9 +2,9 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -coverage>=4.0,!=4.4 # Apache-2.0 -mock>=2.0 # BSD -nose # LGPL -pytest # MIT -pytest-cov # MIT -pytest-mock # MIT +coverage!=4.4 # Apache-2.0 +mock!=4.0.0,!=4.0.1 # BSD +nose # LGPL +pytest # MIT +pytest-cov +pytest-mock diff --git a/testapi/testapi-client/.stestr.conf b/testapi/testapi-client/.stestr.conf new file mode 100644 index 0000000..cdadb67 --- /dev/null +++ b/testapi/testapi-client/.stestr.conf @@ -0,0 +1,2 @@ +[DEFAULT] +test_path=${TEST_PATH:-./testapiclient/tests/unit} diff --git a/testapi/testapi-client/__init__.py b/testapi/testapi-client/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testapi/testapi-client/__init__.py diff --git a/testapi/testapi-client/etc/client.creds b/testapi/testapi-client/etc/client.creds new file mode 100644 index 0000000..ee2a0ab --- /dev/null +++ b/testapi/testapi-client/etc/client.creds @@ -0,0 +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 +export testapi_token=changeme
\ No newline at end of file diff --git a/testapi/testapi-client/requirements.txt b/testapi/testapi-client/requirements.txt new file mode 100644 index 0000000..0c6ef77 --- /dev/null +++ b/testapi/testapi-client/requirements.txt @@ -0,0 +1,8 @@ +setuptools>=16.0,!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2 # PSF/ZPL + +pbr>=2.0.0,!=2.1.0 # Apache-2.0 +cmd2>=0.6.7 # MIT +PrettyTable<0.8,>=0.7.1 # BSD +pyparsing>=2.1.0 # MIT +six>=1.10.0 # MIT +cliff
\ No newline at end of file diff --git a/testapi/testapi-client/setup.cfg b/testapi/testapi-client/setup.cfg new file mode 100644 index 0000000..ee1ba54 --- /dev/null +++ b/testapi/testapi-client/setup.cfg @@ -0,0 +1,74 @@ +[metadata] +name = testapi-client + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[files] +packages = + testapiclient + +[entry_points] +console_scripts = + testapi = testapiclient.main:main + +testapi = + auth = testapiclient.cli.auth:Auth + pod create = testapiclient.cli.pods:PodCreate + pod get = testapiclient.cli.pods:PodGet + pod delete = testapiclient.cli.pods:PodDelete + pod getone = testapiclient.cli.pods:PodGetOne + + project create = testapiclient.cli.projects:ProjectCreate + project get = testapiclient.cli.projects:ProjectGet + project getone = testapiclient.cli.projects:ProjectGetOne + project delete = testapiclient.cli.projects:ProjectDelete + project put = testapiclient.cli.projects:ProjectPut + + testcase create = testapiclient.cli.testcases:TestcaseCreate + testcase get = testapiclient.cli.testcases:TestcaseGet + testcase getone = testapiclient.cli.testcases:TestcaseGetOne + testcase delete = testapiclient.cli.testcases:TestcaseDelete + testcase put = testapiclient.cli.testcases:TestcasePut + + scenario create = testapiclient.cli.scenarios:ScenarioCreate + scenario get = testapiclient.cli.scenarios:ScenarioGet + scenario getone = testapiclient.cli.scenarios:ScenarioGetOne + scenario delete = testapiclient.cli.scenarios:ScenarioDelete + scenario put = testapiclient.cli.scenarios:ScenarioPut + + scenario installer create = testapiclient.cli.scenarios:InstallerCreate + scenario installer delete = testapiclient.cli.scenarios:InstallerDelete + scenario installer put = testapiclient.cli.scenarios:InstallerPut + + scenario version create = testapiclient.cli.scenarios:VersionCreate + scenario version delete = testapiclient.cli.scenarios:VersionDelete + scenario version put = testapiclient.cli.scenarios:VersionPut + + scenario version owner put = testapiclient.cli.scenarios:VersionOwnerPut + + scenario project create = testapiclient.cli.scenarios:ProjectCreate + scenario project delete = testapiclient.cli.scenarios:ProjectDelete + scenario project put = testapiclient.cli.scenarios:ProjectPut + + scenario custom create = testapiclient.cli.scenarios:CustomCreate + scenario custom delete = testapiclient.cli.scenarios:CustomDelete + scenario custom put = testapiclient.cli.scenarios:CustomPut + + scenario trustindicator create = testapiclient.cli.scenarios:TrustIndicatorCreate + + scenario score create = testapiclient.cli.scenarios:ScoreCreate + + deployresult create = testapiclient.cli.deployresults:DeployresultCreate + 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 +tag_svn_revision = 0 diff --git a/testapi/testapi-client/setup.py b/testapi/testapi-client/setup.py new file mode 100644 index 0000000..96b6dbf --- /dev/null +++ b/testapi/testapi-client/setup.py @@ -0,0 +1,11 @@ + +import setuptools + +try: + import multiprocessing # noqa +except ImportError: + pass + +setuptools.setup( + setup_requires=['pbr>=2.0.0'], + pbr=True) diff --git a/testapi/testapi-client/test-requirements.txt b/testapi/testapi-client/test-requirements.txt new file mode 100644 index 0000000..69ec0b1 --- /dev/null +++ b/testapi/testapi-client/test-requirements.txt @@ -0,0 +1,5 @@ +stestr>=1.0.0 # Apache-2.0 +testtools>=2.2.0 # MIT +mock +requests +six>=1.10.0 # MIT
\ No newline at end of file diff --git a/testapi/testapi-client/testapiclient/__init__.py b/testapi/testapi-client/testapiclient/__init__.py new file mode 100644 index 0000000..363bc38 --- /dev/null +++ b/testapi/testapi-client/testapiclient/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# 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 +############################################################################## diff --git a/testapi/testapi-client/testapiclient/cli/__init__.py b/testapi/testapi-client/testapiclient/cli/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/__init__.py diff --git a/testapi/testapi-client/testapiclient/cli/deployresults.py b/testapi/testapi-client/testapiclient/cli/deployresults.py new file mode 100644 index 0000000..1ca0bef --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/deployresults.py @@ -0,0 +1,98 @@ +import json + +from testapiclient.utils import command +from testapiclient.utils import urlparse +from testapiclient.models import deployresult + + +def deployresults_url(): + return urlparse.resource_join('deployresults') + + +def deployresult_url(parsed_args): + return urlparse.path_join(deployresults_url(), parsed_args.deployresult_id) + + +class DeployresultGet(command.Lister): + + def get_parser(self, prog_name): + parser = super(DeployresultGet, self).get_parser(prog_name) + parser.add_argument('-build-id', + help='Search deployresults using build tag') + parser.add_argument('-from', + help='Search deployresults using from date') + parser.add_argument('-scenario', + help='Search deployresults using scenario') + parser.add_argument('-period', + help='Search deployresults using period') + parser.add_argument('-page', + help='Search deployresults using page') + parser.add_argument('-to', + help='Search deployresults using to') + parser.add_argument('---version', + help='Search deployresults using version') + parser.add_argument('-last', + help='Search deployresults using last date') + parser.add_argument('-pod-name', + help='Search deployresults using pod') + parser.add_argument('-criteria', + help='Search deployresults using version') + parser.add_argument('-installer', + help='Search deployresults using installer') + parser.add_argument('-job-name', + help='Search deployresults using project') + + return parser + + def take_action(self, parsed_args): + columns = ( + '_id', + 'pod_name', + 'version', + 'criteria', + 'start_date', + 'stop_date', + 'scenario', + 'installer', + + ) + data = self.app.client_manager.get( + urlparse.query_by(deployresults_url(), + ['build_id', 'from', 'last', + 'scenario', 'period', 'job_name', + 'to', 'version', + 'criteria', 'installer', 'pod_name', 'page'], + parsed_args)) + return self.format_output(columns, data.get('deployresults', [])) + + +class DeployresultGetOne(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(DeployresultGetOne, self).get_parser(prog_name) + parser.add_argument('deployresult_id', + help='Search deployresult by deployresult id') + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.get(deployresult_url(parsed_args))) + + +class DeployresultCreate(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(DeployresultCreate, self).get_parser(prog_name) + parser.add_argument('deployresult', + type=json.loads, + help='Deployresult create request format:\n' + '\'{}\''.format(json.dumps( + deployresult.DeployResultCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.post( + deployresults_url(), parsed_args.deployresult)) diff --git a/testapi/testapi-client/testapiclient/cli/pods.py b/testapi/testapi-client/testapiclient/cli/pods.py new file mode 100644 index 0000000..a7706f6 --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/pods.py @@ -0,0 +1,89 @@ +import json + +from testapiclient.client import pods +from testapiclient.utils import command +from testapiclient.utils import urlparse +from testapiclient.models import pods as pm + + +def pods_url(): + return urlparse.resource_join('pods') + + +def pod_url(parsed_args): + return urlparse.path_join(pods_url(), parsed_args.name) + + +class PodGet(command.Lister): + "Handle get request for pods" + + def get_parser(self, prog_name): + parser = super(PodGet, self).get_parser(prog_name) + parser.add_argument('-name', + default='', + help='Search pods using name') + return parser + + def take_action(self, parsed_args): + columns = ( + "name", + "_id", + "creator", + "role", + "mode", + "creation_date", + ) + + data = self.app.client_manager.get( + urlparse.query_by(pods_url(), 'name', parsed_args)) + return self.format_output(columns, data.get('pods', [])) + + +class PodGetOne(command.ShowOne): + "Handle get request for pod by name" + + def get_parser(self, prog_name): + parser = super(PodGetOne, self).get_parser(prog_name) + parser.add_argument('name', + default='', + help='Find pod using name') + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.get(pod_url(parsed_args))) + + +class PodCreate(command.ShowOne): + "Handle post request for pods" + + def get_parser(self, prog_name): + parser = super(PodCreate, self).get_parser(prog_name) + parser.add_argument('pod', + type=json.loads, + help='Pod create request format :\n' + '\'{}\''.format(json.dumps( + pm.PodCreateRequest().__dict__ + )) + + '\n role should be either ' + '"community-ci" or "production-ci", and ' + 'mode should be either "metal" or "virtual.') + return parser + + def take_action(self, parsed_args): + client = pods.PodsClient(client_manager=self.app.client_manager) + return self.format_output(client.create(parsed_args.pod)) + + +class PodDelete(command.Command): + "Handle delete request for pods" + + def get_parser(self, prog_name): + parser = super(PodDelete, self).get_parser(prog_name) + parser.add_argument('name', + type=str, + help='Delete pods using name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete(pod_url(parsed_args)) diff --git a/testapi/testapi-client/testapiclient/cli/projects.py b/testapi/testapi-client/testapiclient/cli/projects.py new file mode 100644 index 0000000..2fa5b5b --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/projects.py @@ -0,0 +1,97 @@ +import json + +from testapiclient.utils import command +from testapiclient.utils import urlparse +from testapiclient.models import project + + +def projects_url(): + return urlparse.resource_join('projects') + + +def project_url(parsed_args): + return urlparse.path_join(projects_url(), parsed_args.name) + + +class ProjectGet(command.Lister): + + def get_parser(self, prog_name): + parser = super(ProjectGet, self).get_parser(prog_name) + parser.add_argument('-name', + help='Search projects by name') + return parser + + def take_action(self, parsed_args): + columns = ( + 'name', + '_id', + 'creator', + 'creation_date' + ) + data = self.app.client_manager.get( + urlparse.query_by(projects_url(), 'name', parsed_args)) + return self.format_output(columns, data.get('projects', [])) + + +class ProjectGetOne(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(ProjectGetOne, self).get_parser(prog_name) + parser.add_argument('name', + help='Search project by name') + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.get(project_url(parsed_args))) + + +class ProjectCreate(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(ProjectCreate, self).get_parser(prog_name) + parser.add_argument('project', + type=json.loads, + help='Project create request format :\n' + '\'{}\''.format(json.dumps( + project.ProjectCreateRequest().__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.post(projects_url(), parsed_args.project)) + + +class ProjectDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(ProjectDelete, self).get_parser(prog_name) + parser.add_argument('name', + type=str, + help='Delete project by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete(project_url(parsed_args)) + + +class ProjectPut(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(ProjectPut, self).get_parser(prog_name) + parser.add_argument('name', + type=str, + help='Update project by name') + parser.add_argument('project', + type=json.loads, + help='Project Update request format :\n' + '\'{}\''.format(json.dumps( + project.ProjectCreateRequest().__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.put( + project_url(parsed_args), parsed_args.project)) diff --git a/testapi/testapi-client/testapiclient/cli/results.py b/testapi/testapi-client/testapiclient/cli/results.py new file mode 100644 index 0000000..5500501 --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/results.py @@ -0,0 +1,97 @@ +import json + +from testapiclient.utils import command +from testapiclient.utils import urlparse +from testapiclient.models import result + + +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' + '\'{}\''.format(json.dumps( + result.ResultCreateRequest().__dict__))) + 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/cli/scenarios.py b/testapi/testapi-client/testapiclient/cli/scenarios.py new file mode 100644 index 0000000..197ee0c --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/scenarios.py @@ -0,0 +1,603 @@ +import json + +from testapiclient.utils import command +from testapiclient.utils import urlparse +from testapiclient.models import scenario + + +def scenarios_url(): + return urlparse.resource_join('scenarios') + + +def scenario_url(parsed_args): + return urlparse.path_join(scenarios_url(), parsed_args.name) + + +def resources_url(name, resuorce): + return urlparse.resource_join('scenarios', name, resuorce) + + +class ScenarioGet(command.Lister): + + def get_parser(self, prog_name): + parser = super(ScenarioGet, self).get_parser(prog_name) + parser.add_argument('-name', + help='Search scenarios using name') + parser.add_argument('-installer', + help='Search scenarios using installer') + parser.add_argument('---version', + help='Search scenarios using version') + parser.add_argument('-project', + help='Search scenarios using project') + return parser + + def take_action(self, parsed_args): + columns = ( + 'name', + '_id', + 'creator', + 'creation_date' + ) + data = self.app.client_manager.get( + urlparse.query_by(scenarios_url(), + ['name', 'installer', 'version', 'project'], + parsed_args)) + return self.format_output(columns, data.get('scenarios', [])) + + +class ScenarioGetOne(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(ScenarioGetOne, self).get_parser(prog_name) + parser.add_argument('name', + help='Search scenario by name') + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.get(scenario_url(parsed_args))) + + +class ScenarioCreate(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(ScenarioCreate, self).get_parser(prog_name) + parser.add_argument('scenario', + type=json.loads, + help='Scenario create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioCreateRequest( + ).__dict__ + )) + + '\n Intaller create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioInstallerCreateRequest( + ).__dict__ + )) + + '\n Version create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioVersionCreateRequest( + ).__dict__ + )) + + '\n Project create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioProjectCreateRequest( + ).__dict__ + )) + + '\n Custom create request format :\n' + '\'["asf","saf"]\',\n' + '\n Score create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioScoreCreateRequest( + ).__dict__ + )) + + '\nTrustIndicator create request format:\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioTICreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.post( + scenarios_url(), parsed_args.scenario)) + + +class ScenarioDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(ScenarioDelete, self).get_parser(prog_name) + parser.add_argument('name', + type=str, + help='Delete scenario by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete(scenario_url(parsed_args)) + + +class ScenarioPut(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(ScenarioPut, self).get_parser(prog_name) + parser.add_argument('name', + type=str, + help='Update scenario by name') + parser.add_argument('scenario', + type=json.loads, + help='Scenario create request format :\n' + + '\'{}\''.format(json.dumps( + scenario.ScenarioCreateRequest( + ).__dict__ + )) + + '\n Intaller create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioInstallerCreateRequest( + ).__dict__ + )) + + '\n Version create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioVersionCreateRequest( + ).__dict__ + )) + + '\n Project create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioProjectCreateRequest( + ).__dict__ + )) + + '\n Custom create request format :\n' + '\'["asf","saf"]\',\n' + '\n Score create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioScoreCreateRequest( + ).__dict__ + )) + + '\nTrustIndicator create request format:\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioTICreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.put( + scenario_url(parsed_args), parsed_args.scenario)) + + +class InstallerCreate(command.Command): + + def get_parser(self, prog_name): + parser = super(InstallerCreate, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + required=True, + help='Create installer under scenario name') + parser.add_argument('installer', + type=json.loads, + help='Intaller create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioInstallerCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.post( + resources_url( + parsed_args.scenario_name, + 'installers'), parsed_args.installer) + + +class InstallerDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(InstallerDelete, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + required=True, + type=str, + help='Delete installer by scenario name') + parser.add_argument('name', + nargs='+', + help='Delete installer by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete( + resources_url( + parsed_args.scenario_name, + 'installers'), parsed_args.name) + + +class InstallerPut(command.Command): + + def get_parser(self, prog_name): + parser = super(InstallerPut, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Update installer by scenario name') + parser.add_argument('installer', + type=json.loads, + help='Intaller create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioInstallerCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.put( + resources_url( + parsed_args.scenario_name, + 'installers'), parsed_args.installer) + + +class VersionCreate(command.Command): + + def get_parser(self, prog_name): + parser = super(VersionCreate, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + required=True, + help='Create version under scenario name') + parser.add_argument('--installer', + required=True, + help='Create version under scenario name') + parser.add_argument('version', + type=json.loads, + help='version create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioVersionCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.post( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'versions'), + 'installer', + parsed_args), parsed_args.version) + + +class VersionDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(VersionDelete, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + required=True, + type=str, + help='Delete version by scenario name') + parser.add_argument('--installer', + required=True, + help='Create version under scenario name') + parser.add_argument('name', + nargs='+', + help='Delete version by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'versions'), + 'installer', + parsed_args), parsed_args.name) + + +class VersionPut(command.Command): + + def get_parser(self, prog_name): + parser = super(VersionPut, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Update installer by scenario name') + parser.add_argument('--installer', + required=True, + help='Update version under installer name') + parser.add_argument('version', + type=json.loads, + help='version update request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioVersionCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.put( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'versions'), + 'installer', + parsed_args), parsed_args.version) + + +class VersionOwnerPut(command.Command): + + def get_parser(self, prog_name): + parser = super(VersionOwnerPut, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Update version by scenario name') + parser.add_argument('--installer', + required=True, + help='Update version under scenario name') + parser.add_argument('--version', + required=True, + help='Update version under scenario name') + parser.add_argument('owner', + type=json.loads, + help='Intaller create request format :\n' + '\'{"owner": ""}\',\n') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.put( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'owner'), + ['installer', 'version'], + parsed_args), parsed_args.owner) + + +class ProjectCreate(command.Command): + + def get_parser(self, prog_name): + parser = super(ProjectCreate, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Create project by scenario name') + parser.add_argument('--installer', + required=True, + help='Create project under installer name') + parser.add_argument('--version', + required=True, + help='Create project under version name') + parser.add_argument('project', + type=json.loads, + help='Project create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioProjectCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.post( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'projects'), + ['installer', 'version'], + parsed_args), parsed_args.project) + + +class ProjectDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(ProjectDelete, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + required=True, + type=str, + help='Delete projects by scenario name') + parser.add_argument('--installer', + required=True, + help='Delete projects under installer name') + parser.add_argument('--version', + required=True, + help='Delete projects under version name') + parser.add_argument('name', + nargs='+', + help='Delete projects by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'projects'), + ['installer', 'version'], + parsed_args), parsed_args.name) + + +class ProjectPut(command.Command): + + def get_parser(self, prog_name): + parser = super(ProjectPut, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Update project by scenario name') + parser.add_argument('--installer', + required=True, + help='Update project under installer name') + parser.add_argument('--version', + required=True, + help='Update project under version name') + parser.add_argument('project', + type=json.loads, + help='Project update request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioProjectCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.put( + urlparse.query_by( + resources_url(parsed_args.scenario_name, 'projects'), + ['installer', 'version'], + parsed_args), parsed_args.project) + + +class CustomCreate(command.Command): + + def get_parser(self, prog_name): + parser = super(CustomCreate, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Create custom by scenario name') + parser.add_argument('--installer', + required=True, + help='Create custom under installer name') + parser.add_argument('--version', + required=True, + help='Create custom under version name') + parser.add_argument('--project', + required=True, + help='Create custom under project name') + parser.add_argument('custom', + nargs='+', + help='Space sperated strings') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.post( + urlparse.query_by( + resources_url( + parsed_args.scenario_name, + 'customs'), + ['installer', 'version', 'project'], + parsed_args), + parsed_args.custom) + + +class CustomDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(CustomDelete, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + required=True, + type=str, + help='Delete custom by scenario name') + parser.add_argument('--installer', + required=True, + help='Create custom under scenario name') + parser.add_argument('--version', + required=True, + help='Create custom under scenario name') + parser.add_argument('--project', + required=True, + help='Create custom under scenario name') + parser.add_argument('name', + nargs='+', + help='Delete custom by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete( + urlparse.query_by( + resources_url( + parsed_args.scenario_name, + 'customs'), + ['installer', 'version', 'project'], + parsed_args), + parsed_args.name) + + +class CustomPut(command.Command): + + def get_parser(self, prog_name): + parser = super(CustomPut, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Update custom by scenario name') + parser.add_argument('--installer', + required=True, + help='Update custom under installer name') + parser.add_argument('--version', + required=True, + help='Update custom under version name') + parser.add_argument('--project', + required=True, + help='Update custom under project name') + parser.add_argument('custom', + nargs='+', + help='space sperated strings') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.put( + urlparse.query_by( + resources_url( + parsed_args.scenario_name, + 'customs'), + ['installer', 'version', 'project'], + parsed_args), + parsed_args.custom) + + +class TrustIndicatorCreate(command.Command): + + def get_parser(self, prog_name): + parser = super(TrustIndicatorCreate, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Create trust indicator by scenario name') + parser.add_argument('--installer', + required=True, + help='Create trustindicator under installer name') + parser.add_argument('--version', + required=True, + help='Create trust indicator under version name') + parser.add_argument('--project', + required=True, + help='Create trust indicator under project name') + parser.add_argument('trust_indicator', + type=json.loads, + help='trust indicator create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioTICreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + print parsed_args + return self.app.client_manager.post( + urlparse.query_by( + resources_url( + parsed_args.scenario_name, + 'trust_indicators'), + ['installer', 'version', 'project'], + parsed_args), + parsed_args.trust_indicator) + + +class ScoreCreate(command.Command): + + def get_parser(self, prog_name): + parser = super(ScoreCreate, self).get_parser(prog_name) + parser.add_argument('--scenario-name', + type=str, + required=True, + help='Create score by scenario name') + parser.add_argument('--installer', + required=True, + help='Create score under installer name') + parser.add_argument('--version', + required=True, + help='Create score under version name') + parser.add_argument('--project', + required=True, + help='Create score under project name') + parser.add_argument('score', + type=json.loads, + help='score create request format :\n' + '\'{}\''.format(json.dumps( + scenario.ScenarioScoreCreateRequest( + ).__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.post( + urlparse.query_by( + resources_url( + parsed_args.scenario_name, + 'scores'), + ['installer', 'version', 'project'], + parsed_args), + parsed_args.score) diff --git a/testapi/testapi-client/testapiclient/cli/testcases.py b/testapi/testapi-client/testapiclient/cli/testcases.py new file mode 100644 index 0000000..3052c18 --- /dev/null +++ b/testapi/testapi-client/testapiclient/cli/testcases.py @@ -0,0 +1,114 @@ +import json + +from testapiclient.utils import command +from testapiclient.utils import urlparse +from testapiclient.models import testcase + + +def testcases_url(name): + return urlparse.resource_join('projects', name, 'cases') + + +def testcase_url(parsed_args): + return urlparse.path_join( + testcases_url(parsed_args.project_name), parsed_args.name) + + +class TestcaseGet(command.Lister): + + def get_parser(self, prog_name): + parser = super(TestcaseGet, self).get_parser(prog_name) + parser.add_argument('--project-name', + required=True, + help='Search testcases by project name') + return parser + + def take_action(self, parsed_args): + columns = ( + 'name', + '_id', + 'creator', + 'creation_date' + ) + data = self.app.client_manager.get( + testcases_url(parsed_args.project_name)) + return self.format_output(columns, data.get('testcases', [])) + + +class TestcaseGetOne(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(TestcaseGetOne, self).get_parser(prog_name) + parser.add_argument('--project-name', + required=True, + help='Search testcase by project name') + parser.add_argument('name', + help='Search testcase by name') + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.get(testcase_url(parsed_args))) + + +class TestcaseCreate(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(TestcaseCreate, self).get_parser(prog_name) + parser.add_argument('--project-name', + required=True, + help='Create testcase under project name') + parser.add_argument('testcase', + type=json.loads, + help='Testcase create request format:\n' + '\'{}\''.format(json.dumps( + testcase.TestCaseCreateRequest().__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.post( + testcases_url(parsed_args.project_name), parsed_args.testcase)) + + +class TestcaseDelete(command.Command): + + def get_parser(self, prog_name): + parser = super(TestcaseDelete, self).get_parser(prog_name) + parser.add_argument('--project-name', + required=True, + type=str, + help='Delete testcase by project name') + parser.add_argument('name', + type=str, + help='Delete testcase by name') + return parser + + def take_action(self, parsed_args): + return self.app.client_manager.delete(testcase_url(parsed_args)) + + +class TestcasePut(command.ShowOne): + + def get_parser(self, prog_name): + parser = super(TestcasePut, self).get_parser(prog_name) + parser.add_argument('--project-name', + type=str, + required=True, + help='Update testcase by project name') + parser.add_argument('name', + type=str, + help='Update testcase by name') + parser.add_argument('testcase', + type=json.loads, + help='Testcase Update request format:\n' + '\'{}\''.format(json.dumps( + testcase.TestCaseCreateRequest().__dict__ + ))) + return parser + + def take_action(self, parsed_args): + return self.format_output( + self.app.client_manager.put( + testcase_url(parsed_args), parsed_args.testcase)) 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/deploy_results.py b/testapi/testapi-client/testapiclient/client/deploy_results.py new file mode 100644 index 0000000..b0724b0 --- /dev/null +++ b/testapi/testapi-client/testapiclient/client/deploy_results.py @@ -0,0 +1,28 @@ +import json + +from testapiclient.client import base +from testapiclient.utils import urlparse + + +class DeployResultsClient(base.Client): + resource = 'deployresults' + + def __init__(self, **kwargs): + super(DeployResultsClient, self).__init__(**kwargs) + + def create(self, testcase_req): + return self.clientmanager.post(self.url, testcase_req) + + def get(self, **queries): + if queries: + return json.dumps( + self.clientmanager.get( + urlparse.query_join(self.url, **queries))['deployresults']) + else: + return json.dumps( + self.clientmanager.get(self.url)['deployresults']) + + def get_one(self, id): + return json.dumps( + self.clientmanager.get( + urlparse.path_join(self.url, id))) diff --git a/testapi/testapi-client/testapiclient/client/pods.py b/testapi/testapi-client/testapiclient/client/pods.py new file mode 100644 index 0000000..d08114f --- /dev/null +++ b/testapi/testapi-client/testapiclient/client/pods.py @@ -0,0 +1,31 @@ +import json + +from testapiclient.client import base +from testapiclient.utils import urlparse + + +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) + + def get(self, **queries): + if queries: + return json.dumps( + self.clientmanager.get( + urlparse.query_join(self.url, **queries))['pods']) + else: + return json.dumps( + self.clientmanager.get(self.url)['pods']) + + def get_one(self, name): + return json.dumps(self.clientmanager.get( + urlparse.path_join(self.url, name))) + + def delete(self, name): + return self.clientmanager.delete( + urlparse.path_join(self.url, name)) diff --git a/testapi/testapi-client/testapiclient/client/projects.py b/testapi/testapi-client/testapiclient/client/projects.py new file mode 100644 index 0000000..63d00fe --- /dev/null +++ b/testapi/testapi-client/testapiclient/client/projects.py @@ -0,0 +1,35 @@ +import json + +from testapiclient.client import base +from testapiclient.utils import urlparse + + +class ProjectsClient(base.Client): + resource = 'projects' + + def __init__(self, **kwargs): + super(ProjectsClient, self).__init__(**kwargs) + + def create(self, project_req): + return self.clientmanager.post(self.url, project_req) + + def get(self, **queries): + if queries: + return json.dumps( + self.clientmanager.get( + urlparse.query_join(self.url, **queries))['projects']) + else: + return json.dumps( + self.clientmanager.get(self.url)['projects']) + + def get_one(self, name): + return json.dumps(self.clientmanager.get( + urlparse.path_join(self.url, name))) + + def delete(self, name): + return self.clientmanager.delete( + urlparse.path_join(self.url, name)) + + def update(self, name, project_req): + return self.clientmanager.put( + urlparse.path_join(self.url, name), project_req) diff --git a/testapi/testapi-client/testapiclient/client/results.py b/testapi/testapi-client/testapiclient/client/results.py new file mode 100644 index 0000000..7d9ad0e --- /dev/null +++ b/testapi/testapi-client/testapiclient/client/results.py @@ -0,0 +1,28 @@ +import json + +from testapiclient.client import base +from testapiclient.utils import urlparse + + +class ResultsClient(base.Client): + resource = 'results' + + def __init__(self, **kwargs): + super(ResultsClient, self).__init__(**kwargs) + + def create(self, testcase_req): + return self.clientmanager.post(self.url, testcase_req) + + def get(self, **queries): + if queries: + return json.dumps( + self.clientmanager.get( + urlparse.query_join(self.url, **queries))['results']) + else: + return json.dumps( + self.clientmanager.get(self.url)['results']) + + def get_one(self, id): + return json.dumps( + self.clientmanager.get( + urlparse.path_join(self.url, id))) diff --git a/testapi/testapi-client/testapiclient/client/scenarios.py b/testapi/testapi-client/testapiclient/client/scenarios.py new file mode 100644 index 0000000..e5ce2f1 --- /dev/null +++ b/testapi/testapi-client/testapiclient/client/scenarios.py @@ -0,0 +1,177 @@ +import json + +from testapiclient.client import base +from testapiclient.utils import urlparse + + +class ScenariosClient(base.Client): + resource = 'scenarios' + + def __init__(self, **kwargs): + super(ScenariosClient, self).__init__(**kwargs) + + def create(self, scenario_req): + return self.clientmanager.post(self.url, scenario_req) + + def get(self, **queries): + if queries: + return json.dumps( + self.clientmanager.get( + urlparse.query_join(self.url, **queries))['scenarios']) + else: + return json.dumps( + self.clientmanager.get(self.url)['scenarios']) + + def get_one(self, scenario_name): + return json.dumps( + self.clientmanager.get( + urlparse.path_join( + self.url, scenario_name))) + + def delete(self, scenario_name): + return self.clientmanager.delete( + urlparse.path_join( + self.url, scenario_name)) + + def update(self, scenario_name, scenario_req): + return self.clientmanager.put( + urlparse.path_join( + self.url, scenario_name), scenario_req) + + +class InstallersClient(base.Client): + resource = 'scenarios/{}/installers' + + def __init__(self, **kwargs): + super(InstallersClient, self).__init__(**kwargs) + + def delete(self, scenario_name, name): + return self.clientmanager.delete( + self.url.format(scenario_name), [name]) + + def update(self, scenario_name, installer_req): + return self.clientmanager.put( + self.url.format(scenario_name), installer_req) + + def create(self, scenario_name, installer_req): + return self.clientmanager.post( + self.url.format(scenario_name), installer_req) + + +class VersionsClient(base.Client): + resource = 'scenarios/{}/versions' + + def __init__(self, **kwargs): + super(VersionsClient, self).__init__(**kwargs) + + def delete(self, scenario_name, installer, name): + queries = {'installer': installer} + return self.clientmanager.delete( + urlparse.query_join( + self.url.format(scenario_name), **queries), name) + + def update(self, scenario_name, installer, version_req): + queries = {'installer': installer} + return self.clientmanager.put( + urlparse.query_join( + self.url.format(scenario_name), **queries), version_req) + + def create(self, scenario_name, installer, version_req): + queries = {'installer': installer} + return self.clientmanager.post( + urlparse.query_join( + self.url.format(scenario_name), **queries), version_req) + + +class VersionsOwnerClient(base.Client): + resource = 'scenarios/{}/owner' + + def __init__(self, **kwargs): + super(VersionsOwnerClient, self).__init__(**kwargs) + + def update(self, scenario_name, installer, version, owner): + queries = {'installer': installer, 'version': version} + return self.clientmanager.put( + urlparse.query_join( + self.url.format(scenario_name), **queries), owner) + + +class ProjectsClient(base.Client): + resource = 'scenarios/{}/projects' + + def __init__(self, **kwargs): + super(ProjectsClient, self).__init__(**kwargs) + + def delete(self, scenario_name, installer, version, name): + queries = {'installer': installer, 'version': version} + return self.clientmanager.delete( + urlparse.query_join( + self.url.format(scenario_name), **queries), name) + + def update(self, scenario_name, installer, version, project_req): + queries = {'installer': installer, 'version': version} + return self.clientmanager.put( + urlparse.query_join( + self.url.format(scenario_name), **queries), project_req) + + def create(self, scenario_name, installer, version, project_req): + queries = {'installer': installer, 'version': version} + return self.clientmanager.post( + urlparse.query_join( + self.url.format(scenario_name), **queries), project_req) + + +class TrustIndicatorsClient(base.Client): + resource = 'scenarios/{}/trust_indicators' + + def __init__(self, **kwargs): + super(TrustIndicatorsClient, self).__init__(**kwargs) + + def create(self, scenario_name, installer, version, project, trust_in_req): + queries = { + 'installer': installer, 'version': version, 'project': project} + return self.clientmanager.post( + urlparse.query_join( + self.url.format(scenario_name), **queries), trust_in_req) + + +class ScoresClient(base.Client): + resource = 'scenarios/{}/scores' + + def __init__(self, **kwargs): + super(ScoresClient, self).__init__(**kwargs) + + def create(self, scenario_name, installer, version, project, scores_req): + queries = { + 'installer': installer, 'version': version, 'project': project} + return self.clientmanager.post( + urlparse.query_join( + self.url.format(scenario_name), **queries), scores_req) + + +class CustomsClient(base.Client): + resource = 'scenarios/{}/customs' + + def __init__(self, **kwargs): + super(CustomsClient, self).__init__(**kwargs) + + def delete(self, scenario_name, installer, version, project, customs): + queries = { + 'installer': installer, 'version': version, 'project': project} + return self.clientmanager.delete( + urlparse.query_join( + self.url.format(scenario_name), **queries), customs) + + def update(self, scenario_name, installer, version, project, customs): + queries = { + 'installer': installer, 'version': version, 'project': project} + return self.clientmanager.put( + urlparse.query_join( + self.url.format(scenario_name), **queries), customs) + + def create(self, scenario_name, installer, version, project, customs): + queries = { + 'installer': installer, 'version': version, 'project': project} + return self.clientmanager.post( + urlparse.query_join( + self.url.format(scenario_name), **queries), customs) diff --git a/testapi/testapi-client/testapiclient/client/testcases.py b/testapi/testapi-client/testapiclient/client/testcases.py new file mode 100644 index 0000000..bb2b6d3 --- /dev/null +++ b/testapi/testapi-client/testapiclient/client/testcases.py @@ -0,0 +1,36 @@ +import json + +from testapiclient.client import base +from testapiclient.utils import urlparse + + +class TestcasesClient(base.Client): + resource = 'projects/{}/cases' + + def __init__(self, **kwargs): + super(TestcasesClient, self).__init__(**kwargs) + + def create(self, project_name, testcase_req): + return self.clientmanager.post( + self.url.format(project_name), testcase_req) + + def get(self, project_name): + return json.dumps( + self.clientmanager.get( + self.url.format(project_name))['testcases']) + + def get_one(self, project_name, name): + return json.dumps( + self.clientmanager.get( + urlparse.path_join( + self.url.format(project_name), name))) + + def delete(self, project_name, name): + return self.clientmanager.delete( + urlparse.path_join( + self.url.format(project_name), name)) + + def update(self, project_name, name, testcase_req): + return self.clientmanager.put( + urlparse.path_join( + self.url.format(project_name), name), testcase_req) diff --git a/testapi/testapi-client/testapiclient/main.py b/testapi/testapi-client/testapiclient/main.py new file mode 100644 index 0000000..a448146 --- /dev/null +++ b/testapi/testapi-client/testapiclient/main.py @@ -0,0 +1,54 @@ +import sys + +from cliff import app +from cliff import commandmanager + +from testapiclient.utils import clientmanager + + +class TestAPIClient(app.App): + + def __init__(self): + super(TestAPIClient, self).__init__( + description='TestAPI Client', + version='0.1', + command_manager=commandmanager.CommandManager('testapi'), + deferred_help=True, + ) + + def build_option_parser(self, description, version, argparse_kwargs=None): + self.LOG.debug('build_option_parser') + parser = super(TestAPIClient, self).build_option_parser( + description, + version, + argparse_kwargs) + parser.add_argument('-u', + type=str, + help='Username for authentication') + parser.add_argument('-p', + type=str, + help='Password for authentication') + return parser + + def initialize_app(self, argv): + self.LOG.debug('initialize_app') + self.client_manager = clientmanager.ClientManager(self.options) + + def prepare_to_run_command(self, cmd): + self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__) + if self.client_manager.auth_required: + self.client_manager.auth() + + def clean_up(self, cmd, result, err): + self.LOG.debug('clean_up %s', cmd.__class__.__name__) + if err: + self.LOG.debug('got an error: %s', err) + + +def main(argv=sys.argv[1:]): + client = TestAPIClient() + return client.run(argv) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) 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/deployresult.py b/testapi/testapi-client/testapiclient/models/deployresult.py new file mode 100644 index 0000000..5c13966 --- /dev/null +++ b/testapi/testapi-client/testapiclient/models/deployresult.py @@ -0,0 +1,17 @@ +class DeployResultCreateRequest(): + def __init__( + self, build_id='', scenario='', stop_date='', start_date='', + upstream_job_name='', version='', pod_name='', criteria='', + installer='', upstream_build_id='', job_name='', details=''): + self.build_id = build_id + self.scenario = scenario + self.stop_date = stop_date + self.start_date = start_date + self.upstream_job_name = upstream_job_name + self.version = version + self.pod_name = pod_name + self.criteria = criteria + self.installer = installer + self.upstream_build_id = upstream_build_id + self.job_name = job_name + self.details = details diff --git a/testapi/testapi-client/testapiclient/models/pods.py b/testapi/testapi-client/testapiclient/models/pods.py new file mode 100644 index 0000000..4fa42e7 --- /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/models/project.py b/testapi/testapi-client/testapiclient/models/project.py new file mode 100644 index 0000000..fc85588 --- /dev/null +++ b/testapi/testapi-client/testapiclient/models/project.py @@ -0,0 +1,4 @@ +class ProjectCreateRequest(): + def __init__(self, name='', description=''): + self.description = description + self.name = name diff --git a/testapi/testapi-client/testapiclient/models/result.py b/testapi/testapi-client/testapiclient/models/result.py new file mode 100644 index 0000000..766c03a --- /dev/null +++ b/testapi/testapi-client/testapiclient/models/result.py @@ -0,0 +1,16 @@ +class ResultCreateRequest(): + def __init__( + self, project_name='', scenario='', case_name='', pod_name='', + installer='', version='', stop_date='', build_tag='', criteria='', + start_date='', details=''): + self.project_name = project_name + self.scenario = scenario + self.case_name = case_name + self.pod_name = pod_name + self.installer = installer + self.version = version + self.stop_date = stop_date + self.build_tag = build_tag + self.criteria = criteria + self.start_date = start_date + self.details = details diff --git a/testapi/testapi-client/testapiclient/models/scenario.py b/testapi/testapi-client/testapiclient/models/scenario.py new file mode 100644 index 0000000..f4f0f40 --- /dev/null +++ b/testapi/testapi-client/testapiclient/models/scenario.py @@ -0,0 +1,37 @@ +class ScenarioCreateRequest: + def __init__(self, name='', installers=[]): + self.name = name + self.installers = installers + + +class ScenarioInstallerCreateRequest: + def __init__(self, installer='', versions=[]): + self.installer = installer + self.versions = versions + + +class ScenarioVersionCreateRequest: + def __init__(self, version='', owner='', projects=[]): + self.version = version + self.owner = owner + self.projects = projects + + +class ScenarioProjectCreateRequest: + def __init__(self, project='', scores=[], trust_indicators=[], customs=[]): + self.project = project + self.scores = scores + self.trust_indicators = trust_indicators + self.customs = customs + + +class ScenarioScoreCreateRequest: + def __init__(self, score='', date=''): + self.score = score + self.date = date + + +class ScenarioTICreateRequest: + def __init__(self, status='', date=''): + self.status = status + self.date = date diff --git a/testapi/testapi-client/testapiclient/models/testcase.py b/testapi/testapi-client/testapiclient/models/testcase.py new file mode 100644 index 0000000..70d5d78 --- /dev/null +++ b/testapi/testapi-client/testapiclient/models/testcase.py @@ -0,0 +1,20 @@ +class TestCaseCreateRequest(): + def __init__( + self, run='', name='', ci_loop='', tags='', url='', + blocking='', domains='', dependencies='', version='', + criteria='', tier='', trust='', catalog_description='', + description=''): + self.run = run + self.name = name + self.ci_loop = ci_loop + self.tags = tags + self.url = url + self.blocking = blocking + self.domains = domains + self.dependencies = dependencies + self.version = version + self.criteria = criteria + self.tier = tier + self.trust = trust + self.catalog_description = catalog_description + self.description = description diff --git a/testapi/testapi-client/testapiclient/tests/__init__.py b/testapi/testapi-client/testapiclient/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/__init__.py diff --git a/testapi/testapi-client/testapiclient/tests/unit/__init__.py b/testapi/testapi-client/testapiclient/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/__init__.py diff --git a/testapi/testapi-client/testapiclient/tests/unit/fakes.py b/testapi/testapi-client/testapiclient/tests/unit/fakes.py new file mode 100644 index 0000000..8424745 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/fakes.py @@ -0,0 +1,72 @@ +import json +import sys + +import requests +import six +import httplib + + +class FakeResponse(requests.Response): + + def __init__(self, headers=None, status_code=httplib.OK, + data=None, encoding=None): + super(FakeResponse, self).__init__() + + headers = headers or {} + + self.status_code = status_code + + self.headers.update(headers) + if status_code != httplib.OK: + self.reason = data + + self._content = json.dumps(data) + if not isinstance(self._content, six.binary_type): + self._content = self._content.encode() + + +class FakeApp(object): + + def __init__(self, _stdout, _log): + self.stdout = _stdout + self.client_manager = None + self.stdin = sys.stdin + self.stdout = _stdout or sys.stdout + self.stderr = sys.stderr + self.log = _log + + +class FakeLog(object): + + def __init__(self): + self.messages = {} + + def debug(self, msg): + self.messages['debug'] = msg + + def info(self, msg): + self.messages['info'] = msg + + def warning(self, msg): + self.messages['warning'] = msg + + def error(self, msg): + self.messages['error'] = msg + + def critical(self, msg): + self.messages['critical'] = msg + + +class FakeStdout(object): + + def __init__(self): + self.content = [] + + def write(self, text): + self.content.append(text) + + def make_string(self): + result = '' + for line in self.content: + result = result + line + return result diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_app.py b/testapi/testapi-client/testapiclient/tests/unit/test_app.py new file mode 100644 index 0000000..c20b27f --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_app.py @@ -0,0 +1,50 @@ +import urllib + +from mock import mock + +from testapiclient import main +from testapiclient.tests.unit import utils +from testapiclient.tests.unit import fakes + + +class MainTest(utils.TestCommand): + def setUp(self): + super(MainTest, self).setUp() + self.app = main.TestAPIClient() + self.cas_sever = '{}{}{}'.format( + self.env_variables['testapi_cas_auth_url'], + urllib.quote(self.env_variables['testapi_url']), + self.env_variables['testapi_cas_signin_return']) + self.headers = { + 'Content-type': 'application/json', + 'Accept': 'text/plain'} + + def test_auth_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data={'text': "success"}) + self.app.run( + ['-u', 'user', '-p', 'pass', 'pod', 'create', + '{"name": "asfad"}']) + self.post_mock.assert_called_with( + 'http://localhost:8000/api/v1/pods', + data='{"name": "asfad"}', + headers=self.headers) + + def test_auth_failure(self): + self.post_mock.return_value = fakes.FakeResponse( + data={'text': "login"}) + self.app.run( + ['-u', 'user', '-p', 'pass', 'pod', 'create', + '{"name": "asfad"}']) + self.post_mock.assert_called_once_with( + self.cas_sever, + {'pass': 'pass', 'name': 'user', 'form_id': 'user_login'} + ) + + def test_auth_not_called(self): + self.auth_post = mock.patch( + 'testapiclient.utils.clientmanager.ClientManager.auth').start() + self.app.run(['pod', 'get']) + self.auth_post.assert_not_called() + self.get_mock.assert_called_once_with( + 'http://localhost:8000/api/v1/pods', headers=self.headers) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_deployresults.py b/testapi/testapi-client/testapiclient/tests/unit/test_deployresults.py new file mode 100644 index 0000000..0e0385b --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_deployresults.py @@ -0,0 +1,109 @@ +import json + +from mock import mock +from six.moves.urllib import parse +import testtools + +from testapiclient.cli import deployresults +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class DeployresultTest(utils.TestCommand): + def setUp(self): + super(DeployresultTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'deployresults') + self.deployresult_json = { + 'job_name': 'daisy-deploy', + 'scenario': 'test-scenario', + 'stop_date': '2018-04-09 13:44:53', + 'upstream_job_name': 'test-job', + 'build_id': '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', + 'upstream_build_id': 'test-stream' + } + self.deployresult_string = json.dumps(self.deployresult_json) + + +class DeployresultGetTest(DeployresultTest): + + def setUp(self): + super(DeployresultGetTest, self).setUp() + self.deployresults_rsp = {'deployresults': [self.deployresult_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.deployresults_rsp) + deployresult_get = deployresults.DeployresultGet( + self.app, mock.Mock()) + args = ['-job-name', 'dfs'] + verifies = [('job_name', 'dfs')] + parsed_args = self.check_parser(deployresult_get, args, verifies) + deployresult_get.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '?job_name=dfs', + headers=clientmanager.ClientManager.headers) + + def test_get_all(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.deployresults_rsp) + deployresult_get = deployresults.DeployresultGet( + self.app, mock.Mock()) + args = [] + verifies = [] + parsed_args = self.check_parser(deployresult_get, args, verifies) + deployresult_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.deployresult_json) + deployresult_get_one = deployresults.DeployresultGetOne( + self.app, mock.Mock()) + args = ['def'] + verifies = [('deployresult_id', 'def')] + parsed_args = self.check_parser( + deployresult_get_one, args, verifies) + deployresult_get_one.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class DeployresultCreateTest(DeployresultTest): + + def setUp(self): + super(DeployresultCreateTest, self).setUp() + + def test_create_success(self): + succ_rsp = { + 'href': '{}/{}'.format(self.base_url, + self.deployresult_json.get('project_name')) + } + self.post_mock.return_value = fakes.FakeResponse(data=succ_rsp) + deployresult_create = deployresults.DeployresultCreate( + self.app, mock.Mock()) + args = [self.deployresult_string] + verifies = [('deployresult', self.deployresult_json)] + parsed_args = self.check_parser(deployresult_create, args, verifies) + deployresult_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 + deployresult_create = deployresults.DeployresultCreate( + self.app, mock.Mock()) + args = [self.deployresult_string] + verifies = [('deployresult', self.deployresult_json)] + parsed_args = self.check_parser( + deployresult_create, args, verifies) + deployresult_create.take_action(parsed_args) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_deployresults_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_deployresults_client.py new file mode 100644 index 0000000..03288fe --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_deployresults_client.py @@ -0,0 +1,81 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import deploy_results +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class DeployResultsClientTest(utils.TestCommand): + def setUp(self): + super(DeployResultsClientTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'deployresults') + self.deployresult_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.deployresult_id = '5a6dc1089a07c80f3c9f8d62' + self.deployresult_string = json.dumps(self.deployresult_json) + self.deployresult_client = deploy_results.DeployResultsClient() + + +class DeployResultsClientGetTest(DeployResultsClientTest): + + def setUp(self): + super(DeployResultsClientGetTest, self).setUp() + self.deployresults_rsp = {'deployresults': [self.deployresult_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.deployresults_rsp) + self.deployresult_client.get() + self.get_mock.assert_called_once_with( + self.base_url, + headers=clientmanager.ClientManager.headers) + + def test_get_search(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.deployresults_rsp) + self.deployresult_client.get(name='deployresult1') + self.get_mock.assert_called_once_with( + self.base_url + '?name=deployresult1', + headers=clientmanager.ClientManager.headers) + + def test_get_one(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.deployresult_json) + self.deployresult_client.get_one('2333') + self.get_mock.assert_called_once_with( + self.base_url + '/2333', + headers=clientmanager.ClientManager.headers) + + +class DeployResultsClientCreateTest(DeployResultsClientTest): + + def setUp(self): + super(DeployResultsClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format(self.base_url, self.deployresult_id) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=self.succ_rsp) + self.deployresult_client.create(self.deployresult_json) + 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 + self.deployresult_client.create(self.deployresult_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_pod_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_pod_client.py new file mode 100644 index 0000000..1df5660 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_pod_client.py @@ -0,0 +1,89 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import pods +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class PodClientTest(utils.TestCommand): + def setUp(self): + super(PodClientTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'pods') + self.pod_json = { + 'role': 'community-ci', + 'name': 'test_pod', + 'details': '', + 'mode': 'metal' + } + self.pod_client = pods.PodsClient() + self.pod_string = json.dumps(self.pod_json) + + +class PodClientGetTest(PodClientTest): + + def setUp(self): + super(PodClientGetTest, self).setUp() + self.pods_rsp = {'pods': [self.pod_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.pods_rsp) + self.pod_client.get() + self.get_mock.assert_called_once_with( + self.base_url, + headers=clientmanager.ClientManager.headers) + + def test_get_search(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.pods_rsp) + self.pod_client.get(name='pod1') + self.get_mock.assert_called_once_with( + self.base_url + '?name=pod1', + headers=clientmanager.ClientManager.headers) + + def test_get_one(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.pod_json) + self.pod_client.get_one('def') + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class PodClientCreateTest(PodClientTest): + + def setUp(self): + super(PodClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format(self.base_url, self.pod_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=self.succ_rsp) + self.pod_client.create(self.pod_json) + 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 + self.pod_client.create(self.pod_json) + + +class PodClientDeleteTest(PodClientTest): + + def setUp(self): + super(PodClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.pod_client.delete('def') + self.delete_mock.assert_called_once_with( + self.base_url + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.pod_client.delete('def') diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_pods.py b/testapi/testapi-client/testapiclient/tests/unit/test_pods.py new file mode 100644 index 0000000..5d44d4c --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_pods.py @@ -0,0 +1,117 @@ +import json + +from mock import mock +from six.moves.urllib import parse +import testtools + +from testapiclient.cli import pods +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class PodTest(utils.TestCommand): + def setUp(self): + super(PodTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'pods') + self.pod_json = { + 'role': 'community-ci', + 'name': 'test_pod', + 'details': '', + 'mode': 'metal' + } + self.pod_string = json.dumps(self.pod_json) + + +class PodGetTest(PodTest): + + def setUp(self): + super(PodGetTest, self).setUp() + self.pods_rsp = {'pods': [self.pod_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.pods_rsp) + pod_get = pods.PodGet(self.app, mock.Mock()) + args = ['-name', 'dfs'] + verifies = [('name', 'dfs')] + parsed_args = self.check_parser(pod_get, args, verifies) + pod_get.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '?name=dfs', + headers=clientmanager.ClientManager.headers) + + def test_get_all(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.pods_rsp) + pod_get = pods.PodGet(self.app, mock.Mock()) + args = [] + verifies = [] + parsed_args = self.check_parser(pod_get, args, verifies) + pod_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.pod_json) + pod_get_one = pods.PodGetOne(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(pod_get_one, args, verifies) + pod_get_one.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class PodCreateTest(PodTest): + + def setUp(self): + super(PodCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format(self.base_url, self.pod_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=self.succ_rsp) + pod_create = pods.PodCreate(self.app, mock.Mock()) + args = [self.pod_string] + verifies = [('pod', self.pod_json)] + parsed_args = self.check_parser(pod_create, args, verifies) + pod_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 + pod_create = pods.PodCreate(self.app, mock.Mock()) + args = [self.pod_string] + verifies = [('pod', self.pod_json)] + parsed_args = self.check_parser(pod_create, args, verifies) + pod_create.take_action(parsed_args) + + +class PodDeleteTest(PodTest): + + def setUp(self): + super(PodDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + pod_delete = pods.PodDelete(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(pod_delete, args, verifies) + pod_delete.take_action(parsed_args) + self.delete_mock.assert_called_once_with( + self.base_url + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + pod_delete = pods.PodDelete(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(pod_delete, args, verifies) + pod_delete.take_action(parsed_args) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_project_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_project_client.py new file mode 100644 index 0000000..7aa11e6 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_project_client.py @@ -0,0 +1,111 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import projects +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class ProjectClientTest(utils.TestCommand): + def setUp(self): + super(ProjectClientTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'projects') + self.project_json = { + 'description': 'test_description', + 'name': 'test_project', + } + self.project_client = projects.ProjectsClient() + self.project_string = json.dumps(self.project_json) + + +class ProjectClientGetTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientGetTest, self).setUp() + self.projects_rsp = {'projects': [self.project_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.projects_rsp) + self.project_client.get() + self.get_mock.assert_called_once_with( + self.base_url, + headers=clientmanager.ClientManager.headers) + + def test_get_search(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.projects_rsp) + self.project_client.get(name='project1') + self.get_mock.assert_called_once_with( + self.base_url + '?name=project1', + headers=clientmanager.ClientManager.headers) + + def test_get_one(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.project_json) + self.project_client.get_one('def') + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class ProjectClientCreateTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.project_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.project_client.create(self.project_json) + 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 + self.project_client.create(self.project_json) + + +class ProjectClientDeleteTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.project_client.delete('def') + self.delete_mock.assert_called_once_with( + self.base_url + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.project_client.delete('def') + + +class ProjectClientUpdateTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.project_client.update('def', self.project_json) + self.put_mock.assert_called_once_with( + self.base_url + '/def', + data=self.project_string, + headers=clientmanager.ClientManager.headers) + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.project_client.update('def', self.project_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_projects.py b/testapi/testapi-client/testapiclient/tests/unit/test_projects.py new file mode 100644 index 0000000..86486ce --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_projects.py @@ -0,0 +1,140 @@ +import json + +from mock import mock +from six.moves.urllib import parse +import testtools + +from testapiclient.cli import projects +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class ProjectTest(utils.TestCommand): + def setUp(self): + super(ProjectTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'projects') + self.project_json = { + 'name': 'test_project', + 'description': '' + } + self.project_string = json.dumps(self.project_json) + + +class ProjectGetTest(ProjectTest): + + def setUp(self): + super(ProjectGetTest, self).setUp() + self.projects_rsp = {'projects': [self.project_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.projects_rsp) + project_get = projects.ProjectGet(self.app, mock.Mock()) + args = ['-name', 'dfs'] + verifies = [('name', 'dfs')] + parsed_args = self.check_parser(project_get, args, verifies) + project_get.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '?name=dfs', + headers=clientmanager.ClientManager.headers) + + def test_get_all(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.projects_rsp) + project_get = projects.ProjectGet(self.app, mock.Mock()) + args = [] + verifies = [] + parsed_args = self.check_parser(project_get, args, verifies) + project_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.project_json) + project_get_one = projects.ProjectGetOne(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(project_get_one, args, verifies) + project_get_one.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class ProjectCreateTest(ProjectTest): + + def setUp(self): + super(ProjectCreateTest, self).setUp() + + def test_create_success(self): + succ_rsp = { + 'href': '{}/{}'.format(self.base_url, + self.project_json.get('name')) + } + self.post_mock.return_value = fakes.FakeResponse(data=succ_rsp) + project_create = projects.ProjectCreate(self.app, mock.Mock()) + args = [self.project_string] + verifies = [('project', self.project_json)] + parsed_args = self.check_parser(project_create, args, verifies) + project_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 + project_create = projects.ProjectCreate(self.app, mock.Mock()) + args = [self.project_string] + verifies = [('project', self.project_json)] + parsed_args = self.check_parser(project_create, args, verifies) + project_create.take_action(parsed_args) + + +class ProjectDeleteTest(ProjectTest): + + def setUp(self): + super(ProjectDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + project_delete = projects.ProjectDelete(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(project_delete, args, verifies) + project_delete.take_action(parsed_args) + self.delete_mock.assert_called_once_with( + self.base_url + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + project_delete = projects.ProjectDelete(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(project_delete, args, verifies) + project_delete.take_action(parsed_args) + + +class ProjectPutTest(ProjectTest): + + def setUp(self): + super(ProjectPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse(data=self.project_json) + project_put = projects.ProjectPut(self.app, mock.Mock()) + args = ['def', self.project_string] + verifies = [('name', 'def'), ('project', self.project_json)] + parsed_args = self.check_parser(project_put, args, verifies) + project_put.take_action(parsed_args) + self.put_mock.assert_called_once() + + def test_put_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + project_put = projects.ProjectPut(self.app, mock.Mock()) + args = ['def', self.project_string] + verifies = [('name', 'def'), ('project', self.project_json)] + parsed_args = self.check_parser(project_put, args, verifies) + project_put.take_action(parsed_args) 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/tests/unit/test_results_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_results_client.py new file mode 100644 index 0000000..ae677f7 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_results_client.py @@ -0,0 +1,78 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import results +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class ResultClientTest(utils.TestCommand): + def setUp(self): + super(ResultClientTest, 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_id = '5a6dc1089a07c80f3c9f8d62' + self.result_string = json.dumps(self.result_json) + self.result_client = results.ResultsClient() + + +class ResultClientGetTest(ResultClientTest): + + def setUp(self): + super(ResultClientGetTest, self).setUp() + self.results_rsp = {'results': [self.result_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.results_rsp) + self.result_client.get() + self.get_mock.assert_called_once_with( + self.base_url, + headers=clientmanager.ClientManager.headers) + + def test_get_search(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.results_rsp) + self.result_client.get(name='result1') + self.get_mock.assert_called_once_with( + self.base_url + '?name=result1', + headers=clientmanager.ClientManager.headers) + + def test_get_one(self): + self.get_mock.return_value = fakes.FakeResponse(data=self.result_json) + self.result_client.get_one('2333') + self.get_mock.assert_called_once_with( + self.base_url + '/2333', + headers=clientmanager.ClientManager.headers) + + +class ResultClientCreateTest(ResultClientTest): + + def setUp(self): + super(ResultClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format(self.base_url, self.result_id) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=self.succ_rsp) + self.result_client.create(self.result_json) + 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 + self.result_client.create(self.result_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario.py new file mode 100644 index 0000000..b14cf04 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario.py @@ -0,0 +1,147 @@ +import json + +from mock import mock +from six.moves.urllib import parse +import testtools + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes as fk +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class ScenarioTest(utils.TestCommand): + def setUp(self): + super(ScenarioTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios') + self.scenario_json = { + "installers": [], + "name": "test_scenario" + } + self.scenario_string = json.dumps(self.scenario_json) + + +class ScenarioGetTest(ScenarioTest): + + def setUp(self): + super(ScenarioGetTest, self).setUp() + self.scenarios_rsp = {'scenarios': [self.scenario_json]} + + def test_get(self): + self.get_mock.return_value = fk.FakeResponse(data=self.scenarios_rsp) + scenario_get = scenarios.ScenarioGet(self.app, mock.Mock()) + args = ['-name', 's1', '-installer', + 'i1', '---version', 'v1', '-project', 'p1'] + verifies = [ + ('name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', 'p1')] + parsed_args = self.check_parser(scenario_get, args, verifies) + scenario_get.take_action(parsed_args) + kall = self.get_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + '?version=v1&name=s1&installer=i1&project=p1') + + def test_get_all(self): + self.get_mock.return_value = fk.FakeResponse(data=self.scenarios_rsp) + scenario_get = scenarios.ScenarioGet(self.app, mock.Mock()) + args = [] + verifies = [] + parsed_args = self.check_parser(scenario_get, args, verifies) + scenario_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 = fk.FakeResponse(data=self.scenario_json) + scenario_get_one = scenarios.ScenarioGetOne(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(scenario_get_one, args, verifies) + scenario_get_one.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class ScenarioCreateTest(ScenarioTest): + + def setUp(self): + super(ScenarioCreateTest, self).setUp() + + def test_create_success(self): + succ_rsp = { + 'href': '{}/{}'.format(self.base_url, + self.scenario_json.get('name')) + } + self.post_mock.return_value = fk.FakeResponse(data=succ_rsp) + scenario_create = scenarios.ScenarioCreate(self.app, mock.Mock()) + args = [self.scenario_string] + verifies = [('scenario', self.scenario_json)] + parsed_args = self.check_parser(scenario_create, args, verifies) + scenario_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 + scenario_create = scenarios.ScenarioCreate(self.app, mock.Mock()) + args = [self.scenario_string] + verifies = [('scenario', self.scenario_json)] + parsed_args = self.check_parser(scenario_create, args, verifies) + scenario_create.take_action(parsed_args) + + +class ScenarioDeleteTest(ScenarioTest): + + def setUp(self): + super(ScenarioDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fk.FakeResponse() + scenario_delete = scenarios.ScenarioDelete(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(scenario_delete, args, verifies) + scenario_delete.take_action(parsed_args) + self.delete_mock.assert_called_once_with( + self.base_url + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + scenario_delete = scenarios.ScenarioDelete(self.app, mock.Mock()) + args = ['def'] + verifies = [('name', 'def')] + parsed_args = self.check_parser(scenario_delete, args, verifies) + scenario_delete.take_action(parsed_args) + + +class ScenarioPutTest(ScenarioTest): + + def setUp(self): + super(ScenarioPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fk.FakeResponse(data=self.scenario_json) + scenario_put = scenarios.ScenarioPut(self.app, mock.Mock()) + args = ['def', self.scenario_string] + verifies = [('name', 'def'), ('scenario', self.scenario_json)] + parsed_args = self.check_parser(scenario_put, args, verifies) + scenario_put.take_action(parsed_args) + self.put_mock.assert_called_once() + + def test_put_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + scenario_put = scenarios.ScenarioPut(self.app, mock.Mock()) + args = ['def', self.scenario_string] + verifies = [('name', 'def'), ('scenario', self.scenario_json)] + parsed_args = self.check_parser(scenario_put, args, verifies) + scenario_put.take_action(parsed_args) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_client.py new file mode 100644 index 0000000..6e9e0fa --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_client.py @@ -0,0 +1,104 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class ScenarioClientTest(utils.TestCommand): + def setUp(self): + super(ScenarioClientTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios') + self.scenario_json = { + "installers": [], + "name": "test_scenario" + } + self.scenario_client = scenarios.ScenariosClient() + self.scenario_string = json.dumps(self.scenario_json) + + +class ScenarioClientGetTest(ScenarioClientTest): + + def setUp(self): + super(ScenarioClientGetTest, self).setUp() + self.scenarios_rsp = {'scenarios': [self.scenario_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.scenarios_rsp) + self.scenario_client.get() + 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.scenario_json) + self.scenario_client.get_one('def') + self.get_mock.assert_called_once_with( + self.base_url + '/def', + headers=clientmanager.ClientManager.headers) + + +class ScenarioClientCreateTest(ScenarioClientTest): + + def setUp(self): + super(ScenarioClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.scenario_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.scenario_client.create(self.scenario_json) + 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 + self.scenario_client.create(self.scenario_json) + + +class ScenarioClientDeleteTest(ScenarioClientTest): + + def setUp(self): + super(ScenarioClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.scenario_client.delete('def') + self.delete_mock.assert_called_once_with( + self.base_url + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.scenario_client.delete('def') + + +class ScenarioClientUpdateTest(ScenarioClientTest): + + def setUp(self): + super(ScenarioClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.scenario_client.update( + 'def', self.scenario_json) + self.put_mock.assert_called_once_with( + self.base_url + '/def', + data=self.scenario_string, + headers=clientmanager.ClientManager.headers) + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.scenario_client.update('def', self.scenario_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_custom.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_custom.py new file mode 100644 index 0000000..e3f89c0 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_custom.py @@ -0,0 +1,88 @@ + + +from mock import mock +from six.moves.urllib import parse + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class CustomTest(utils.TestCommand): + def setUp(self): + super(CustomTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios/{}/customs') + self.scenario_name = 's1' + self.custom_input = 'custom' + self.custom_parsed = ['custom'] + + +class CustomCreateTest(CustomTest): + + def setUp(self): + super(CustomCreateTest, self).setUp() + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=None) + custom_create = scenarios.CustomCreate(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', + 'v1', '--project', 'p1', self.custom_input] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', 'p1'), + ('custom', self.custom_parsed)] + parsed_args = self.check_parser(custom_create, args, verifies) + custom_create.take_action(parsed_args) + self.post_mock.assert_called_once() + + +class CustomDeleteTest(CustomTest): + + def setUp(self): + super(CustomDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse(data=None) + custom_delete = scenarios.CustomDelete(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', + '--version', 'v1', '--project', 'p1', 'def'] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', 'p1'), + ('name', ['def'])] + parsed_args = self.check_parser(custom_delete, args, verifies) + custom_delete.take_action(parsed_args) + kall = self.delete_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + '?version=v1&project=p1&installer=i1') + + +class CustomPutTest(CustomTest): + + def setUp(self): + super(CustomPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse( + data=None) + custom_put = scenarios.CustomPut(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', 'v1', + '--project', 'p1', self.custom_input] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', 'p1'), + ('custom', self.custom_parsed)] + parsed_args = self.check_parser(custom_put, args, verifies) + custom_put.take_action(parsed_args) + self.put_mock.assert_called_once() diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_custom_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_custom_client.py new file mode 100644 index 0000000..7c6d62c --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_custom_client.py @@ -0,0 +1,106 @@ +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class CustomClientTest(utils.TestCommand): + def setUp(self): + super(CustomClientTest, self).setUp() + self.scenario_name = 'scenrio1' + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/customs'.format(self.scenario_name)) + self.custom_raw = 'custom' + self.custom_input = ['custom'] + self.installer_name = 'installer' + self.version_name = 'version' + self.project_name = 'project' + self.custom_client = scenarios.CustomsClient() + + +class CustomClientCreateTest(CustomClientTest): + + def setUp(self): + super(CustomClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.scenario_name) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.custom_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.custom_raw) + 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 + self.custom_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.custom_raw) + + +class CustomClientDeleteTest(CustomClientTest): + + def setUp(self): + super(CustomClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.custom_client.delete( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.custom_raw) + kall = self.delete_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + + '?installer=installer&version=version&project=project') + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.custom_client.delete( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.custom_raw) + + +class CustomClientUpdateTest(CustomClientTest): + + def setUp(self): + super(CustomClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.custom_client.update( + self.scenario_name, + self.installer_name, + self.version_name, + self.project_name, + self.custom_raw) + kall = self.put_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + + '?installer=installer&version=version&project=project') + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.custom_client.update( + self.scenario_name, + self.installer_name, + self.version_name, + self.project_name, + self.custom_raw) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_installer.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_installer.py new file mode 100644 index 0000000..2246a59 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_installer.py @@ -0,0 +1,74 @@ +import json + +from mock import mock +from six.moves.urllib import parse + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class InstallerTest(utils.TestCommand): + def setUp(self): + super(InstallerTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios/{}/installers') + self.scenario_name = 's1' + self.installer_json = { + 'versions': [], + 'installer': 'test-installer', + } + self.installer_string = json.dumps(self.installer_json) + + +class InstallerCreateTest(InstallerTest): + + def setUp(self): + super(InstallerCreateTest, self).setUp() + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=None) + installer_create = scenarios.InstallerCreate(self.app, mock.Mock()) + args = ['--scenario-name', 's1', self.installer_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', self.installer_json)] + parsed_args = self.check_parser(installer_create, args, verifies) + installer_create.take_action(parsed_args) + self.post_mock.assert_called_once() + + +class InstallerDeleteTest(InstallerTest): + + def setUp(self): + super(InstallerDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse(data=None) + installer_delete = scenarios.InstallerDelete(self.app, mock.Mock()) + args = ['--scenario-name', 's1', 'def'] + verifies = [('scenario_name', 's1'), ('name', ['def'])] + parsed_args = self.check_parser(installer_delete, args, verifies) + installer_delete.take_action(parsed_args) + self.delete_mock.assert_called_once_with( + self.base_url.format(parsed_args.scenario_name), + data=json.dumps(['def']), + headers=clientmanager.ClientManager.headers) + + +class InstallerPutTest(InstallerTest): + + def setUp(self): + super(InstallerPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse( + data=None) + installer_put = scenarios.InstallerPut(self.app, mock.Mock()) + args = ['--scenario-name', 's1', self.installer_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', self.installer_json)] + parsed_args = self.check_parser(installer_put, args, verifies) + installer_put.take_action(parsed_args) + self.put_mock.assert_called_once() diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_installer_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_installer_client.py new file mode 100644 index 0000000..71ba150 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_installer_client.py @@ -0,0 +1,86 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class InstallerClientTest(utils.TestCommand): + def setUp(self): + super(InstallerClientTest, self).setUp() + self.scenario_name = 'scenrio1' + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/installers'.format(self.scenario_name)) + self.installer_json = { + 'versions': [], + 'installer': 'test-installer', + } + self.installer_client = scenarios.InstallersClient() + self.installer_string = json.dumps(self.installer_json) + + +class InstallerClientCreateTest(InstallerClientTest): + + def setUp(self): + super(InstallerClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.installer_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.installer_client.create(self.scenario_name, self.installer_json) + 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 + self.installer_client.create( + self.scenario_name, self.installer_json) + + +class InstallerClientDeleteTest(InstallerClientTest): + + def setUp(self): + super(InstallerClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.installer_client.delete(self.scenario_name, 'def') + self.delete_mock.assert_called_once_with( + self.base_url, + data=json.dumps(['def']), + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.installer_client.delete(self.scenario_name, 'def') + + +class InstallerClientUpdateTest(InstallerClientTest): + + def setUp(self): + super(InstallerClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.installer_client.update( + self.scenario_name, + self.installer_json) + self.put_mock.assert_called_once_with( + self.base_url, + data=self.installer_string, + headers=clientmanager.ClientManager.headers) + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.installer_client.update('def', self.installer_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_project.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_project.py new file mode 100644 index 0000000..7bd6645 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_project.py @@ -0,0 +1,90 @@ +import json + +from mock import mock +from six.moves.urllib import parse + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class ProjectTest(utils.TestCommand): + def setUp(self): + super(ProjectTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios/{}/projects') + self.scenario_name = 's1' + self.project_json = { + 'trust_indicators': [], + 'project': 'test-project', + 'scores': [], + 'customs': [] + } + self.project_string = json.dumps(self.project_json) + + +class ProjectCreateTest(ProjectTest): + + def setUp(self): + super(ProjectCreateTest, self).setUp() + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=None) + project_create = scenarios.ProjectCreate(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', 'v1', + self.project_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', self.project_json)] + parsed_args = self.check_parser(project_create, args, verifies) + project_create.take_action(parsed_args) + self.post_mock.assert_called_once() + + +class ProjectDeleteTest(ProjectTest): + + def setUp(self): + super(ProjectDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse(data=None) + project_delete = scenarios.ProjectDelete(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', 'v1', + 'def'] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('name', ['def'])] + parsed_args = self.check_parser(project_delete, args, verifies) + project_delete.take_action(parsed_args) + kall = self.delete_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + '?version=v1&installer=i1') + + +class ProjectPutTest(ProjectTest): + + def setUp(self): + super(ProjectPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse( + data=None) + project_put = scenarios.ProjectPut(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', 'v1', + self.project_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', self.project_json)] + parsed_args = self.check_parser(project_put, args, verifies) + project_put.take_action(parsed_args) + self.put_mock.assert_called_once() diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_project_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_project_client.py new file mode 100644 index 0000000..f8c3d60 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_project_client.py @@ -0,0 +1,104 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class ProjectClientTest(utils.TestCommand): + def setUp(self): + super(ProjectClientTest, self).setUp() + self.scenario_name = 'scenrio1' + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/projects'.format(self.scenario_name)) + self.project_json = { + 'trust_indicators': [], + 'project': 'test-project', + 'scores': [], + 'customs': [] + } + self.installer_name = 'installer' + self.version_name = 'version' + self.project_client = scenarios.ProjectsClient() + self.project_string = json.dumps(self.project_json) + + +class ProjectClientCreateTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.project_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.project_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_json) + 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 + self.project_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_json) + + +class ProjectClientDeleteTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.project_client.delete( + self.scenario_name, self.installer_name, + self.version_name, 'def') + kall = self.delete_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + '?installer=installer&version=version') + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.project_client.delete( + self.scenario_name, self.installer_name, + self.version_name, 'def') + + +class ProjectClientUpdateTest(ProjectClientTest): + + def setUp(self): + super(ProjectClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.project_client.update( + self.scenario_name, + self.installer_name, + self.version_name, + self.project_json) + kall = self.put_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + '?installer=installer&version=version') + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.project_client.update( + self.scenario_name, + self.installer_name, + self.version_name, + self.project_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_score.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_score.py new file mode 100644 index 0000000..3b0e1c6 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_score.py @@ -0,0 +1,42 @@ +import json + +from mock import mock +from six.moves.urllib import parse + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class ScoreTest(utils.TestCommand): + def setUp(self): + super(ScoreTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios/{}/scores') + self.scenario_name = 's1' + self.score_json = { + 'score': 'test_score1', + 'date': '2018/01/2' + } + self.score_string = json.dumps(self.score_json) + + +class ScoreCreateTest(ScoreTest): + + def setUp(self): + super(ScoreCreateTest, self).setUp() + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=None) + score_create = scenarios.ScoreCreate(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', 'v1', + '--project', 'p1', self.score_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', 'p1'), + ('score', self.score_json)] + parsed_args = self.check_parser(score_create, args, verifies) + score_create.take_action(parsed_args) + self.post_mock.assert_called_once() diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_score_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_score_client.py new file mode 100644 index 0000000..beebd47 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_score_client.py @@ -0,0 +1,53 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class ScoreClientTest(utils.TestCommand): + def setUp(self): + super(ScoreClientTest, self).setUp() + self.scenario_name = 'scenrio1' + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/scores'.format(self.scenario_name)) + self.score_json = { + 'score': 'test_score1', + 'date': '2018/01/2' + } + self.installer_name = 'installer' + self.version_name = 'version' + self.project_name = 'project' + self.score_client = scenarios.ScoresClient() + self.score_string = json.dumps(self.score_json) + + +class ScoreClientCreateTest(ScoreClientTest): + + def setUp(self): + super(ScoreClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.score_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.score_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.score_json) + 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 + self.score_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.score_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_trust_indicator.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_trust_indicator.py new file mode 100644 index 0000000..04be30c --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_trust_indicator.py @@ -0,0 +1,45 @@ +import json + +from mock import mock +from six.moves.urllib import parse + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class TrustIndicatorTest(utils.TestCommand): + def setUp(self): + super(TrustIndicatorTest, self).setUp() + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/trustindicators' + ) + self.scenario_name = 's1' + self.trust_indicator_json = { + 'status': 'test_status', + 'date': '2018/01/2' + } + self.trust_indicator_string = json.dumps(self.trust_indicator_json) + + +class TrustIndicatorCreateTest(TrustIndicatorTest): + + def setUp(self): + super(TrustIndicatorCreateTest, self).setUp() + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=None) + ti_create = scenarios.TrustIndicatorCreate(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', '--version', 'v1', + '--project', 'p1', self.trust_indicator_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('project', 'p1'), + ('trust_indicator', self.trust_indicator_json)] + parsed_args = self.check_parser(ti_create, args, verifies) + ti_create.take_action(parsed_args) + self.post_mock.assert_called_once() diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_trust_indicator_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_trust_indicator_client.py new file mode 100644 index 0000000..e44e2d2 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_trust_indicator_client.py @@ -0,0 +1,53 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils + + +class TrustIndicatorClientTest(utils.TestCommand): + def setUp(self): + super(TrustIndicatorClientTest, self).setUp() + self.scenario_name = 'scenrio1' + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/trust_indicators'.format(self.scenario_name)) + self.trust_indicator_json = { + 'status': 'test_status', + 'date': '2018/01/2' + } + self.installer_name = 'installer' + self.version_name = 'version' + self.project_name = 'project' + self.trust_indicator_client = scenarios.TrustIndicatorsClient() + self.trust_indicator_string = json.dumps(self.trust_indicator_json) + + +class TrustIndicatorClientCreateTest(TrustIndicatorClientTest): + + def setUp(self): + super(TrustIndicatorClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.trust_indicator_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.trust_indicator_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.trust_indicator_json) + 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 + self.trust_indicator_client.create( + self.scenario_name, self.installer_name, + self.version_name, self.project_name, + self.trust_indicator_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_version.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_version.py new file mode 100644 index 0000000..280e9a6 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_version.py @@ -0,0 +1,107 @@ +import json + +from mock import mock +from six.moves.urllib import parse + +from testapiclient.cli import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class VersionTest(utils.TestCommand): + def setUp(self): + super(VersionTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'scenarios/{}/versions') + self.scenario_name = 's1' + self.version_json = { + 'projects': [], + 'version': 'test-version', + 'owner': 'test_owner' + } + self.version_string = json.dumps(self.version_json) + + +class VersionCreateTest(VersionTest): + + def setUp(self): + super(VersionCreateTest, self).setUp() + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse(data=None) + version_create = scenarios.VersionCreate(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', self.version_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', self.version_json)] + parsed_args = self.check_parser(version_create, args, verifies) + version_create.take_action(parsed_args) + self.post_mock.assert_called_once() + + +class VersionDeleteTest(VersionTest): + + def setUp(self): + super(VersionDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse(data=None) + version_delete = scenarios.VersionDelete(self.app, mock.Mock()) + args = ['--scenario-name', 's1', '--installer', 'i1', 'def'] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('name', ['def'])] + parsed_args = self.check_parser(version_delete, args, verifies) + version_delete.take_action(parsed_args) + self.delete_mock.assert_called_once_with( + self.base_url.format(parsed_args.scenario_name) + '?installer=i1', + data=json.dumps(['def']), + headers=clientmanager.ClientManager.headers) + + +class VersionPutTest(VersionTest): + + def setUp(self): + super(VersionPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse( + data=None) + version_put = scenarios.VersionPut(self.app, mock.Mock()) + args = [ + '--scenario-name', 's1', '--installer', 'i1', self.version_string] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', self.version_json)] + parsed_args = self.check_parser(version_put, args, verifies) + version_put.take_action(parsed_args) + self.put_mock.assert_called_once() + + +class VersionOwnerPutTest(VersionTest): + + def setUp(self): + super(VersionOwnerPutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse( + data=None) + version_put = scenarios.VersionOwnerPut(self.app, mock.Mock()) + version_owner = { + 'owner': 'test_owner' + } + args = [ + '--scenario-name', 's1', '--installer', 'i1', + '--version', 'v1', json.dumps(version_owner)] + verifies = [ + ('scenario_name', 's1'), + ('installer', 'i1'), + ('version', 'v1'), + ('owner', version_owner)] + parsed_args = self.check_parser(version_put, args, verifies) + version_put.take_action(parsed_args) + self.put_mock.assert_called_once() diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_scenario_version_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_version_client.py new file mode 100644 index 0000000..1ae2409 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_scenario_version_client.py @@ -0,0 +1,96 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import scenarios +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class VersionClientTest(utils.TestCommand): + def setUp(self): + super(VersionClientTest, self).setUp() + self.scenario_name = 'scenrio1' + self.base_url = parse.urljoin( + self.api_url, + 'scenarios/{}/versions'.format(self.scenario_name)) + self.version_json = { + 'projects': [], + 'version': 'test-version', + 'owner': 'test_owner' + } + self.installer_name = 'installer' + self.version_client = scenarios.VersionsClient() + self.version_string = json.dumps(self.version_json) + + +class VersionClientCreateTest(VersionClientTest): + + def setUp(self): + super(VersionClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.version_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.version_client.create( + self.scenario_name, self.installer_name, self.version_json) + 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 + self.version_client.create( + self.scenario_name, self.installer_name, self.version_json) + + +class VersionClientDeleteTest(VersionClientTest): + + def setUp(self): + super(VersionClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.version_client.delete( + self.scenario_name, self.installer_name, 'def') + kall = self.delete_mock.call_args + args, kwargs = kall + self.assert_url( + args[0], + self.base_url + '?installer=installer') + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.version_client.delete( + self.scenario_name, self.installer_name, 'def') + + +class VersionClientUpdateTest(VersionClientTest): + + def setUp(self): + super(VersionClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.version_client.update( + self.scenario_name, + self.installer_name, + self.version_json) + self.put_mock.assert_called_once_with( + self.base_url + '?installer=installer', + data=self.version_string, + headers=clientmanager.ClientManager.headers) + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.version_client.update( + self.scenario_name, + self.installer_name, + self.version_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_testcase_client.py b/testapi/testapi-client/testapiclient/tests/unit/test_testcase_client.py new file mode 100644 index 0000000..d80ae27 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_testcase_client.py @@ -0,0 +1,118 @@ +import json + +from six.moves.urllib import parse +import testtools + +from testapiclient.client import testcases +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class TestcaseClientTest(utils.TestCommand): + def setUp(self): + super(TestcaseClientTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'projects/{}/cases') + self.project_name = 'functest' + self.testcase_json = { + 'run': '', + 'name': 'test-case', + 'ci_loop': '', + 'tags': '', + 'url': '', + 'blocking': '', + 'domains': '', + 'dependencies': '', + 'version': '', + 'criteria': '', + 'tier': '', + 'trust': '', + 'catalog_description': '', + 'description': '' + } + self.testcase_client = testcases.TestcasesClient() + self.testcase_string = json.dumps(self.testcase_json) + + +class TestcaseClientGetTest(TestcaseClientTest): + + def setUp(self): + super(TestcaseClientGetTest, self).setUp() + self.testcases_rsp = {'testcases': [self.testcase_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.testcases_rsp) + self.testcase_client.get(self.project_name) + self.get_mock.assert_called_once_with( + self.base_url.format(self.project_name), + headers=clientmanager.ClientManager.headers) + + def test_get_one(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.testcase_json) + self.testcase_client.get_one(self.project_name, 'def') + self.get_mock.assert_called_once_with( + self.base_url.format(self.project_name) + '/def', + headers=clientmanager.ClientManager.headers) + + +class TestcaseClientCreateTest(TestcaseClientTest): + + def setUp(self): + super(TestcaseClientCreateTest, self).setUp() + self.succ_rsp = { + 'href': '{}/{}'.format( + self.base_url, self.testcase_json.get('name')) + } + + def test_create_success(self): + self.post_mock.return_value = fakes.FakeResponse( + data=self.succ_rsp) + self.testcase_client.create(self.project_name, self.testcase_json) + 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 + self.testcase_client.create(self.project_name, self.testcase_json) + + +class TestcaseClientDeleteTest(TestcaseClientTest): + + def setUp(self): + super(TestcaseClientDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + self.testcase_client.delete(self.project_name, 'def') + self.delete_mock.assert_called_once_with( + self.base_url.format(self.project_name) + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + self.testcase_client.delete(self.project_name, 'def') + + +class TestcaseClientUpdateTest(TestcaseClientTest): + + def setUp(self): + super(TestcaseClientUpdateTest, self).setUp() + + def test_update_success(self): + self.put_mock.return_value = fakes.FakeResponse() + self.testcase_client.update( + self.project_name, 'def', self.testcase_json) + self.put_mock.assert_called_once_with( + self.base_url.format(self.project_name) + '/def', + data=self.testcase_string, + headers=clientmanager.ClientManager.headers) + + def test_update_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + self.testcase_client.update( + self.project_name, 'def', self.testcase_json) diff --git a/testapi/testapi-client/testapiclient/tests/unit/test_testcases.py b/testapi/testapi-client/testapiclient/tests/unit/test_testcases.py new file mode 100644 index 0000000..6fd2120 --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/test_testcases.py @@ -0,0 +1,155 @@ +import json + +from mock import mock +from six.moves.urllib import parse +import testtools + +from testapiclient.cli import testcases +from testapiclient.tests.unit import fakes +from testapiclient.tests.unit import utils +from testapiclient.utils import clientmanager + + +class TestcaseTest(utils.TestCommand): + def setUp(self): + super(TestcaseTest, self).setUp() + self.base_url = parse.urljoin(self.api_url, 'projects/{}/cases') + self.project_name = 'functest' + self.testcase_json = { + 'run': '', + 'name': 'test-case', + 'ci_loop': '', + 'tags': '', + 'url': '', + 'blocking': '', + 'domains': '', + 'dependencies': '', + 'version': '', + 'criteria': '', + 'tier': '', + 'trust': '', + 'catalog_description': '', + 'description': '' + } + self.testcase_string = json.dumps(self.testcase_json) + + +class TestcaseGetTest(TestcaseTest): + + def setUp(self): + super(TestcaseGetTest, self).setUp() + self.testcases_rsp = {'testcases': [self.testcase_json]} + + def test_get(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.testcases_rsp) + testcase_get = testcases.TestcaseGet(self.app, mock.Mock()) + args = ['--project-name', 'dfs'] + verifies = [('project_name', 'dfs')] + parsed_args = self.check_parser(testcase_get, args, verifies) + testcase_get.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url.format(parsed_args.project_name), + headers=clientmanager.ClientManager.headers) + + def test_get_one(self): + self.get_mock.return_value = fakes.FakeResponse( + data=self.testcase_json) + testcase_get_one = testcases.TestcaseGetOne(self.app, mock.Mock()) + args = ['--project-name', 'functest', 'def'] + verifies = [('project_name', 'functest'), ('name', 'def')] + parsed_args = self.check_parser(testcase_get_one, args, verifies) + testcase_get_one.take_action(parsed_args) + self.get_mock.assert_called_once_with( + self.base_url.format(parsed_args.project_name) + '/def', + headers=clientmanager.ClientManager.headers) + + +class TestcaseCreateTest(TestcaseTest): + + def setUp(self): + super(TestcaseCreateTest, self).setUp() + + def test_create_success(self): + succ_rsp = { + 'href': '{}/{}'.format(self.base_url.format(self.project_name), + self.testcase_json.get('name')) + } + self.post_mock.return_value = fakes.FakeResponse(data=succ_rsp) + testcase_create = testcases.TestcaseCreate(self.app, mock.Mock()) + args = ['--project-name', 'functest', self.testcase_string] + verifies = [ + ('project_name', 'functest'), + ('testcase', self.testcase_json)] + parsed_args = self.check_parser(testcase_create, args, verifies) + testcase_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 + testcase_create = testcases.TestcaseCreate(self.app, mock.Mock()) + args = ['--project-name', 'functest', self.testcase_string] + verifies = [ + ('project_name', 'functest'), + ('testcase', self.testcase_json)] + parsed_args = self.check_parser(testcase_create, args, verifies) + testcase_create.take_action(parsed_args) + + +class TestcaseDeleteTest(TestcaseTest): + + def setUp(self): + super(TestcaseDeleteTest, self).setUp() + + def test_delete_success(self): + self.delete_mock.return_value = fakes.FakeResponse() + testcase_delete = testcases.TestcaseDelete(self.app, mock.Mock()) + args = ['--project-name', 'functest', 'def'] + verifies = [('project_name', 'functest'), ('name', 'def')] + parsed_args = self.check_parser(testcase_delete, args, verifies) + testcase_delete.take_action(parsed_args) + self.delete_mock.assert_called_once_with( + self.base_url.format(parsed_args.project_name) + '/def', + data=None, + headers=clientmanager.ClientManager.headers) + + def test_delete_failure(self): + with testtools.ExpectedException(Exception, 'Delete failed: Error'): + self.delete_mock.return_value = utils.FAKE_FAILURE + testcase_delete = testcases.TestcaseDelete(self.app, mock.Mock()) + args = ['--project-name', 'functest', 'def'] + verifies = [('project_name', 'functest'), ('name', 'def')] + parsed_args = self.check_parser(testcase_delete, args, verifies) + testcase_delete.take_action(parsed_args) + + +class TestcasePutTest(TestcaseTest): + + def setUp(self): + super(TestcasePutTest, self).setUp() + + def test_put_success(self): + self.put_mock.return_value = fakes.FakeResponse( + data=self.testcase_json) + testcase_put = testcases.TestcasePut(self.app, mock.Mock()) + args = ['--project-name', 'functest', 'def', self.testcase_string] + verifies = [ + ('project_name', 'functest'), + ('name', 'def'), + ('testcase', self.testcase_json)] + parsed_args = self.check_parser(testcase_put, args, verifies) + testcase_put.take_action(parsed_args) + self.put_mock.assert_called_once() + + def test_put_failure(self): + with testtools.ExpectedException(Exception, 'Update failed: Error'): + self.put_mock.return_value = utils.FAKE_FAILURE + testcase_put = testcases.TestcasePut(self.app, mock.Mock()) + args = ['--project-name', 'functest', 'def', self.testcase_string] + verifies = [ + ('project_name', 'functest'), + ('name', 'def'), + ('testcase', self.testcase_json)] + parsed_args = self.check_parser(testcase_put, args, verifies) + testcase_put.take_action(parsed_args) diff --git a/testapi/testapi-client/testapiclient/tests/unit/utils.py b/testapi/testapi-client/testapiclient/tests/unit/utils.py new file mode 100644 index 0000000..c59aadd --- /dev/null +++ b/testapi/testapi-client/testapiclient/tests/unit/utils.py @@ -0,0 +1,58 @@ +import httplib + +from mock import mock +from six.moves.urllib import parse +import testtools + +from testapiclient.tests.unit import fakes +from testapiclient.utils import clientmanager + +FAKE_FAILURE = fakes.FakeResponse(status_code=httplib.FORBIDDEN, data='Error') + + +class ParserException(Exception): + pass + + +class TestCommand(testtools.TestCase): + api_url = 'http://localhost:8000/api/v1/' + + def setUp(self): + super(TestCommand, self).setUp() + self.env_variables = { + 'testapi_url': 'http://localhost:8000/api/v1', + 'testapi_cas_auth_url': + ( + 'https://identity.linuxfoundation.org/user' + + '/login?destination=cas/login%3Fservice%3D' + ), + 'testapi_cas_signin_return': '/auth/signin_return' + } + self.config_mock = mock.patch.dict( + 'os.environ', self.env_variables).start() + self.fake_stdout = fakes.FakeStdout() + self.fake_log = fakes.FakeLog() + self.app = fakes.FakeApp(self.fake_stdout, self.fake_log) + self.app.client_manager = clientmanager.ClientManager() + self.get_mock = mock.patch('requests.Session.get').start() + self.post_mock = mock.patch('requests.Session.post').start() + self.delete_mock = mock.patch('requests.Session.delete').start() + self.put_mock = mock.patch('requests.Session.put').start() + + def check_parser(self, cmd, args, verify_args): + cmd_parser = cmd.get_parser('check_parser') + try: + parsed_args = cmd_parser.parse_args(args) + except SystemExit: + raise ParserException("Argument parse failed") + for av in verify_args: + attr, value = av + if attr: + self.assertIn(attr, parsed_args) + self.assertEqual(value, getattr(parsed_args, attr)) + return parsed_args + + def assert_url(self, actual_url, expected_url): + actual_parsed = parse.parse_qs(parse.urlparse(actual_url).query) + expected_parsed = parse.parse_qs(parse.urlparse(expected_url).query) + assert actual_parsed == expected_parsed diff --git a/testapi/testapi-client/testapiclient/utils/__init__.py b/testapi/testapi-client/testapiclient/utils/__init__.py new file mode 100644 index 0000000..ebb891f --- /dev/null +++ b/testapi/testapi-client/testapiclient/utils/__init__.py @@ -0,0 +1,8 @@ +def get_item_properties(item, fields): + """Return a tuple containing the item properties. + + :param item: a single item resource (e.g. Server, Project, etc) + :param fields: tuple of strings with the desired field names + """ + + return tuple([item.get(field, '') for field in fields]) diff --git a/testapi/testapi-client/testapiclient/utils/clientmanager.py b/testapi/testapi-client/testapiclient/utils/clientmanager.py new file mode 100644 index 0000000..cbfd723 --- /dev/null +++ b/testapi/testapi-client/testapiclient/utils/clientmanager.py @@ -0,0 +1,87 @@ +import httplib +import json +import os +import urllib +import logging + +import requests + +LOG = logging.getLogger(__name__) + + +class ClientManager(object): + headers = { + 'Content-type': 'application/json', + 'Accept': 'text/plain'} + + def __init__(self, cli_options=None): + self.cli_options = cli_options + self.session = requests.Session() + self._auth_completed = False + + @property + def auth_required(self): + return self._auth() + + def _auth(self): + return { + 'name': self.cli_options.u, + 'pass': self.cli_options.p + } if self.cli_options.u else None + + def auth(self): + + if self._auth_completed: + return + + hostname = '{}{}{}'.format(os.environ.get('testapi_cas_auth_url'), + urllib.quote(os.environ.get('testapi_url')), + os.environ.get('testapi_cas_signin_return')) + data = self._auth() + data.update({'form_id': 'user_login'}) + LOG.debug('authenticating.....') + response = self.session.post(hostname, data) + if "login" in response.text: + raise Exception('Authenticate failed') + self._auth_completed = True + + def get(self, url): + return self._parse_response('Get', + self._request('get', url, + 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), + headers=self.headers)) + + def put(self, url, data): + return self._parse_response('Update', + self._request('put', url, + data=json.dumps(data), + headers=self.headers)) + + def delete(self, url, *args): + data = json.dumps(args[0]) if len(args) > 0 else None + return self._parse_response('Delete', + self._request('delete', url, + data=data, + headers=self.headers)) + + def _request(self, method, *args, **kwargs): + return getattr(self.session, method)(*args, **kwargs) + + def _raise_failure(self, op, response): + raise Exception('{} failed: {}'.format(op, response.reason)) + + def _parse_response(self, op, response): + if response.status_code == httplib.OK: + if op != 'Delete' and response.text != '': + return response.json() + else: + return None + else: + self._raise_failure(op, response) diff --git a/testapi/testapi-client/testapiclient/utils/command.py b/testapi/testapi-client/testapiclient/utils/command.py new file mode 100644 index 0000000..b9d1ce8 --- /dev/null +++ b/testapi/testapi-client/testapiclient/utils/command.py @@ -0,0 +1,38 @@ +import abc +import logging + +from cliff import command +from cliff import lister +from cliff import show +import six + +from testapiclient import utils + + +class CommandMeta(abc.ABCMeta): + + def __new__(mcs, name, bases, cls_dict): + if 'log' not in cls_dict: + cls_dict['log'] = logging.getLogger( + cls_dict['__module__'] + '.' + name) + return super(CommandMeta, mcs).__new__(mcs, name, bases, cls_dict) + + +@six.add_metaclass(CommandMeta) +class Command(command.Command): + def run(self, parsed_args): + self.log.debug('run(%s)', parsed_args) + return super(Command, self).run(parsed_args) + + +class Lister(Command, lister.Lister): + @staticmethod + def format_output(columns, data): + return (columns, + (utils.get_item_properties(s, columns) for s in data)) + + +class ShowOne(Command, show.ShowOne): + @staticmethod + def format_output(body): + return zip(*sorted(six.iteritems(body))) diff --git a/testapi/testapi-client/testapiclient/utils/urlparse.py b/testapi/testapi-client/testapiclient/utils/urlparse.py new file mode 100644 index 0000000..47d40d5 --- /dev/null +++ b/testapi/testapi-client/testapiclient/utils/urlparse.py @@ -0,0 +1,40 @@ +import os + +from six.moves.urllib import parse + + +def path_join(base, *urls): + def _path_join(base, url): + if not base.endswith('/'): + base += '/' + return parse.urljoin(base, url) + + urls = (base,) + urls + return reduce(_path_join, urls) + + +def query_join(base, **queries): + return base + '?' + parse.urlencode(queries) + + +def resource_join(*url): + testapi_url = os.environ.get('testapi_url') + return path_join(testapi_url, *url) + + +def get_queries(queries, parsed_args): + if not isinstance(queries, list): + queries = [queries] + + return {query: getattr(parsed_args, query) + for query in queries + if hasattr(parsed_args, query) and getattr(parsed_args, query)} + + +def query_by(base, queries, parsed_args): + qs = get_queries(queries, parsed_args) + return query_join(base, **qs) if qs else base + + +def url_format(base, parsed_args): + return base.format(**(parsed_args.__dict__)) diff --git a/testapi/testapi-client/tox.ini b/testapi/testapi-client/tox.ini new file mode 100644 index 0000000..51c3d57 --- /dev/null +++ b/testapi/testapi-client/tox.ini @@ -0,0 +1,34 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = pep8,py27 +skipsdist = True +sitepackages = True + +[testenv] +usedevelop = True +install_command = pip install -U {opts} {packages} +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +setenv= + HOME = {envtmpdir} + PYTHONPATH = {toxinidir} +commands = stestr run {posargs} +whitelist_externals = stestr + +[testenv:pep8] +deps = flake8 +commands = flake8 {toxinidir} + +[flake8] +# H803 skipped on purpose per list discussion. +# E123, E125 skipped as they are invalid PEP-8. + +show-source = True +ignore = E123,E125,H803 +builtins = _ +exclude = bin, build, dist, lib, local, .git, .eggs, .tox, .venv, venv, testapi_client.egg-info
\ No newline at end of file diff --git a/testapi/tox.ini b/testapi/tox.ini index 13b197c..0478e23 100644 --- a/testapi/tox.ini +++ b/testapi/tox.ini @@ -14,6 +14,8 @@ install_command = pip install -U {opts} {packages} deps = -rrequirements.txt -rtest-requirements.txt + -chttps://raw.githubusercontent.com/openstack/requirements/stable/ussuri/upper-constraints.txt + -cupper-constraints.txt commands= py.test \ --basetemp={envtmpdir} \ @@ -30,18 +32,21 @@ basepython=python2.7 commands = sphinx-build -W -b html docs/ docs/_build [testenv:pep8] +basepython=python2.7 deps = flake8 commands = flake8 {toxinidir} [flake8] +basepython=python2.7 # H803 skipped on purpose per list discussion. # E123, E125 skipped as they are invalid PEP-8. show-source = True -ignore = E123,E125,H803,E501 +ignore = E123,E125,H803,W503,W504 builtins = _ exclude = build,dist,doc,legacy,.eggs,.git,.tox,.venv,testapi_venv,venv [pytest] +basepython=python2.7 testpaths = opnfv_testapi/tests python_functions = test_* diff --git a/testapi/upper-constraints.txt b/testapi/upper-constraints.txt new file mode 100644 index 0000000..5226f48 --- /dev/null +++ b/testapi/upper-constraints.txt @@ -0,0 +1,9 @@ +epydoc===3.0.1 +motor===1.2.2 +python-cas===1.2.0 +argparse===1.2.1 +backports-abc===0.5 +backports.ssl-match-hostname===3.5.0.1 +html5lib===0.999 +singledispatch===3.4.0.3 +wsgiref===0.1.2 |