├── .gitignore ├── .travis.yml ├── Procfile ├── README.md ├── cucumber-formatter ├── pom.xml └── src │ ├── main │ ├── java │ │ └── at │ │ │ └── porscheinformatik │ │ │ └── cucumber │ │ │ └── formatter │ │ │ ├── AbstractJsonFormatter.java │ │ │ ├── HtmlFormatter.java │ │ │ ├── MimeTypeToExtensionsUtil.java │ │ │ └── MongoDbFormatter.java │ └── resources │ │ ├── css │ │ ├── bootstrap-spacelab.css │ │ ├── colorbox.css │ │ └── style.css │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ │ ├── img │ │ ├── border1.png │ │ ├── border2.png │ │ ├── controls.png │ │ ├── load.gif │ │ └── loading.gif │ │ ├── index.html │ │ ├── js │ │ ├── angular-route.min.js │ │ ├── angular.localStorageModule.js │ │ ├── angular.min.js │ │ ├── app.js │ │ ├── bootstrap.min.js │ │ ├── charts.js │ │ ├── config.js │ │ ├── dateAndTime.js │ │ ├── jquery.colorbox-min.js │ │ ├── jquery.min.js │ │ ├── json3.min.js │ │ ├── lightbox.js │ │ ├── ui-bootstrap-tpls-0.5.0.min.js │ │ └── utils.js │ │ └── pages │ │ ├── feature.html │ │ └── features.html │ └── test │ ├── java │ └── at │ │ └── porscheinformatik │ │ └── cucumber │ │ └── formatter │ │ ├── HtmlFormatIT.java │ │ ├── MimeTypeToExtensionsUtilTest.java │ │ ├── MongoFormatIT.java │ │ ├── RpnCalculator.java │ │ └── RpnCalculatorStepdefs.java │ └── resources │ ├── at │ └── porscheinformatik │ │ └── cucumber │ │ └── formatter │ │ ├── basic_arithmetic.feature │ │ ├── basic_arithmetic_one.feature │ │ ├── basic_arithmetic_two.feature │ │ ├── exception.feature │ │ └── exception_in_before.feature │ ├── sampleVideo.mp4 │ └── sampleVideo.zip ├── cucumber-mongodb-rest ├── pom.xml └── src │ ├── main │ ├── java │ │ └── at │ │ │ └── porscheinformatik │ │ │ └── cucumber │ │ │ └── mongodb │ │ │ └── rest │ │ │ ├── CollectionAccessChecker.java │ │ │ ├── DatabaseConfig.java │ │ │ ├── controller │ │ │ ├── CollectionController.java │ │ │ ├── CucumberPluginController.java │ │ │ ├── FileController.java │ │ │ ├── NameObject.java │ │ │ ├── QueryController.java │ │ │ ├── ReportController.java │ │ │ ├── RightController.java │ │ │ ├── RightsObject.java │ │ │ ├── RoleController.java │ │ │ ├── Roles.java │ │ │ ├── StatisticsController.java │ │ │ ├── ValueObject.java │ │ │ └── dto │ │ │ │ ├── DateDTO.java │ │ │ │ ├── EmbeddingDTO.java │ │ │ │ ├── FeatureDTO.java │ │ │ │ ├── ReportDTO.java │ │ │ │ ├── ResultDTO.java │ │ │ │ ├── ScenarioDTO.java │ │ │ │ ├── StepDTO.java │ │ │ │ ├── StepResultDTO.java │ │ │ │ └── TagDTO.java │ │ │ └── db │ │ │ └── MongoDB.java │ └── resources │ │ ├── mapMostExecutedSteps.js │ │ ├── mapMostFailedSteps.js │ │ ├── mapStepsDurations.js │ │ ├── reduceCumulatedStepDurations.js │ │ └── reduceHighestSingleStepDurations.js │ └── test │ └── java │ └── at │ └── porscheinformatik │ └── cucumber │ └── mongodb │ └── rest │ └── controller │ └── QueryControllerTest.java ├── cucumber-report-web ├── gulpfile.js ├── package.json ├── pom.xml ├── src │ └── main │ │ ├── java │ │ └── at │ │ │ └── porscheinformatik │ │ │ └── cucumber │ │ │ ├── CucumberReportApplication.java │ │ │ └── web │ │ │ ├── UserController.java │ │ │ └── config │ │ │ ├── CucumberReportProperties.java │ │ │ ├── WebMvcConfig.java │ │ │ └── WebSecurityConfig.java │ │ ├── resources │ │ ├── logback.xml │ │ └── static │ │ │ ├── components │ │ │ └── header.html │ │ │ ├── css │ │ │ └── style.css │ │ │ ├── index.html │ │ │ └── pages │ │ │ ├── assignments.html │ │ │ ├── embedded_lightbox.html │ │ │ ├── features.html │ │ │ ├── help.html │ │ │ ├── products.html │ │ │ ├── rankings.html │ │ │ ├── reports.html │ │ │ ├── scenarios.html │ │ │ └── statistics.html │ │ └── typescript │ │ ├── ccrdb │ │ ├── app.module.ts │ │ ├── components │ │ │ ├── components.module.ts │ │ │ └── menu.controller.ts │ │ ├── core │ │ │ ├── authentication.service.ts │ │ │ ├── back-button.service.ts │ │ │ ├── core.module.ts │ │ │ ├── core.router.config.ts │ │ │ ├── deletion-mode.service.ts │ │ │ ├── index.controller.ts │ │ │ ├── loading-bar.service.ts │ │ │ └── search-bar.service.ts │ │ ├── directives │ │ │ ├── confirm.directive.ts │ │ │ └── directives.module.ts │ │ ├── pages │ │ │ ├── assignments.controller.ts │ │ │ ├── features.controller.ts │ │ │ ├── features.service.ts │ │ │ ├── groupModalInstance.controller.ts │ │ │ ├── groupModalInstance.service.ts │ │ │ ├── help.controller.ts │ │ │ ├── pages.module.ts │ │ │ ├── products.controller.ts │ │ │ ├── products.service.ts │ │ │ ├── rankings.controller.ts │ │ │ ├── rankings.service.ts │ │ │ ├── reports.controller.ts │ │ │ ├── reports.service.ts │ │ │ ├── scenarios.controller.ts │ │ │ ├── scenarios.service.ts │ │ │ ├── statistics.controller.ts │ │ │ └── statistics.service.ts │ │ └── util │ │ │ └── Time.ts │ │ └── import.d.ts ├── tsconfig.json ├── tsd.json ├── tslint.json └── typings │ ├── angular-ui-bootstrap │ └── angular-ui-bootstrap.d.ts │ ├── angular-ui-router │ └── angular-ui-router.d.ts │ ├── angularjs │ └── angular.d.ts │ ├── jquery │ └── jquery.d.ts │ └── tsd.d.ts ├── docs ├── Charts.PNG ├── Feature-based Reports.PNG ├── Selected Step.PNG ├── Top 5 and Complete List.PNG ├── Version-based Reports.PNG └── different execution dates of a version.PNG ├── pom.xml ├── silk-bdd-result-plugin ├── createJarWithDependencies.sh ├── pom.xml └── src │ └── main │ ├── java │ └── at │ │ └── porscheinformatik │ │ └── common │ │ └── utils │ │ └── silkplugin │ │ ├── SilkSynchronizer.java │ │ ├── dto │ │ ├── DateDTO.java │ │ ├── EmbeddingDTO.java │ │ ├── FeatureDTO.java │ │ ├── ReportDTO.java │ │ ├── ResultDTO.java │ │ ├── ScenarioDTO.java │ │ ├── StepDTO.java │ │ ├── StepResultDTO.java │ │ └── TagDTO.java │ │ └── gui │ │ └── ConfigurationGUI.java │ └── resources │ ├── config.properties │ └── poi.jpg └── system.properties /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .classpath 3 | .project 4 | .settings 5 | node_modules 6 | .factorypath 7 | /bin 8 | *.iml 9 | *.idea 10 | *.log 11 | cucumber-report-web/src/main/resources/application.properties -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: false 3 | deploy: 4 | provider: releases 5 | api_key: 6 | secure: Woy2dR+fCDg9/LEoJTl6LmKHSwPINfgyNhUrhDF8C4+3iSNZjOBSRtqAVq4P27TYE9XSwTkfKH8l7L/wyLcc0Nt2oAQity9hkUhOGicGyOb+7ObB6Hzo67B0AIDLr8DdNDLu0/MixfXzAWytmMG3Cxj1jY+oNgDpvvrLKZln+1s= 7 | file_glob: true 8 | file: cucumber-report-web/target/cucumber-report-web-*.war 9 | skip_cleanup: true 10 | on: 11 | tags: true 12 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java -Dcucumber.report.db.mongo.uri=$MONGO_URI -Dcucumber.report.db.mongo.database=$MONGO_DB -Dcucumber.report.db.mongo.username=$MONGO_USER -Dcucumber.report.db.mongo.password=$MONGO_PWD -Dserver.port=$PORT $JAVA_OPTS -jar cucumber-report-web/target/cucumber-report-web*-bootable.war -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cucumber Report DB 2 | =================== 3 | 4 | [![Build Status](https://travis-ci.org/porscheinformatik/cucumber-report-db.svg?branch=master)](https://travis-ci.org/porscheinformatik/cucumber-report-db) 5 | 6 | Stores results of BDD tests with Cucumber-JVM in a database and provides reporting capabilities. It can display graphs of different test runs 7 | as well as compute statistics of most failed steps or highest runtime grouped by steps 8 | 9 | The project includes the submodule 'silk-bdd-result-plugin' which is a plugin for fetching the test results from the database into a Silk Central test management system. 10 | 11 | Want to learn more? [See the wiki.](https://github.com/porscheinformatik/cucumber-report-db/wiki) or take a look at a [demo with sample data](https://cucumber-report-db.herokuapp.com) 12 | 13 | ## Setup 14 | 15 | The Cucumber-Report-DB has two main components. The [cucumber-report-web](cucumber-report-web) and the [cucumber-formatter](cucumber-formatter). In order to store and display the results of cucumber tests the web application 16 | has to be set up correctly and the formatter has to be included in the cucumber test run. 17 | 18 | ### Web application (cucumber-report-web) 19 | 20 | Requirements: 21 | * Java 6 or later 22 | * MongoDB 2.6.3 or later [Download](https://www.mongodb.org/downloads) or [as-a-service](https://mongolab.com/) 23 | 24 | Setup (latest release version): 25 | * Start the MongoDB ```mongod --dbpath /path/to/db``` 26 | * Download the ```*-bootable.war``` from the [latest release](https://github.com/porscheinformatik/cucumber-report-db/releases/latest) 27 | * execute ```java -jar cucumber-report-web*-bootable.war``` 28 | 29 | Setup (from source): 30 | * Install Maven 3 31 | * Clone the Repo 32 | * Start the MongoDB ```mongod --dbpath /path/to/db``` 33 | * execute ```mvn clean install``` 34 | * execute ```java -jar cucumber-report-web/target/cucumber-report-web*-bootable.war``` 35 | 36 | Per default the web application connects to a mongodb hosted on ```mongodb://localhost:27017/``` without authentication (the mongodb default) 37 | 38 | By setting the following system properties the connection to the mongodb can be configured 39 | * cucumber.report.db.mongo.uri (uri to mongodb e.g. ```mongodb://user:passwd@xxx.mongolab.com:55980/cucumberreportdb```) 40 | * cucumber.report.db.mongo.username 41 | * cucumber.report.db.mongo.password 42 | * cucumber.report.db.mongo.database 43 | 44 | Security (Http basic auth): 45 | By setting the system property ```cucumber.report.db.secured``` a http basic auth for the web application can be activated. 46 | You have to specify the location of a property file by setting the system property cucumber.report.db.config. The config file must have the format [cucumber-report-web.properties)[cucumber-report-web\src\main\resources\cucumber-report-web.properties] 47 | 48 | ### Formatter (MongoDbFormatter) 49 | In order to publish the results of cucumber test runs to the web application, include the ```at.porscheinformatik.cucumber.formatter.MongoDbFormatter``` in your cucumber-run (see ```at.porscheinformatik.cucumber.formatter.MongoFormatIT``` for an example) 50 | 51 | Per default the formatter expects the web application to be hosted on ```http://localhost:8081``` and the name of the subject under test is "product_version" 52 | By setting the following system property the location of the webapp can be defined 53 | * cucumber.report.server.baseUrl 54 | 55 | By setting the following system properties the name and version of the subject under test can be specified 56 | * cucumber.report.product.name 57 | * cucumber.report.product.version 58 | 59 | When the webapp has enabled basic auth the user and password for the formatter have to be specified via the following system properties 60 | * cucumber.report.server.username 61 | * cucumber.report.server.password 62 | 63 | ## License 64 | 65 | This software is licensed under the Apache Software License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0.txt -------------------------------------------------------------------------------- /cucumber-formatter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | Porsche Informatik Cucumber Formatter 5 | cucumber-formatter 6 | jar 7 | 8 | 9 | at.porscheinformatik.cucumber-report-db 10 | cucumber-report-db-parent 11 | 1.2.0.BUILD-SNAPSHOT 12 | .. 13 | 14 | 15 | 16 | 17 | info.cukes 18 | cucumber-core 19 | 20 | 21 | com.sun.jersey 22 | jersey-client 23 | 24 | 25 | org.apache.commons 26 | commons-lang3 27 | 28 | 29 | org.slf4j 30 | slf4j-api 31 | 32 | 33 | 34 | info.cukes 35 | cucumber-java 36 | 37 | 38 | info.cukes 39 | cucumber-junit 40 | 41 | 42 | junit 43 | junit 44 | 45 | 46 | commons-io 47 | commons-io 48 | test 49 | 50 | 51 | joda-time 52 | joda-time 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /cucumber-formatter/src/main/java/at/porscheinformatik/cucumber/formatter/MimeTypeToExtensionsUtil.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.formatter; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class MimeTypeToExtensionsUtil 7 | { 8 | protected static final Map MIME_TYPES_EXTENSIONS = new HashMap() 9 | { 10 | { 11 | put("image/jpeg", "jpg"); 12 | put("text/plain", "log"); 13 | } 14 | }; 15 | 16 | public static String getExtension(final String mimeType) 17 | { 18 | if (MIME_TYPES_EXTENSIONS.containsKey(mimeType)) 19 | { 20 | return MIME_TYPES_EXTENSIONS.get(mimeType); 21 | } 22 | return mimeType.substring(mimeType.indexOf("/") + 1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/css/colorbox.css: -------------------------------------------------------------------------------- 1 | /* 2 | Colorbox Core Style: 3 | The following CSS is consistent between example themes and should not be altered. 4 | */ 5 | #colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;} 6 | #cboxOverlay{position:fixed; width:100%; height:100%;} 7 | #cboxMiddleLeft, #cboxBottomLeft{clear:left;} 8 | #cboxContent{position:relative;} 9 | #cboxLoadedContent{overflow:hidden !important; -webkit-overflow-scrolling: touch;} 10 | #cboxTitle{margin:0;} 11 | #cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%; height:100%;} 12 | #cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;} 13 | .cboxPhoto{float:left; margin:auto; border:0; display:block; max-width:none; -ms-interpolation-mode:bicubic;} 14 | .cboxIframe{width:100%; height:100%; display:block; border:0;} 15 | #colorbox, #cboxContent, #cboxLoadedContent{box-sizing:content-box; -moz-box-sizing:content-box; -webkit-box-sizing:content-box;} 16 | 17 | /* 18 | User Style: 19 | Change the following styles to modify the appearance of Colorbox. They are 20 | ordered & tabbed in a way that represents the nesting of the generated HTML. 21 | */ 22 | #cboxOverlay{background:#bcd;} 23 | #colorbox{outline:0;} 24 | #cboxTopLeft{width:25px; height:25px; background:url(../img/border1.png) no-repeat 0 0;} 25 | #cboxTopCenter{height:25px; background:url(../img/border1.png) repeat-x 0 -50px;} 26 | #cboxTopRight{width:25px; height:25px; background:url(../img/border1.png) no-repeat -25px 0;} 27 | #cboxBottomLeft{width:25px; height:25px; background:url(../img/border1.png) no-repeat 0 -25px;} 28 | #cboxBottomCenter{height:25px; background:url(../img/border1.png) repeat-x 0 -75px;} 29 | #cboxBottomRight{width:25px; height:25px; background:url(../img/border1.png) no-repeat -25px -25px;} 30 | #cboxMiddleLeft{width:25px; background:url(../img/border2.png) repeat-y 0 0;} 31 | #cboxMiddleRight{width:25px; background:url(../img/border2.png) repeat-y -25px 0;} 32 | 33 | #cboxContent{background:#fff;}/* margin-top:20px; */ 34 | .cboxIframe{background:#fff;} 35 | #cboxError{padding:50px; border:1px solid #999;} 36 | #cboxLoadedContent{border:5px solid #000; background:#fff; border-radius: 10px;} 37 | #cboxTitle{position:absolute; top:-20px; left:0; color:#999;} 38 | #cboxCurrent{position:absolute; top:-20px; right:0px; color:#999;} 39 | #cboxLoadingGraphic{background:url(../img/loading.gif) no-repeat center center;} 40 | 41 | /* these elements are buttons, and may need to have additional styles reset to avoid unwanted base styles */ 42 | #cboxPrevious, #cboxNext, #cboxSlideshow, #cboxClose {border:0; padding:0; margin:0; overflow:visible; width:auto; background:none; } 43 | 44 | /* avoid outlines on :active (mouseclick), but preserve outlines on :focus (tabbed navigating) */ 45 | #cboxPrevious:active, #cboxNext:active, #cboxSlideshow:active, #cboxClose:active {outline:0;} 46 | 47 | #cboxSlideshow{position:absolute; top:-20px; right:90px; color:#fff;} 48 | #cboxPrevious{position:absolute; top:50%; left:5px; margin-top:-32px; background:url(../img/controls.png) no-repeat top left; width:28px; height:65px; text-indent:-9999px;} 49 | #cboxPrevious:hover{background-position:bottom left;} 50 | #cboxNext{position:absolute; top:50%; right:5px; margin-top:-32px; background:url(../img/controls.png) no-repeat top right; width:28px; height:65px; text-indent:-9999px;} 51 | #cboxNext:hover{background-position:bottom right;} 52 | #cboxClose{position:absolute; top:5px; right:5px; display:block; background:url(../img/controls.png) no-repeat top center; width:38px; height:19px; text-indent:-9999px;} 53 | #cboxClose:hover{background-position:bottom center;} 54 | -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/css/style.css: -------------------------------------------------------------------------------- 1 | .btn:focus { 2 | outline: 0; 3 | } 4 | .affix { 5 | width: 200px; 6 | } 7 | body{ 8 | padding-top: 70px; 9 | } 10 | .table td , .table th { 11 | text-align: center !important; 12 | } 13 | 14 | .table .firstTD{ 15 | text-align: left !important; 16 | } 17 | .table .noWrap{ 18 | white-space: nowrap; 19 | } 20 | 21 | .table thead > tr > th, .table tbody > tr > th, .table tfoot > tr > th, .table thead > tr > td, .table tbody > tr > td, .table tfoot > tr > td { 22 | padding: 6px; 23 | } 24 | .badge, .label { 25 | cursor: default; 26 | } 27 | .container { 28 | width: 95% !important; 29 | } 30 | .uri{ 31 | display: inline-block; 32 | padding: 0px 6px; 33 | margin: 0px; 34 | /*word-wrap: normal;*/ 35 | word-break: normal; 36 | white-space: normal; 37 | color: #d14; 38 | } 39 | .errorLogContent{ 40 | margin: 10px; 41 | } 42 | 43 | .errorLogContent .btn{ 44 | position: absolute; 45 | } 46 | 47 | .errorLogContent dl{ 48 | word-wrap: break-word; 49 | } 50 | 51 | .errorLogCode{ 52 | padding-top: 22px; 53 | word-break: normal; 54 | word-wrap: normal; 55 | white-space: pre; 56 | overflow: auto; 57 | max-height: 250px; 58 | } 59 | 60 | .lightbox{ 61 | max-width: 100%; 62 | max-height: 100%; 63 | } 64 | .panelHead{ 65 | color: #ffffff; 66 | background-color: #446e9b; 67 | border-color: #446e9b; 68 | padding: 10px 15px; 69 | border-bottom: 1px solid transparent; 70 | border-top-right-radius: 3px; 71 | border-top-left-radius: 3px; 72 | } 73 | .panelTitle{ 74 | display: inline; 75 | color: #fff; 76 | margin-top: 0; 77 | margin-bottom: 0; 78 | font-size: 16px; 79 | } 80 | /* 81 | .panelBody{ 82 | position: absolute; 83 | top: 41px; 84 | max-width: 100%; 85 | max-height: 100%; 86 | padding: 15px; 87 | } 88 | */ 89 | .panelBody img{ 90 | max-width:100%; 91 | cursor: pointer; 92 | cursor: -webkit-zoom-in; 93 | cursor: -moz-zoom-in; 94 | } 95 | 96 | .sortToggle span{ 97 | cursor: pointer; 98 | } 99 | 100 | .tdCollapse { 101 | max-height: 20px; 102 | overflow: hidden; 103 | cursor: pointer; 104 | /* 105 | -webkit-transition: max-height 1s; 106 | -moz-transition: max-height 1s; 107 | -o-transition: max-height 1s; 108 | transition: max-height 1s; 109 | */ 110 | } 111 | 112 | .tdUncollapse { 113 | max-height: none; /*2000px;*/ 114 | } -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/img/border1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/img/border1.png -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/img/border2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/img/border2.png -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/img/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/img/controls.png -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/img/load.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/img/load.gif -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/main/resources/img/loading.gif -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cucumber BDD Report 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 47 | 48 |
49 | 53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/js/angular-route.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.2.0rc1 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(p,b,E){'use strict';function u(b,d){return l(new (l(function(){},{prototype:b})),d)}var v=b.copy,A=b.equals,l=b.extend,t=b.forEach,n=b.isDefined,w=b.isFunction,x=b.isString,B=b.element;p=b.module("ngRoute",["ng"]).provider("$route",function(){function b(c,q){var d=q.caseInsensitiveMatch,m={originalPath:c,regexp:c},l=m.keys=[];c=c.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(c,b,q,d){c="?"===d?d:null;d="*"===d?d:null;l.push({name:q,optional:!!c});b=b||"";return""+ 7 | (c?"":b)+"(?:"+(c?b:"")+(d&&"(.+)?"||"([^/]+)?")+")"+(c||"")}).replace(/([\/$\*])/g,"\\$1");m.regexp=RegExp("^"+c+"$",d?"i":"");return m}var d={};this.when=function(c,q){d[c]=l({reloadOnSearch:!0},q,c&&b(c,q));if(c){var h="/"==c[c.length-1]?c.substr(0,c.length-1):c+"/";d[h]=l({redirectTo:c},b(h,q))}return this};this.otherwise=function(c){this.when(null,c);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(c,b,h,m,y,p,C,D){function r(){var a= 8 | s(),e=k.current;if(a&&e&&a.$$route===e.$$route&&A(a.pathParams,e.pathParams)&&!a.reloadOnSearch&&!f)e.params=a.params,v(e.params,h),c.$broadcast("$routeUpdate",e);else if(a||e)f=!1,c.$broadcast("$routeChangeStart",a,e),(k.current=a)&&a.redirectTo&&(x(a.redirectTo)?b.path(g(a.redirectTo,a.params)).search(a.params).replace():b.url(a.redirectTo(a.pathParams,b.path(),b.search())).replace()),m.when(a).then(function(){if(a){var c=l({},a.resolve),b,e;t(c,function(a,b){c[b]=x(a)?y.get(a):y.invoke(a)});n(b= 9 | a.template)?w(b)&&(b=b(a.params)):n(e=a.templateUrl)&&(w(e)&&(e=e(a.params)),e=D.getTrustedResourceUrl(e),n(e)&&(a.loadedTemplateUrl=e,b=p.get(e,{cache:C}).then(function(a){return a.data})));n(b)&&(c.$template=b);return m.all(c)}}).then(function(b){a==k.current&&(a&&(a.locals=b,v(a.params,h)),c.$broadcast("$routeChangeSuccess",a,e))},function(b){a==k.current&&c.$broadcast("$routeChangeError",a,e,b)})}function s(){var a,c;t(d,function(d,k){var f;if(f=!c){var g=b.path();f=d.keys;var m={};if(d.regexp)if(g= 10 | d.regexp.exec(g)){for(var h=1,p=g.length;h/g, '>').replace(/"/g, '"').replace(/'/g, '''); 3 | } -------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/pages/feature.html: -------------------------------------------------------------------------------- 1 | 6 |

Feature: {{ feature.name }}

7 |
8 | {{ tag.name }} 9 |

10 |
11 | 15 |
{{feature.uri}}

16 | 17 |
Scenarios:
18 |
19 |
20 | {{scenarioIdx = $index;""}} 21 |
Scenario: {{ scenario.name }}
22 |
23 | {{ tag.name }} 24 |

25 |
26 |
Description:
27 |

{{ scenario.description }}

28 |

{{ scenario.background.keyword }} {{ scenario.background.name }}

29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {{stepIdx = $index;""}} 41 | 44 | 48 | 49 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
StepStatusDurationAttachments
42 | {{ step.keyword }} {{ step.name }} 43 | 45 | {{ step.result.status }} 46 | 47 | {{ step.result.duration ? duration(step.result.duration) : '' }} 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 | 77 |
78 |
79 |
Sum{{ scenario.status }}{{ scenario.result.duration ? duration(scenario.result.duration) : '' }}
87 |
88 |
-------------------------------------------------------------------------------- /cucumber-formatter/src/main/resources/pages/features.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
FeatureTagsStatusDurationScenariosSteps
PassedUnknownFailedTotalPassedUnknownFailedSkippedTotal
31 | {{ feature.name }} 32 | {{ tag.name }} 35 | FAILED 36 | OK 37 | {{ duration(feature) }}{{ feature.result.passedScenarioCount || "" }}{{ feature.result.unknownScenarioCount || "" }}{{ feature.result.failedScenarioCount || "" }}{{ feature.result.scenarioCount || "" }}{{ feature.result.passedStepCount || "" }}{{ feature.result.unknownStepCount || "" }}{{ feature.result.failedStepCount || "" }}{{ feature.result.skippedStepCount || "" }}{{ feature.result.stepCount || "" }}
{{ filteredFeatures.length ? 'Sum' : 'No entries found!'}}{{ filteredFeatures.length ? duration(sum( filteredFeatures, 'duration')) : '' }}{{ sum( filteredFeatures, 'passedScenarioCount') }}{{ sum( filteredFeatures, 'unknownScenarioCount') }}{{ sum( filteredFeatures, 'failedScenarioCount') }}{{ sum( filteredFeatures, 'scenarioCount') }}{{ sum( filteredFeatures, 'passedStepCount') }}{{ sum( filteredFeatures, 'unknownStepCount') }}{{ sum( filteredFeatures, 'failedStepCount') }}{{ sum( filteredFeatures, 'skippedStepCount') }}{{ sum( filteredFeatures, 'stepCount') }}
-------------------------------------------------------------------------------- /cucumber-formatter/src/test/java/at/porscheinformatik/cucumber/formatter/HtmlFormatIT.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.formatter; 2 | 3 | import cucumber.api.CucumberOptions; 4 | import cucumber.api.junit.Cucumber; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.runner.RunWith; 8 | 9 | @CucumberOptions(features = "classpath:at/porscheinformatik/cucumber/formatter/basic_arithmetic.feature", 10 | format = "at.porscheinformatik.cucumber.formatter.HtmlFormatter:target/html") 11 | @RunWith(Cucumber.class) 12 | @Ignore 13 | public class HtmlFormatIT 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /cucumber-formatter/src/test/java/at/porscheinformatik/cucumber/formatter/MimeTypeToExtensionsUtilTest.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.formatter; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class MimeTypeToExtensionsUtilTest 7 | { 8 | @Test 9 | public void mapApplicationZipToZip() 10 | { 11 | String extension = MimeTypeToExtensionsUtil.getExtension("application/zip"); 12 | Assert.assertEquals("zip", extension); 13 | } 14 | 15 | @Test 16 | public void mapImagePngToPng() 17 | { 18 | String extension = MimeTypeToExtensionsUtil.getExtension("image/png"); 19 | Assert.assertEquals("png", extension); 20 | } 21 | 22 | @Test 23 | public void mapImageJpegToJpg() 24 | { 25 | String extension = MimeTypeToExtensionsUtil.getExtension("image/jpeg"); 26 | Assert.assertEquals("jpg", extension); 27 | } 28 | 29 | @Test 30 | public void mapTextPlainToLog() 31 | { 32 | String extension = MimeTypeToExtensionsUtil.getExtension("text/plain"); 33 | Assert.assertEquals("log", extension); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /cucumber-formatter/src/test/java/at/porscheinformatik/cucumber/formatter/MongoFormatIT.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.formatter; 2 | 3 | import cucumber.api.CucumberOptions; 4 | import cucumber.api.junit.Cucumber; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.runner.RunWith; 8 | 9 | @CucumberOptions(features = "classpath:at/porscheinformatik/cucumber/formatter", 10 | format = "at.porscheinformatik.cucumber.formatter.MongoDbFormatter", 11 | glue = "at.porscheinformatik.cucumber.formatter", tags = "~@Skip") 12 | @RunWith(Cucumber.class) 13 | @Ignore 14 | public class MongoFormatIT 15 | { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /cucumber-formatter/src/test/java/at/porscheinformatik/cucumber/formatter/RpnCalculator.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.formatter; 2 | 3 | import static java.util.Arrays.asList; 4 | 5 | import java.util.Deque; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | public class RpnCalculator { 10 | private final Deque stack = new LinkedList(); 11 | private static final List OPS = asList("-", "+", "*", "/"); 12 | 13 | public void push(Object arg) { 14 | if (OPS.contains(arg)) { 15 | Number y = stack.removeLast(); 16 | Number x = stack.isEmpty() ? 0 : stack.removeLast(); 17 | Double val = null; 18 | if (arg.equals("-")) { 19 | val = x.doubleValue() - y.doubleValue(); 20 | } else if (arg.equals("+")) { 21 | val = x.doubleValue() + y.doubleValue(); 22 | } else if (arg.equals("*")) { 23 | val = x.doubleValue() * y.doubleValue(); 24 | } else if (arg.equals("/")) { 25 | val = x.doubleValue() / y.doubleValue(); 26 | } 27 | push(val); 28 | } else { 29 | stack.add((Number) arg); 30 | } 31 | } 32 | 33 | public void PI() { 34 | push(Math.PI); 35 | } 36 | 37 | public Number value() { 38 | return stack.getLast(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cucumber-formatter/src/test/java/at/porscheinformatik/cucumber/formatter/RpnCalculatorStepdefs.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.formatter; 2 | 3 | import cucumber.api.Scenario; 4 | import cucumber.api.java.After; 5 | import cucumber.api.java.Before; 6 | import cucumber.api.java.en.Given; 7 | import cucumber.api.java.en.Then; 8 | import cucumber.api.java.en.When; 9 | 10 | import java.io.FileInputStream; 11 | import java.io.IOException; 12 | import java.util.List; 13 | import java.util.Random; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | 17 | public class RpnCalculatorStepdefs 18 | { 19 | private RpnCalculator calc; 20 | 21 | @Given("^a calculator I just turned on$") 22 | public void a_calculator_I_just_turned_on() 23 | { 24 | calc = new RpnCalculator(); 25 | } 26 | 27 | @When("^I add (\\d+) and (\\d+)$") 28 | public void adding(int arg1, int arg2) 29 | { 30 | calc.push(arg1); 31 | calc.push(arg2); 32 | calc.push("+"); 33 | } 34 | 35 | @Given("^I press (.+)$") 36 | public void I_press(String what) 37 | { 38 | calc.push(what); 39 | } 40 | 41 | @Then("^the result is (\\d+)$") 42 | public void the_result_is(double expected) 43 | { 44 | assertEquals(expected, calc.value()); 45 | } 46 | 47 | @When("^I have an assertion with tags$") 48 | public void I_have_an_assertion_with_tags() { 49 | assertEquals("", ""); 50 | } 51 | 52 | 53 | @Before({"~@foo"}) 54 | public void before() 55 | { 56 | System.out.println("Runs before scenarios *not* tagged with @foo"); 57 | } 58 | 59 | @Before("@ExceptionOnBefore") 60 | public void beforeWithException() { 61 | throw new RuntimeException("Exception in @Before"); 62 | } 63 | 64 | @After 65 | public void after(Scenario scenario) 66 | { 67 | try 68 | { 69 | if ("passed" .equals(scenario.getStatus())) 70 | { 71 | int random = new Random().nextInt(4); 72 | if (random % 3 == 0) 73 | { 74 | embedFileFromClasspath(scenario, "/img/loading.gif", "image/bmp"); 75 | embedFileFromClasspath(scenario, "/img/controls.png", "image/png"); 76 | embedFileFromClasspath(scenario, "/sampleVideo.mp4", "video/mp4"); 77 | embedFileFromClasspath(scenario, "/sampleVideo.zip", "application/zip"); 78 | } else if(random % 3 == 1) { 79 | embedFileFromClasspath(scenario, "/sampleVideo.zip", "application/zip"); 80 | } else { 81 | embedFileFromClasspath(scenario, "/logFile.log", "text/plain"); 82 | } 83 | } 84 | else 85 | { 86 | embedFileFromClasspath(scenario, "/sampleVideo.mp4", "video/mp4"); 87 | } 88 | } 89 | catch (java.lang.Exception e) 90 | { 91 | e.printStackTrace(); 92 | } 93 | } 94 | 95 | private void embedFileFromClasspath(final Scenario scenario, String path, String mimeType) throws IOException 96 | { 97 | FileInputStream fis = new FileInputStream(this.getClass().getResource(path).getFile()); 98 | scenario.embed(org.apache.commons.io.IOUtils.toByteArray(fis), mimeType); 99 | } 100 | 101 | @Given("^the previous entries:$") 102 | public void thePreviousEntries(List entries) 103 | { 104 | for (Entry entry : entries) 105 | { 106 | calc.push(entry.first); 107 | calc.push(entry.second); 108 | calc.push(entry.operation); 109 | } 110 | } 111 | 112 | public class Entry 113 | { 114 | Integer first; 115 | Integer second; 116 | String operation; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/at/porscheinformatik/cucumber/formatter/basic_arithmetic.feature: -------------------------------------------------------------------------------- 1 | @foo 2 | Feature: Basic Arithmetic 3 | 4 | Background: A Calculator 5 | Given a calculator I just turned on 6 | 7 | @SILK_ID_1 8 | Scenario: Addition 9 | # Try to change one of the values below to provoke a failure 10 | When I add 4 and 5 11 | Then the result is 9 12 | 13 | @SILK_ID_2 14 | Scenario: Another Addition 15 | # Try to change one of the values below to provoke a failure 16 | When I add 4 and 7 17 | Then the result is 11 18 | 19 | @SILK_ID_3 20 | Scenario Outline: Many additions 21 | Given the previous entries: 22 | | first | second | operation | 23 | | 1 | 1 | + | 24 | | 2 | 1 | + | 25 | When I press + 26 | And I add and 27 | And I press + 28 | Then the result is 29 | 30 | Examples: Single digits 31 | | a | b | c | 32 | | 1 | 2 | 1 | 33 | | 2 | 3 | 2 | 34 | 35 | Examples: Double digits 36 | | a | b | c | 37 | | 10 | 20 | 35 | 38 | | 20 | 30 | 2 | 39 | 40 | 41 | Scenario: Another Addition 42 | # Try to change one of the values below to provoke a failure 43 | When I add 4 and 7 44 | And I have an unimplemented step 45 | Then the result is 11 46 | 47 | Scenario Outline: Negative additions 48 | Given the previous entries: 49 | | first | second | operation | 50 | | | | | 51 | When I press + 52 | Then the result is 53 | 54 | Examples: 55 | | first | second | number | operation | 56 | | 1 | -2 | 1 | + | 57 | | -3 | -2 | 5 | + | 58 | 59 | 60 | Scenario: Invalid values 61 | Given the previous entries: 62 | | first | second | operation | 63 | | | | + | 64 | 65 | 66 | Scenario: Datatable with empty values 67 | Given the variables: 68 | | | | -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/at/porscheinformatik/cucumber/formatter/basic_arithmetic_one.feature: -------------------------------------------------------------------------------- 1 | @foo 2 | Feature: Basic Arithmetic One 3 | 4 | Background: A Calculator 5 | Given a calculator I just turned on 6 | 7 | @Skip 8 | Scenario: Addition Two 9 | # Try to change one of the values below to provoke a failure 10 | When I add 4 and 5 11 | Then the result is 9 -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/at/porscheinformatik/cucumber/formatter/basic_arithmetic_two.feature: -------------------------------------------------------------------------------- 1 | @foo 2 | Feature: Basic Arithmetic Two 3 | 4 | Background: A Calculator 5 | Given a calculator I just turned on 6 | 7 | Scenario: Addition Two 8 | # Try to change one of the values below to provoke a failure 9 | When I add 4 and 5 10 | Then the result is 9 11 | 12 | Scenario: Another Addition Two 13 | # Try to change one of the values below to provoke a failure 14 | When I add 4 and 7 15 | Then the result is 11 16 | 17 | @SILK_ID_4 18 | Scenario Outline: Many additions 19 | Given the previous entries: 20 | | first | second | operation | 21 | | 1 | 1 | + | 22 | | 2 | 1 | + | 23 | When I press + 24 | And I add and 25 | And I press + 26 | Then the result is 27 | 28 | Examples: Double digits 29 | | a | b | c | 30 | | 10 | 20 | 35 | 31 | | 20 | 30 | 2 | 32 | 33 | 34 | @SILK_ID_5 35 | Scenario: Another Addition Two 36 | # Try to change one of the values below to provoke a failure 37 | When I add 4 and 7 38 | And I have an unimplemented step 39 | Then the result is 11 40 | -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/at/porscheinformatik/cucumber/formatter/exception.feature: -------------------------------------------------------------------------------- 1 | Feature: Scenarios with exceptions 2 | 3 | Scenario: Failing Assertion with tags 4 | Given I have an assertion with tags -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/at/porscheinformatik/cucumber/formatter/exception_in_before.feature: -------------------------------------------------------------------------------- 1 | @ExceptionOnBefore 2 | Feature: Check whether an exception in the before-hook doesn't mess up the report 3 | 4 | Scenario: Simple Addition 5 | Given a calculator I just turned on 6 | When I add 4 and 5 7 | Then the result is 9 -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/sampleVideo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/test/resources/sampleVideo.mp4 -------------------------------------------------------------------------------- /cucumber-formatter/src/test/resources/sampleVideo.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/cucumber-formatter/src/test/resources/sampleVideo.zip -------------------------------------------------------------------------------- /cucumber-mongodb-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | Porsche Informatik Cucumber MongoDB Rest API 5 | cucumber-mongodb-rest 6 | jar 7 | 8 | 9 | at.porscheinformatik.cucumber-report-db 10 | cucumber-report-db-parent 11 | 1.2.0.BUILD-SNAPSHOT 12 | .. 13 | 14 | 15 | 16 | 17 | 18 | org.springframework 19 | spring-webmvc 20 | 21 | 22 | 23 | org.springframework.data 24 | spring-data-mongodb 25 | 26 | 27 | org.springframework.security 28 | spring-security-web 29 | 30 | 31 | 32 | javax.servlet 33 | javax.servlet-api 34 | provided 35 | 36 | 37 | com.google.guava 38 | guava 39 | 40 | 41 | 42 | org.mongodb 43 | mongo-java-driver 44 | 45 | 46 | 47 | org.slf4j 48 | slf4j-api 49 | 50 | 51 | 52 | com.google.code.gson 53 | gson 54 | 55 | 56 | org.testng 57 | testng 58 | test 59 | 60 | 61 | org.mockito 62 | mockito-core 63 | test 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/CollectionAccessChecker.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.data.mongodb.core.MongoOperations; 7 | import org.springframework.data.mongodb.core.query.Criteria; 8 | import org.springframework.data.mongodb.core.query.Query; 9 | import org.springframework.security.core.GrantedAuthority; 10 | import org.springframework.security.core.context.SecurityContextHolder; 11 | 12 | import com.google.common.base.Function; 13 | import com.google.common.collect.Lists; 14 | 15 | import at.porscheinformatik.cucumber.mongodb.rest.controller.Roles; 16 | 17 | /** 18 | * Utility class to check whether a user has access to a collection(product) by either having role ROLE_ADMIN 19 | * or by having one of the roles specified in the collection "products" for the wanted collection 20 | */ 21 | public abstract class CollectionAccessChecker 22 | { 23 | public static boolean hasAccess(MongoOperations mongodb, String collectionName) 24 | { 25 | List authorities = new ArrayList(SecurityContextHolder.getContext() 26 | .getAuthentication().getAuthorities()); 27 | List roles = Lists.transform(authorities, new Function() 28 | { 29 | @Override 30 | public String apply(final GrantedAuthority grantedAuthority) 31 | { 32 | return grantedAuthority.getAuthority(); 33 | } 34 | }); 35 | 36 | if (roles.contains(Roles.ROLE_ADMIN)) 37 | { 38 | return true; 39 | } 40 | 41 | Query query = new Query(); 42 | Criteria crit = Criteria.where("name").is(collectionName).and("rights").in(roles); 43 | query.addCriteria(crit); 44 | query.fields().include("name"); 45 | return mongodb.exists(query, "products"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/DatabaseConfig.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest; 2 | 3 | import java.net.UnknownHostException; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.data.authentication.UserCredentials; 10 | import org.springframework.data.mongodb.config.AbstractMongoConfiguration; 11 | 12 | import com.mongodb.MongoClient; 13 | import com.mongodb.MongoClientURI; 14 | 15 | @Configuration 16 | public class DatabaseConfig extends AbstractMongoConfiguration 17 | { 18 | private static final String DEFAULT_MONGO_DB_URI = "mongodb://localhost:27017/"; 19 | private static final String DEFAULT_DATABASE_NAME = "bddReports"; 20 | public static final String SYSTEM_PROPERTY_MONGO_URI = "cucumber.report.db.mongo.uri"; 21 | public static final String SYSTEM_PROPERTY_MONGO_DATABASE = "cucumber.report.db.mongo.database"; 22 | public static final String SYSTEM_PROPERTY_MONGO_USERNAME = "cucumber.report.db.mongo.username"; 23 | public static final String SYSTEM_PROPERTY_MONGO_PASSWORD = "cucumber.report.db.mongo.password"; 24 | 25 | private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseConfig.class); 26 | 27 | @Override 28 | protected String getDatabaseName() 29 | { 30 | return getDatabase(); 31 | } 32 | 33 | @Override 34 | @Bean 35 | public MongoClient mongo() throws UnknownHostException 36 | { 37 | String dbUri = getUri(); 38 | LOGGER.info("Connecting to mongo {}", dbUri); 39 | return new MongoClient(new MongoClientURI(dbUri)); 40 | } 41 | 42 | @Override 43 | protected UserCredentials getUserCredentials() 44 | { 45 | String mongoUsername = System.getProperty(SYSTEM_PROPERTY_MONGO_USERNAME); 46 | String mongoPassword = System.getProperty(SYSTEM_PROPERTY_MONGO_PASSWORD); 47 | if (mongoUsername == null) 48 | { 49 | //no authentication 50 | return null; 51 | } 52 | return new UserCredentials(mongoUsername, mongoPassword); 53 | } 54 | 55 | public static String getUri() 56 | { 57 | return System.getProperty(SYSTEM_PROPERTY_MONGO_URI, DEFAULT_MONGO_DB_URI); 58 | } 59 | 60 | public static String getDatabase() 61 | { 62 | return System.getProperty(SYSTEM_PROPERTY_MONGO_DATABASE, DEFAULT_DATABASE_NAME); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/CucumberPluginController.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.data.mongodb.MongoDbFactory; 11 | import org.springframework.data.mongodb.core.MongoTemplate; 12 | import org.springframework.data.mongodb.core.convert.MongoConverter; 13 | import org.springframework.data.mongodb.gridfs.GridFsOperations; 14 | import org.springframework.data.mongodb.gridfs.GridFsTemplate; 15 | import org.springframework.http.HttpStatus; 16 | import org.springframework.http.ResponseEntity; 17 | import org.springframework.security.access.annotation.Secured; 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.PathVariable; 20 | import org.springframework.web.bind.annotation.RequestBody; 21 | import org.springframework.web.bind.annotation.RequestMapping; 22 | import org.springframework.web.bind.annotation.RequestMethod; 23 | import org.springframework.web.bind.annotation.RequestParam; 24 | 25 | import com.google.gson.Gson; 26 | import com.google.gson.JsonArray; 27 | import com.google.gson.JsonObject; 28 | 29 | @Controller 30 | @RequestMapping("/rest/cucumberplugin") 31 | public class CucumberPluginController 32 | { 33 | private static final Logger LOGGER = LoggerFactory.getLogger(CucumberPluginController.class); 34 | 35 | @Autowired 36 | private MongoTemplate mongoTemplate; 37 | 38 | @Autowired 39 | private MongoDbFactory dbFactory; 40 | 41 | @Autowired 42 | private MongoConverter converter; 43 | 44 | @Secured("ROLE_FORMATTER") 45 | @RequestMapping(value = "/{product}/{version}/{category}/report", method = RequestMethod.POST) 46 | public ResponseEntity insertData(@PathVariable("product") String product, 47 | @PathVariable("version") String version, @PathVariable("category") String category, 48 | @RequestBody String reportString) 49 | { 50 | Gson gsonInstance = new Gson(); 51 | JsonObject collectionEntry = new JsonObject(); 52 | JsonObject reportJson = gsonInstance.fromJson(reportString, JsonObject.class); 53 | collectionEntry.addProperty("version", version); 54 | collectionEntry.addProperty("category", category); 55 | collectionEntry.add("report", reportJson); 56 | 57 | if (!mongoTemplate.collectionExists(product)) 58 | { 59 | LOGGER.info("create new entry in products for {}", product); 60 | JsonObject productJson = new JsonObject(); 61 | JsonArray rightsJson = new JsonArray(); 62 | 63 | productJson.addProperty("name", product); 64 | productJson.add("rights", rightsJson); 65 | mongoTemplate.insert(productJson.toString(), "products"); 66 | } 67 | 68 | LOGGER.info("insert report into collection {}, {}", product, version); 69 | mongoTemplate.insert(collectionEntry.toString(), product); 70 | return new ResponseEntity(HttpStatus.OK); 71 | } 72 | 73 | //supports the empty default category 74 | @Secured("ROLE_FORMATTER") 75 | @RequestMapping(value = "/{product}/{version}/report", method = RequestMethod.POST) 76 | public ResponseEntity insertData(@PathVariable("product") String product, 77 | @PathVariable("version") String version, @RequestBody String reportString) 78 | { 79 | return insertData(product, version, "", reportString); 80 | } 81 | 82 | //TODO update to work like insertReport 83 | @Secured("ROLE_FORMATTER") 84 | @RequestMapping(value = "/{collection}/{version}/media", method = RequestMethod.POST) 85 | public ResponseEntity insertMedia(@PathVariable("collection") String collection, 86 | @PathVariable("version") String version, @RequestParam(value = "filename") String filename, 87 | HttpServletRequest httpServletRequest) throws IOException 88 | { 89 | LOGGER.info("insert binary into collection {}, {}", collection, version); 90 | String contentType = httpServletRequest.getHeader("content-type"); 91 | GridFsOperations gridfs = new GridFsTemplate(dbFactory, converter, collection + '_' + version); 92 | gridfs.store(httpServletRequest.getInputStream(), filename, contentType); 93 | return new ResponseEntity(HttpStatus.OK); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/FileController.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | import static org.springframework.data.mongodb.core.query.Query.*; 4 | 5 | import java.io.IOException; 6 | import java.net.UnknownHostException; 7 | 8 | import javax.servlet.ServletOutputStream; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.data.mongodb.MongoDbFactory; 13 | import org.springframework.data.mongodb.core.convert.MongoConverter; 14 | import org.springframework.data.mongodb.gridfs.GridFsCriteria; 15 | import org.springframework.data.mongodb.gridfs.GridFsOperations; 16 | import org.springframework.data.mongodb.gridfs.GridFsTemplate; 17 | import org.springframework.http.HttpStatus; 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.PathVariable; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestMethod; 22 | 23 | import com.mongodb.DB; 24 | import com.mongodb.gridfs.GridFS; 25 | import com.mongodb.gridfs.GridFSDBFile; 26 | 27 | /** 28 | * @author Stefan Mayer (yms) 29 | */ 30 | @Controller 31 | @RequestMapping("/rest/file") 32 | public class FileController 33 | { 34 | @Autowired 35 | private MongoDbFactory dbFactory; 36 | 37 | @Autowired 38 | private MongoConverter converter; 39 | 40 | @RequestMapping(value = "/{collection}/{fileName}/", method = RequestMethod.GET) 41 | public void findFileByName( 42 | @PathVariable(value = "collection") String collection, 43 | @PathVariable(value = "fileName") String fileName, 44 | HttpServletResponse response) throws IOException 45 | { 46 | GridFsOperations gridfs = new GridFsTemplate(dbFactory, converter, collection); 47 | 48 | // TODO check maybe Spring can handle DBCursor automaticallys 49 | GridFSDBFile file = gridfs.findOne(query(GridFsCriteria.whereFilename().is(fileName))); 50 | 51 | if (file != null) 52 | { 53 | response.setContentLength((int) file.getLength()); 54 | String contentType = file.getContentType(); 55 | if (contentType != null) 56 | { 57 | response.setContentType(contentType); 58 | } 59 | ServletOutputStream out = response.getOutputStream(); 60 | 61 | file.writeTo(out); 62 | out.flush(); 63 | out.close(); 64 | } 65 | else 66 | { 67 | response.setStatus(HttpStatus.NOT_FOUND.value()); 68 | } 69 | } 70 | 71 | public GridFSDBFile getByFilename(DB db, String collection, String filename) throws UnknownHostException 72 | { 73 | GridFS gridFS = new GridFS(db, collection); 74 | return gridFS.findOne(filename); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/NameObject.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | public class NameObject 4 | { 5 | private String id; 6 | private String name; 7 | 8 | public String getId() 9 | { 10 | return id; 11 | } 12 | 13 | public String getName() 14 | { 15 | return name; 16 | } 17 | 18 | public void setName(String name) 19 | { 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public String toString() 25 | { 26 | return "{\"id\"=\"" + id + "\", \"name\"=\"" + name + "\"}"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/RightController.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.mongodb.core.MongoOperations; 8 | import org.springframework.data.mongodb.core.query.Criteria; 9 | import org.springframework.data.mongodb.core.query.Query; 10 | import org.springframework.data.mongodb.core.query.Update; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.security.access.annotation.Secured; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.PathVariable; 16 | import org.springframework.web.bind.annotation.RequestBody; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RequestMethod; 19 | import org.springframework.web.bind.annotation.ResponseBody; 20 | 21 | import com.mongodb.BasicDBObject; 22 | 23 | @Controller 24 | @RequestMapping("/rest/rights") 25 | public class RightController 26 | { 27 | @Autowired 28 | private MongoOperations mongodb; 29 | 30 | @Secured(Roles.ROLE_ADMIN) 31 | @RequestMapping(value = "/{product}", method = RequestMethod.GET) 32 | @ResponseBody 33 | public List getRight(@PathVariable(value = "product") String product) throws IOException 34 | { 35 | BasicDBObject query = new BasicDBObject(); 36 | query.put("name", product); 37 | 38 | @SuppressWarnings("unchecked") 39 | List products = mongodb.getCollection("products").distinct("rights", query); 40 | return products; 41 | } 42 | 43 | @Secured(Roles.ROLE_ADMIN) 44 | @RequestMapping(value = "/{product}", method = RequestMethod.POST) 45 | @ResponseBody 46 | public ResponseEntity postRight(@PathVariable(value = "product") String product, 47 | @RequestBody String newGroup) throws IOException 48 | { 49 | Query query = new Query(); 50 | Criteria criteria = Criteria.where("name").is(product); 51 | query.addCriteria(criteria); 52 | 53 | Update update = new Update(); 54 | update.addToSet("rights", newGroup); 55 | 56 | if (mongodb.updateFirst(query, update, "products").isUpdateOfExisting()) 57 | { 58 | return new ResponseEntity(HttpStatus.OK); 59 | } 60 | else 61 | { 62 | return new ResponseEntity(HttpStatus.NOT_MODIFIED); 63 | } 64 | } 65 | 66 | @Secured(Roles.ROLE_ADMIN) 67 | @RequestMapping(value = "/{product}/{group}", method = RequestMethod.DELETE) 68 | @ResponseBody 69 | public ResponseEntity deleteRight(@PathVariable(value = "product") String product, 70 | @PathVariable(value = "group") String group) throws IOException 71 | { 72 | Query query = new Query(); 73 | Criteria criteria = Criteria.where("name").is(product); 74 | query.addCriteria(criteria); 75 | 76 | Update update = new Update(); 77 | update.pull("rights", group); 78 | 79 | if (mongodb.updateFirst(query, update, "products").isUpdateOfExisting()) 80 | { 81 | return new ResponseEntity(HttpStatus.OK); 82 | } 83 | else 84 | { 85 | return new ResponseEntity(HttpStatus.NOT_MODIFIED); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/RightsObject.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | public class RightsObject 4 | { 5 | private String id; 6 | private String rights; 7 | 8 | public String getId() 9 | { 10 | return id; 11 | } 12 | 13 | public String getRights() 14 | { 15 | return rights; 16 | } 17 | 18 | public void setRights(String rights) 19 | { 20 | this.rights = rights; 21 | } 22 | 23 | @Override 24 | public String toString() 25 | { 26 | return "{\"id\"=\"" + id + "\", \"rights\"=\"" + rights + "\"}"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/RoleController.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.context.SecurityContextHolder; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestMethod; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | 13 | import com.google.common.base.Function; 14 | import com.google.common.collect.Lists; 15 | 16 | @Controller 17 | @RequestMapping("/rest/roles") 18 | public class RoleController 19 | { 20 | @RequestMapping(value = "/current", method = RequestMethod.GET) 21 | public @ResponseBody List getRole() 22 | { 23 | List authorities = new ArrayList(SecurityContextHolder.getContext() 24 | .getAuthentication().getAuthorities()); 25 | List roles = Lists.transform(authorities, new Function() 26 | { 27 | @Override 28 | public String apply(final GrantedAuthority grantedAuthority) 29 | { 30 | return grantedAuthority.getAuthority(); 31 | } 32 | }); 33 | return roles; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/Roles.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | public interface Roles 4 | { 5 | String FORMATTER = "FORMATTER"; 6 | String USER = "USER"; 7 | String ADMIN = "ADMIN"; 8 | 9 | String ROLE_FORMATTER = "ROLE_" + FORMATTER; 10 | String ROLE_ADMIN = "ROLE_" + ADMIN; 11 | String ROLE_USER = "ROLE_" + USER; 12 | } 13 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/StatisticsController.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.mongodb.core.MongoOperations; 10 | import org.springframework.data.mongodb.core.mapreduce.MapReduceResults; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.web.bind.annotation.PathVariable; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.ResponseBody; 17 | 18 | import at.porscheinformatik.cucumber.mongodb.rest.CollectionAccessChecker; 19 | 20 | /** 21 | * @author Johannes Probst 22 | */ 23 | @Controller 24 | @RequestMapping("/rest/statistics/rankings") 25 | public class StatisticsController 26 | { 27 | @Autowired 28 | private MongoOperations mongodb; 29 | 30 | private static final String MAP_STEPS_DURATIONS = "classpath:mapStepsDurations.js"; 31 | private static final String MAP_MOST_FAILED_STEPS = "classpath:mapMostFailedSteps.js"; 32 | private static final String MAP_MOST_EXECUTED_STEPS = "classpath:mapMostExecutedSteps.js"; 33 | 34 | private static final String REDUCE_HIGHEST_SINGLE_STEP_DURATIONS = "classpath:reduceHighestSingleStepDurations.js"; 35 | private static final String REDUCE_CUMULATED_STEP_DURATIONS = "classpath:reduceCumulatedStepDurations.js"; 36 | 37 | @RequestMapping(value = "/{collection}/CumulatedStepDurationRanking", method = RequestMethod.GET) 38 | @ResponseBody 39 | public void findHighestCumulatedStepDuration( 40 | HttpServletRequest request, 41 | @PathVariable(value = "collection") String collection, 42 | HttpServletResponse response) throws IOException 43 | { 44 | mapReduce(collection, MAP_STEPS_DURATIONS, REDUCE_CUMULATED_STEP_DURATIONS, response); 45 | } 46 | 47 | @RequestMapping(value = "/{collection}/highestSingleStepDurationRanking", method = RequestMethod.GET) 48 | @ResponseBody 49 | public void findHighestSingleStepDuration( 50 | HttpServletRequest request, 51 | @PathVariable(value = "collection") String collection, 52 | HttpServletResponse response) throws IOException 53 | { 54 | mapReduce(collection, MAP_STEPS_DURATIONS, REDUCE_HIGHEST_SINGLE_STEP_DURATIONS, response); 55 | } 56 | 57 | @RequestMapping(value = "/{collection}/mostFailedStepsRanking", method = RequestMethod.GET) 58 | @ResponseBody 59 | public void findMostFailed( 60 | HttpServletRequest request, 61 | @PathVariable(value = "collection") String collection, 62 | HttpServletResponse response) throws IOException 63 | { 64 | mapReduce(collection, MAP_MOST_FAILED_STEPS, REDUCE_CUMULATED_STEP_DURATIONS, response); 65 | } 66 | 67 | @RequestMapping(value = "/{collection}/mostExecutedStepsRanking", method = RequestMethod.GET) 68 | @ResponseBody 69 | public void findMostExecuted( 70 | HttpServletRequest request, 71 | @PathVariable(value = "collection") String collection, 72 | HttpServletResponse response) throws IOException 73 | { 74 | mapReduce(collection, MAP_MOST_EXECUTED_STEPS, REDUCE_CUMULATED_STEP_DURATIONS, response); 75 | } 76 | 77 | private void mapReduce(String collection, String mapFunction, String reduceFunction, HttpServletResponse response) 78 | throws IOException 79 | { 80 | if (!CollectionAccessChecker.hasAccess(mongodb, collection)) 81 | { 82 | response.sendError(403); 83 | return; 84 | } 85 | 86 | MapReduceResults allCollectionSteps = 87 | mongodb.mapReduce(collection, mapFunction, reduceFunction, ValueObject.class); 88 | 89 | StringBuilder result = new StringBuilder("["); 90 | for (ValueObject valueObject : allCollectionSteps) 91 | { 92 | result.append("{\"id\":\"" + valueObject.getId().replace("\"", "\\\"") + "\", \"value\":" 93 | + valueObject.getValue() + "}" + ","); 94 | } 95 | if (result.length() > 1) 96 | { 97 | result.replace(result.length() - 1, result.length(), "]"); 98 | } 99 | else 100 | { 101 | result.append("]"); 102 | } 103 | 104 | response.setContentType(MediaType.APPLICATION_JSON_VALUE); 105 | response.getWriter().write(result.toString()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/ValueObject.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | public class ValueObject 4 | { 5 | private String id; 6 | private float value; 7 | 8 | public String getId() 9 | { 10 | return id; 11 | } 12 | 13 | public float getValue() 14 | { 15 | return value; 16 | } 17 | 18 | public void setValue(float value) 19 | { 20 | this.value = value; 21 | } 22 | 23 | @Override 24 | public String toString() 25 | { 26 | return "{\"id\"=\"" + id + "\", \"value\"=\"" + value + "\"}"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/DateDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | public class DateDTO 4 | { 5 | private String $date; 6 | 7 | public String get$date() 8 | { 9 | return $date; 10 | } 11 | 12 | public void set$date(String $date) 13 | { 14 | this.$date = $date; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/EmbeddingDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | public class EmbeddingDTO 4 | { 5 | private String mime_type; 6 | private String url; 7 | 8 | public String getMime_type() 9 | { 10 | return mime_type; 11 | } 12 | 13 | public void setMime_type(String mime_type) 14 | { 15 | this.mime_type = mime_type; 16 | } 17 | 18 | public String getUrl() 19 | { 20 | return url; 21 | } 22 | 23 | public void setUrl(String url) 24 | { 25 | this.url = url; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/FeatureDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | import java.util.List; 4 | 5 | public class FeatureDTO 6 | { 7 | private List tags; 8 | private String id; 9 | private ResultDTO result; 10 | private String description; 11 | private String keyword; 12 | private String name; 13 | private Integer line; 14 | private String uri; 15 | private List scenarios; 16 | 17 | public List getTags() 18 | { 19 | return tags; 20 | } 21 | 22 | public void setTags(List tags) 23 | { 24 | this.tags = tags; 25 | } 26 | 27 | public String getId() 28 | { 29 | return id; 30 | } 31 | 32 | public void setId(String id) 33 | { 34 | this.id = id; 35 | } 36 | 37 | public ResultDTO getResult() 38 | { 39 | return result; 40 | } 41 | 42 | public void setResult(ResultDTO result) 43 | { 44 | this.result = result; 45 | } 46 | 47 | public String getDescription() 48 | { 49 | return description; 50 | } 51 | 52 | public void setDescription(String description) 53 | { 54 | this.description = description; 55 | } 56 | 57 | public String getKeyword() 58 | { 59 | return keyword; 60 | } 61 | 62 | public void setKeyword(String keyword) 63 | { 64 | this.keyword = keyword; 65 | } 66 | 67 | public String getName() 68 | { 69 | return name; 70 | } 71 | 72 | public void setName(String name) 73 | { 74 | this.name = name; 75 | } 76 | 77 | public Integer getLine() 78 | { 79 | return line; 80 | } 81 | 82 | public void setLine(Integer line) 83 | { 84 | this.line = line; 85 | } 86 | 87 | public String getUri() 88 | { 89 | return uri; 90 | } 91 | 92 | public void setUri(String uri) 93 | { 94 | this.uri = uri; 95 | } 96 | 97 | public List getScenarios() 98 | { 99 | return scenarios; 100 | } 101 | 102 | public void setScenarios(List scenarios) 103 | { 104 | this.scenarios = scenarios; 105 | } 106 | 107 | public ScenarioDTO getScenarioById(String id) 108 | { 109 | for (ScenarioDTO scenario : scenarios) 110 | { 111 | if (scenario.getId().equals(id)) 112 | { 113 | return scenario; 114 | } 115 | } 116 | return null; 117 | } 118 | 119 | public ScenarioDTO getScenarioByTag(String tag) 120 | { 121 | for (ScenarioDTO scenario : scenarios) 122 | { 123 | if (scenario.getTags() != null) 124 | { 125 | for (TagDTO currentTag : scenario.getTags()) 126 | { 127 | if (currentTag.getName().equals(tag)) 128 | { 129 | return scenario; 130 | } 131 | } 132 | } 133 | } 134 | return null; 135 | } 136 | } -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/ReportDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | import java.util.Calendar; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.TimeZone; 7 | 8 | /** 9 | * @author Stefan Mayer (yms) 10 | */ 11 | public class ReportDTO 12 | { 13 | private List features; 14 | private DateDTO date; 15 | 16 | public List getFeatures() 17 | { 18 | return features; 19 | } 20 | 21 | public void setFeatures(List features) 22 | { 23 | this.features = features; 24 | } 25 | 26 | public Date getDate() 27 | { 28 | return getCalendar().getTime(); 29 | } 30 | 31 | public Calendar getCalendar() 32 | { 33 | Calendar cal = javax.xml.bind.DatatypeConverter.parseDateTime(date.get$date()); 34 | cal.setTimeZone(TimeZone.getTimeZone("GMT+02:00")); 35 | return cal; 36 | } 37 | 38 | public DateDTO getDateDTO() 39 | { 40 | return date; 41 | } 42 | 43 | public void setDateDTO(DateDTO date) 44 | { 45 | this.date = date; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/ResultDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class ResultDTO 6 | { 7 | private BigInteger duration; 8 | private Integer stepCount; 9 | private Integer scenarioCount; 10 | private Integer passedStepCount; 11 | private Integer skippedStepCount; 12 | private Integer failedStepCount; 13 | 14 | public BigInteger getDuration() 15 | { 16 | return duration; 17 | } 18 | 19 | public void setDuration(BigInteger duration) 20 | { 21 | this.duration = duration; 22 | } 23 | 24 | public Integer getStepCount() 25 | { 26 | return stepCount; 27 | } 28 | 29 | public void setStepCount(Integer stepCount) 30 | { 31 | this.stepCount = stepCount; 32 | } 33 | 34 | public Integer getScenarioCount() 35 | { 36 | return scenarioCount; 37 | } 38 | 39 | public void setScenarioCount(Integer scenarioCount) 40 | { 41 | this.scenarioCount = scenarioCount; 42 | } 43 | 44 | public Integer getPassedStepCount() 45 | { 46 | return passedStepCount; 47 | } 48 | 49 | public void setPassedStepCount(Integer passedStepCount) 50 | { 51 | this.passedStepCount = passedStepCount; 52 | } 53 | 54 | public Integer getSkippedStepCount() 55 | { 56 | return skippedStepCount; 57 | } 58 | 59 | public void setSkippedStepCount(Integer skippedStepCount) 60 | { 61 | this.skippedStepCount = skippedStepCount; 62 | } 63 | 64 | public Integer getFailedStepCount() 65 | { 66 | return failedStepCount; 67 | } 68 | 69 | public void setFailedStepCount(Integer failedStepCount) 70 | { 71 | this.failedStepCount = failedStepCount; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/ScenarioDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | import java.util.List; 4 | 5 | public class ScenarioDTO 6 | { 7 | private String id; 8 | private List tags; 9 | private ResultDTO result; 10 | private String description; 11 | private String name; 12 | private String keyword; 13 | private Integer line; 14 | private List steps; 15 | private String type; 16 | 17 | public List getTags() 18 | { 19 | return tags; 20 | } 21 | 22 | public void setTags(List tags) 23 | { 24 | this.tags = tags; 25 | } 26 | 27 | public String getId() 28 | { 29 | return id; 30 | } 31 | 32 | public void setId(String id) 33 | { 34 | this.id = id; 35 | } 36 | 37 | public ResultDTO getResult() 38 | { 39 | return result; 40 | } 41 | 42 | public void setResult(ResultDTO result) 43 | { 44 | this.result = result; 45 | } 46 | 47 | public String getDescription() 48 | { 49 | return description; 50 | } 51 | 52 | public void setDescription(String description) 53 | { 54 | this.description = description; 55 | } 56 | 57 | public String getName() 58 | { 59 | return name; 60 | } 61 | 62 | public void setName(String name) 63 | { 64 | this.name = name; 65 | } 66 | 67 | public String getKeyword() 68 | { 69 | return keyword; 70 | } 71 | 72 | public void setKeyword(String keyword) 73 | { 74 | this.keyword = keyword; 75 | } 76 | 77 | public Integer getLine() 78 | { 79 | return line; 80 | } 81 | 82 | public void setLine(Integer line) 83 | { 84 | this.line = line; 85 | } 86 | 87 | public List getSteps() 88 | { 89 | return steps; 90 | } 91 | 92 | public void setSteps(List steps) 93 | { 94 | this.steps = steps; 95 | } 96 | 97 | public String getType() 98 | { 99 | return type; 100 | } 101 | 102 | public void setType(String type) 103 | { 104 | this.type = type; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/StepDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | import java.util.List; 4 | 5 | public class StepDTO 6 | { 7 | private StepResultDTO result; 8 | private List embeddings; 9 | private String name; 10 | private String keyword; 11 | private Integer line; 12 | 13 | public StepResultDTO getResult() 14 | { 15 | return result; 16 | } 17 | 18 | public void setResult(StepResultDTO result) 19 | { 20 | this.result = result; 21 | } 22 | 23 | public List getEmbeddings() 24 | { 25 | return embeddings; 26 | } 27 | 28 | public void setEmbeddings(List embeddings) 29 | { 30 | this.embeddings = embeddings; 31 | } 32 | 33 | public String getName() 34 | { 35 | return name; 36 | } 37 | 38 | public void setName(String name) 39 | { 40 | this.name = name; 41 | } 42 | 43 | public String getKeyword() 44 | { 45 | return keyword; 46 | } 47 | 48 | public void setKeyword(String keyword) 49 | { 50 | this.keyword = keyword; 51 | } 52 | 53 | public Integer getLine() 54 | { 55 | return line; 56 | } 57 | 58 | public void setLine(Integer line) 59 | { 60 | this.line = line; 61 | } 62 | } -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/StepResultDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class StepResultDTO 6 | { 7 | private BigInteger duration; 8 | private String status; 9 | private String error_message; 10 | 11 | public BigInteger getDuration() 12 | { 13 | return duration; 14 | } 15 | 16 | public void setDuration(BigInteger duration) 17 | { 18 | this.duration = duration; 19 | } 20 | 21 | public String getStatus() 22 | { 23 | return status; 24 | } 25 | 26 | public void setStatus(String status) 27 | { 28 | this.status = status; 29 | } 30 | 31 | public String getError_message() 32 | { 33 | return error_message; 34 | } 35 | 36 | public void setError_message(String error_message) 37 | { 38 | this.error_message = error_message; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/controller/dto/TagDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller.dto; 2 | 3 | public class TagDTO 4 | { 5 | private String name; 6 | private Long line; 7 | 8 | public String getName() 9 | { 10 | return name; 11 | } 12 | 13 | public void setName(String name) 14 | { 15 | this.name = name; 16 | } 17 | 18 | public Long getLine() 19 | { 20 | return line; 21 | } 22 | 23 | public void setLine(Long line) 24 | { 25 | this.line = line; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/java/at/porscheinformatik/cucumber/mongodb/rest/db/MongoDB.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.db; 2 | 3 | import java.util.Iterator; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import com.google.gson.Gson; 9 | import com.mongodb.BasicDBObject; 10 | import com.mongodb.DB; 11 | import com.mongodb.DBCollection; 12 | import com.mongodb.DBObject; 13 | import com.mongodb.MongoClient; 14 | 15 | import at.porscheinformatik.cucumber.mongodb.rest.DatabaseConfig; 16 | 17 | /** 18 | * @author Stefan Mayer (yms) 19 | */ 20 | @Service 21 | public class MongoDB 22 | { 23 | @Autowired 24 | private MongoClient mongoClient; 25 | 26 | public T fetchLast(Class type, String collectionName, String fieldName, String fieldValue) 27 | { 28 | DB database = mongoClient.getDB(DatabaseConfig.getDatabase()); 29 | if (!database.collectionExists(collectionName)) 30 | { 31 | throw new RuntimeException("The collection: \"" + collectionName + "\" does not exist!"); 32 | } 33 | DBCollection collection = database.getCollection(collectionName); 34 | BasicDBObject dbObject = new BasicDBObject(); 35 | dbObject.put(fieldName, fieldValue); 36 | 37 | DBObject sortBy = new BasicDBObject("_id", -1); 38 | DBObject excludeId = new BasicDBObject("_id", 0); 39 | 40 | Iterator fetchResults = collection.find(dbObject, excludeId).sort(sortBy).limit(1).iterator(); 41 | 42 | return new Gson().fromJson(fetchResults.next().toString(), type); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/resources/mapMostExecutedSteps.js: -------------------------------------------------------------------------------- 1 | function() {this.report.features.forEach(function(feature) { 2 | if(feature.scenarios !== undefined) { 3 | feature.scenarios.forEach(function(scenario) { 4 | scenario.steps.forEach(function(step) { 5 | if (step.result.status==="failed" || step.result.status==="passed" ){ 6 | emit(step.name, 1); 7 | } 8 | }) 9 | }) 10 | } 11 | })} -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/resources/mapMostFailedSteps.js: -------------------------------------------------------------------------------- 1 | function() { 2 | this.report.features.forEach(function(feature) { 3 | if(feature.scenarios != null) { 4 | feature.scenarios.forEach(function (scenario) { 5 | scenario.steps.forEach(function (step) { 6 | if (step.result.status === "failed") { 7 | emit(step.name, 1); 8 | } 9 | }) 10 | }) 11 | } 12 | })} -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/resources/mapStepsDurations.js: -------------------------------------------------------------------------------- 1 | function() {this.report.features.forEach(function(feature) { 2 | if(feature.scenarios !== undefined) { 3 | feature.scenarios.forEach(function (scenario) { 4 | scenario.steps.forEach(function (step) { 5 | if (step.result.status === "failed" || step.result.status === "passed") { 6 | emit(step.name, step.result.duration); 7 | } 8 | }) 9 | }) 10 | } 11 | })} -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/resources/reduceCumulatedStepDurations.js: -------------------------------------------------------------------------------- 1 | function(key, values) { return Array.sum(values); } -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/main/resources/reduceHighestSingleStepDurations.js: -------------------------------------------------------------------------------- 1 | function(key, values) { return Math.max.apply(Math, values); } 2 | -------------------------------------------------------------------------------- /cucumber-mongodb-rest/src/test/java/at/porscheinformatik/cucumber/mongodb/rest/controller/QueryControllerTest.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.mongodb.rest.controller; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | 5 | import org.testng.annotations.DataProvider; 6 | import org.testng.annotations.Test; 7 | 8 | public class QueryControllerTest 9 | { 10 | @Test(dataProvider = "computeNumberOfSkips") 11 | public void last_ten_with_list_nine(int cursorLength, int lastNrOfDocuments, int expectedNrOfSkips) 12 | { 13 | QueryController queryController = new QueryController(); 14 | int actualNrOfSkips = queryController.skipToLast(cursorLength, lastNrOfDocuments); 15 | assertEquals(actualNrOfSkips, expectedNrOfSkips); 16 | } 17 | 18 | @DataProvider 19 | public Object[][] computeNumberOfSkips() 20 | { 21 | return new Object[][]{ 22 | {9, 10, 0}, 23 | {11, 10, 1}, 24 | {30, 10, 20} 25 | }; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /cucumber-report-web/gulpfile.js: -------------------------------------------------------------------------------- 1 | // - modules 2 | 3 | var path = require('path'), 4 | gulp = require('gulp'), 5 | tsc = require('gulp-typescript'), 6 | tslint = require('gulp-tslint'), 7 | concat = require('gulp-concat'), 8 | uglify = require('gulp-uglify'), 9 | angularFilesort = require('gulp-angular-filesort'), 10 | sourcemaps = require('gulp-sourcemaps'), 11 | util = require('gulp-util'); 12 | 13 | // - config 14 | 15 | var npmPath = 'node_modules/'; 16 | var srcDir = './src/main/web/'; 17 | var angularVersion = !!util.env.production ? 'node_modules/angular/angular.min.js' : 'node_modules/angular/angular.js'; 18 | var conf = { 19 | paths: { 20 | vendorjs: [ 21 | angularVersion, 22 | 'node_modules/angular-ui-router/release/angular-ui-router.min.js', 23 | 'node_modules/angular-sanitize/angular-sanitize.min.js', 24 | 'node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js', 25 | 'node_modules/angular-google-chart/ng-google-chart.min.js'], 26 | vendorstyles: [ 27 | 'node_modules/bootswatch/spacelab/bootstrap.min.css', 28 | 'node_modules/angular-ui-bootstrap/dist/ui-bootstrap-csp.css'], 29 | dist: './target/classes/static/assets/' 30 | }, 31 | production: !!util.env.production 32 | }; 33 | 34 | var tsProject = tsc.createProject('tsconfig.json'), 35 | tsGlob = tsProject.config.compilerOptions.rootDir + '**/*.ts'; 36 | 37 | // - tasks 38 | gulp.task('ts-lint', function() { 39 | return gulp.src(tsGlob) 40 | .pipe(tslint({ 41 | formatter: "verbose" 42 | })) 43 | .pipe(tslint.report()); 44 | }); 45 | 46 | gulp.task('ts-compile', function() { 47 | return gulp.src(tsGlob) 48 | .pipe(tsc(tsProject)) 49 | .pipe(angularFilesort()) 50 | .pipe(concat('app.min.js')) 51 | .pipe(conf.production ? uglify() : util.noop()) 52 | .pipe(gulp.dest(conf.paths.dist)); 53 | }); 54 | 55 | gulp.task('ts-testcompile', function() { 56 | gulp.src('src/test/typescript/**/*.ts') 57 | .pipe(tsc()) 58 | .pipe(gulp.dest('target/typescript/')); 59 | }); 60 | 61 | 62 | gulp.task('scripts-vendor', function () { 63 | return gulp.src(conf.paths.vendorjs) 64 | .pipe(sourcemaps.init({loadMaps: true})) 65 | .pipe(concat('vendor.min.js')) 66 | .pipe(conf.production ? util.noop() : sourcemaps.write()) 67 | .pipe(gulp.dest(conf.paths.dist)); 68 | }); 69 | 70 | gulp.task('angular-i18n', function () { 71 | return gulp.src('node_modules/angular-i18n/angular-locale_*.js') 72 | .pipe(gulp.dest(conf.paths.dist + '/i18n')); 73 | }); 74 | 75 | gulp.task('style-vendor', function () { 76 | return gulp.src(conf.paths.vendorstyles) 77 | .pipe(concat('vendor.css')) 78 | .pipe(gulp.dest(conf.paths.dist)); 79 | }); 80 | 81 | gulp.task('fonts', function () { 82 | return gulp.src('node_modules/bootswatch/fonts/glyphicons*') 83 | .pipe(gulp.dest(conf.paths.dist + '../fonts/')); 84 | }); 85 | 86 | 87 | gulp.task('test', ['scripts-vendor', 'ts-testcompile', 'ts-compile'], function (done) { 88 | var Server = require('karma').Server; 89 | new Server({ 90 | configFile: __dirname + '/src/test/karma.conf.js', 91 | browsers: ['Firefox'], 92 | singleRun: true 93 | }, done).start(); 94 | }); 95 | 96 | gulp.task('default', ['style-vendor', 'fonts', 'scripts-vendor', 'ts-lint', 'ts-compile']); 97 | 98 | gulp.task('watch', ['default'], function () { 99 | gulp.watch(tsGlob, ['ts-compile', 'ts-lint']); 100 | }); 101 | -------------------------------------------------------------------------------- /cucumber-report-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cucumber-report-web", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "gulp", 8 | "watch": "gulp watch" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "private": true, 13 | "dependencies": { 14 | "angular": "1.5.8", 15 | "angular-google-chart": "0.1.0", 16 | "angular-ui-bootstrap": "2.0.0", 17 | "angular-ui-router": "0.3.1", 18 | "bootswatch": "3.3.7" 19 | }, 20 | "devDependencies": { 21 | "gulp": "~3.9.1", 22 | "gulp-angular-filesort": "~1.1.1", 23 | "gulp-concat": "~2.6.0", 24 | "gulp-sourcemaps": "~1.6.0", 25 | "gulp-tslint": "~6.0.2", 26 | "gulp-typescript": "~2.13.6", 27 | "gulp-uglify": "~1.5.4", 28 | "gulp-util": "~3.0.7", 29 | "tslint": "~3.14.0", 30 | "typescript": "~1.8.10" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cucumber-report-web/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | Porsche Informatik Cucumber Report Web 6 | cucumber-report-web 7 | war 8 | 9 | 10 | at.porscheinformatik.cucumber-report-db 11 | cucumber-report-db-parent 12 | 1.2.0.BUILD-SNAPSHOT 13 | .. 14 | 15 | 16 | 17 | 18 | 19 | ${project.groupId} 20 | cucumber-mongodb-rest 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-tomcat 30 | provided 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-security 35 | 36 | 37 | org.springframework.security 38 | spring-security-ldap 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-configuration-processor 43 | 44 | 45 | 46 | ch.qos.logback 47 | logback-classic 48 | 49 | 50 | 51 | 52 | 53 | 54 | yms 55 | Stefan Mayer 56 | s.mayer@porscheinformatik.at 57 | 58 | Developer 59 | 60 | 61 | 62 | ypa 63 | Peyman Aparviz 64 | aparviz.peyman@porscheinformatik.at 65 | 66 | Developer 67 | 68 | 69 | 70 | pro 71 | Ortwin Probst 72 | ortwin.probst@porscheinformatik.at 73 | 74 | Developer 75 | 76 | 77 | 78 | koc 79 | Christian Köberl 80 | christian.koeberl@porscheinformatik.at 81 | 82 | Developer 83 | 84 | 85 | 86 | xbk 87 | Klaus Bayrhammer 88 | extern.klaus.bayrhammer@porscheinformatik.at 89 | 90 | Developer 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | maven-war-plugin 99 | 100 | 101 | package 102 | 103 | war 104 | 105 | 106 | 107 | 108 | true 109 | 110 | 111 | 112 | 113 | 114 | 115 | true 116 | false 117 | 118 | 119 | 120 | org.springframework.boot 121 | spring-boot-maven-plugin 122 | 123 | 124 | 125 | repackage 126 | 127 | 128 | bootable 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/java/at/porscheinformatik/cucumber/CucumberReportApplication.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.context.web.SpringBootServletInitializer; 7 | import org.springframework.context.annotation.ComponentScan; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @ComponentScan 12 | @EnableAutoConfiguration 13 | public class CucumberReportApplication extends SpringBootServletInitializer 14 | { 15 | public static void main(String[] args) 16 | { 17 | SpringApplication.run(CucumberReportApplication.class, args); 18 | } 19 | 20 | @Override 21 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 22 | { 23 | return application.sources(CucumberReportApplication.class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/java/at/porscheinformatik/cucumber/web/UserController.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.web; 2 | 3 | import org.springframework.security.core.context.SecurityContext; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | @RequestMapping("/api/users") 10 | public class UserController 11 | { 12 | @RequestMapping("/current/") 13 | public Object currentUser() 14 | { 15 | SecurityContext securityContext = SecurityContextHolder.getContext(); 16 | 17 | return securityContext.getAuthentication(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/java/at/porscheinformatik/cucumber/web/config/CucumberReportProperties.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.web.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @ConfigurationProperties("cucumber.report") 11 | public class CucumberReportProperties 12 | { 13 | private Security security = new Security(); 14 | 15 | private List users = new ArrayList<>(); 16 | 17 | public Security getSecurity() 18 | { 19 | return security; 20 | } 21 | 22 | public List getUsers() 23 | { 24 | return users; 25 | } 26 | 27 | public static class Security 28 | { 29 | // TODO doc cucumber.report.db.secured 30 | private SecurityType type = SecurityType.DISABLED; 31 | 32 | private List admins = new ArrayList<>(); 33 | 34 | private List formatters = new ArrayList<>(); 35 | 36 | public static enum SecurityType 37 | { 38 | LDAP, 39 | IN_MEMORY, 40 | DISABLED 41 | } 42 | 43 | private Ldap ldap = new Ldap(); 44 | 45 | public SecurityType getType() 46 | { 47 | return type; 48 | } 49 | 50 | public void setType(SecurityType type) 51 | { 52 | this.type = type; 53 | } 54 | 55 | public Ldap getLdap() 56 | { 57 | return ldap; 58 | } 59 | 60 | public List getAdmins() 61 | { 62 | return admins; 63 | } 64 | 65 | public List getFormatters() 66 | { 67 | return formatters; 68 | } 69 | } 70 | 71 | public static class Ldap 72 | { 73 | private String url; 74 | 75 | public String getUrl() 76 | { 77 | return url; 78 | } 79 | 80 | public void setUrl(String url) 81 | { 82 | this.url = url; 83 | } 84 | } 85 | 86 | public static class User 87 | { 88 | private String name; 89 | 90 | private String password; 91 | 92 | private List roles = new ArrayList<>(); 93 | 94 | public String getName() 95 | { 96 | return name; 97 | } 98 | 99 | public void setName(String name) 100 | { 101 | this.name = name; 102 | } 103 | 104 | public String getPassword() 105 | { 106 | return password; 107 | } 108 | 109 | public void setPassword(String password) 110 | { 111 | this.password = password; 112 | } 113 | 114 | public List getRoles() 115 | { 116 | return roles; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/java/at/porscheinformatik/cucumber/web/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.cucumber.web.config; 2 | 3 | import java.io.IOException; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.autoconfigure.web.ResourceProperties; 7 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.core.io.Resource; 10 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 12 | import org.springframework.web.servlet.resource.PathResourceResolver; 13 | 14 | @Configuration 15 | @EnableConfigurationProperties({ResourceProperties.class}) 16 | public class WebMvcConfig extends WebMvcConfigurerAdapter 17 | { 18 | @Autowired 19 | private ResourceProperties resourceProperties; 20 | 21 | @Override 22 | public void addResourceHandlers(ResourceHandlerRegistry registry) 23 | { 24 | Integer cachePeriod = resourceProperties.getCachePeriod(); 25 | 26 | registry.addResourceHandler("/**/*.*") 27 | .addResourceLocations(resourceProperties.getStaticLocations()) 28 | .setCachePeriod(cachePeriod); 29 | 30 | registry.addResourceHandler("/**") 31 | .addResourceLocations("classpath:/static/index.html") 32 | .setCachePeriod(cachePeriod) 33 | .resourceChain(true) 34 | .addResolver(new PathResourceResolver() 35 | { 36 | @Override 37 | protected Resource getResource(String resourcePath, Resource location) throws IOException 38 | { 39 | return location.exists() && location.isReadable() ? location : null; 40 | } 41 | }); 42 | } 43 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/components/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/css/style.css: -------------------------------------------------------------------------------- 1 | .btn:focus { 2 | outline: 0; 3 | } 4 | .affix { 5 | width: 200px; 6 | } 7 | .table td , .table th { 8 | text-align: center !important; 9 | } 10 | 11 | .table .firstTD{ 12 | text-align: left !important; 13 | } 14 | .table .noWrap{ 15 | white-space: nowrap; 16 | } 17 | 18 | .table thead > tr > th, .table tbody > tr > th, .table tfoot > tr > th, .table thead > tr > td, .table tbody > tr > td, .table tfoot > tr > td { 19 | padding: 6px; 20 | } 21 | .badge, .label { 22 | cursor: default; 23 | } 24 | .container { 25 | width: 95% !important; 26 | } 27 | .uri{ 28 | display: inline-block; 29 | padding: 0px 6px; 30 | margin: 0px; 31 | /*word-wrap: normal;*/ 32 | word-break: normal; 33 | white-space: normal; 34 | color: #d14; 35 | } 36 | .errorLogContent{ 37 | margin: 10px; 38 | } 39 | 40 | .errorLogContent .btn{ 41 | position: absolute; 42 | } 43 | 44 | .errorLogContent dl{ 45 | word-wrap: break-word; 46 | } 47 | 48 | .errorLogCode{ 49 | padding-top: 22px; 50 | word-break: normal; 51 | word-wrap: normal; 52 | white-space: pre; 53 | overflow: auto; 54 | max-height: 250px; 55 | } 56 | 57 | .panelHead{ 58 | color: #ffffff; 59 | background-color: #446e9b; 60 | border-color: #446e9b; 61 | padding: 10px 15px; 62 | border-bottom: 1px solid transparent; 63 | border-top-right-radius: 3px; 64 | border-top-left-radius: 3px; 65 | } 66 | .panelTitle{ 67 | display: inline; 68 | color: #fff; 69 | margin-top: 0; 70 | margin-bottom: 0; 71 | font-size: 16px; 72 | } 73 | 74 | .panelBody img{ 75 | max-width:100%; 76 | cursor: pointer; 77 | cursor: -webkit-zoom-in; 78 | cursor: -moz-zoom-in; 79 | } 80 | 81 | .sortToggle span{ 82 | cursor: pointer; 83 | } 84 | 85 | .tdCollapse { 86 | max-height: 20px; 87 | overflow: hidden; 88 | cursor: pointer; 89 | } 90 | 91 | .tdUncollapse { 92 | max-height: none; 93 | } 94 | 95 | tr.rankings td:nth-child(1) { 96 | width:70%; 97 | } 98 | tr.rankings td:nth-child(2) { 99 | width:15%; 100 | } 101 | tr.rankings td:nth-child(3) { 102 | width:15%; 103 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test Report 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 29 |
30 | 31 |
32 | 33 |
34 | 39 | 44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/assignments.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 40 | 41 | 42 |
ProductAssigned Groups
17 | 18 | 39 |
43 |
-------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/embedded_lightbox.html: -------------------------------------------------------------------------------- 1 | 4 | 15 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/features.html: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
FeatureTagsStatusDurationScenariosSteps
PassedUnknownFailedTotalPassedUnknownFailedSkippedTotal
{{ feature.name }} {{ tag.name }} FAILED OK {{ fCtrl.formatTime(feature.result.duration) }} {{ feature.result.passedScenarioCount || "" }}{{ feature.result.unknownScenarioCount || "" }}{{ feature.result.failedScenarioCount || "" }}{{ feature.result.scenarioCount || "" }}{{ feature.result.passedStepCount || "" }}{{ feature.result.unknownStepCount || "" }}{{ feature.result.failedStepCount || "" }}{{ feature.result.skippedStepCount || "" }}{{ feature.result.stepCount || "" }}
{{ fCtrl.features.length ? 'Sum' : 'No entries found!'}}{{ fCtrl.features.length ? fCtrl.formatTime(fCtrl.featureSums.duration) : '' }}{{ fCtrl.featureSums.passedScenarioCount }}{{ fCtrl.featureSums.unknownScenarioCount }}{{ fCtrl.featureSums.failedScenarioCount }}{{ fCtrl.featureSums.scenarioCount }}{{ fCtrl.featureSums.passedStepCount }}{{ fCtrl.featureSums.unknownStepCount }}{{ fCtrl.featureSums.failedStepCount }}{{ fCtrl.featureSums.skippedStepCount }}{{ fCtrl.featureSums.stepCount }}
66 |
-------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/help.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |

Help

7 | 8 |
9 | 15 |
16 | 17 |

Common

18 |
19 | <TODO> 20 |
21 | 22 |

MongoDB Spring Rest API

23 |
24 | <TODO> 25 |
26 | 27 | 28 |

BDD-Silk Plugin

29 |
30 |

1. Kopieren des Silk-Plugins auf den Execution Server

31 |

Hierbei muss das ausführbare Silk-Java-Plugin an eine beliebige Stelle am "Exceution Server" kopiert werden. Z.B.:

32 |
C:\<PFAD ZUM SILK PLUGIN>\silk-bdd-plugin-1.0.jar
33 |

Durch doppelklick auf das ".jar" File öffnet sich eine Konfigurations-GUI wo man unter anderem die Verbindungsdaten zum 34 | Server konfiguriert kann.

35 | 36 |

2. Anlegen der benötigten Test Cases in Silk Central als Junit Tests 37 |

38 |

39 | WICHTIG: Hier muss darauf geachtet werden, dass bei jedem Test Case die "JUnit Test Properties" korrekt gesetzt 40 | werden: 41 |

42 |
Vorlage: Test class = com.poi.egh.turntable.silkplugin.SilkSynchronizer.class Test method = run Java home directory = C:\Programme\Java\jre6\
43 | 		Classpath = C:\
44 | 		\silk-bdd-plugin-1.0.jar Record external AUT Coverage = false
45 | 	
46 |

47 | HINWEIS: Der Name für einen "Test Case" kann beliebig gewählt werden, und folgt keiner bestimmten Namenskonvention. 48 |

49 | 50 |

3. Anlegen der benötigten Execution Plans in Silk Central

51 |

52 | WICHTIG: Es muss unbedingt auf die richtige Benennung der "Execution Plans" geachtet werden. Dabei wird folgende 53 | Namenskonvention verwendet (diese kann in der Silk Plugin Konfigurations-GUI geändert werden): 54 |

55 |
Vorlage für einen einzelnen "Execution Plan": VDV3_<BELIEBIGER TEXT> (z.B. VDV3_ 01 - 1 - Login OK)
56 |

In diesem Fall ist das Prefix "VDV3". Somit weiß das Plugin dass es sich um einen Test von VDV3 handelt.

57 |
Vorlage für gleiche "Execution Plans" für mehrere Produkte: Dies kann mithilfe von sogenannten "Configuration Suites" festgelegt
58 | 		werden. Möchte man z.B. einen Testfall für mehrere Produkte mappen, so werden in einer "Configuration Suite" die entsprechenden
59 | 		Produkt (z.B. VDV3, VGSG) hinzugefügt: <BELIEBIGER NAME FÜR SUITE> VDV3 VGSG
60 | 	
61 |

62 | WICHTIG: Damit die jeweiligen "Executions" ausgeführt werden können, muss unter "Deployment" der Server auf 63 | dem sich das Silk-Plugin befindet, angegeben werden. 64 |

65 | 66 |

Konfigurieren der BDD-Szenarien

67 |

Damit das Silk-Plugin einen Silk-Test einem BDD-Test zuordnen kann, müssen die Szenarien in den BDD-Features um ein neues 68 | Cucumber-Tag (Default: @SILK_ID_XXXX) erweitert werden. Nachdem man das zu erweiternde BDD-Scenario gefunden hat, muss 69 | dieses wie im folgenden Abschnitt beschrieben um eine Annotation erweitert werden:

70 |
Beispiel: Möchte man etwa die Execution "VDV3_01-1 - Login OK" auf das BDD-Scenario "Login is ok" mappen, so geht man folgendermaßen
71 | 		vor: a. Suchen der ensprechenden "Execution Plan ID" in Silk Central (z.B. 27172) b. Suchen des BDD-Feature welches unser
72 | 		Ziel-Scenario enthält (z.B. test.feature) c. Heraussuchen des Scenarios (z.B. Scenario: Login is ok)# d. Dieses Scenario
73 | 		wird nun um eine neue Annotation erweitert. Z.B.: @SILK_ID_<EXECUTION PLAN ID> (z.B. @SILK_ID_27172) Scenario: Login
74 | 		is ok When the user uses correct credentials to log in Then the user should be logged in
75 | 76 |

Fertig ;-)

77 |

Jenkins ist so konfiguriert, dass alle Ergebnisse der BDD-Tests die dort ausgeführt werden, in eine NoSQL Datenbank nach 78 | Produkt und Version gespeichert werden. Sind zu einem Silk-Test keine Daten am Server vorhanden, oder tritt ein Problem 79 | beim Auslesen der Daten auf, so wird in Silk eine entsprechende Fehlermeldung im LOG angezeigt.

80 |
-------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/products.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 24 | 27 | 28 | 29 |
ProductStatisticsBulk Deletion
18 | 19 | 21 | 22 | 23 | 25 | 26 |
30 |
31 | -------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/reports.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Category ({{rCtrl.categoryId}}) 4 | 13 | 14 | 15 | Version ({{rCtrl.versionId}}) 16 | 25 | 26 |
27 | 28 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 59 | 60 | 65 | 66 | 67 |
FeaturesVersionCategoryStatisticsDateAdmin
{{report.featureNames}}{{report.version}}{{report.category}} 52 |
53 | {{statistics=rCtrl.statistics;""}} 54 |
{{report.statistics.passed}}
55 |
{{report.statistics.unknown}}
56 |
{{report.statistics.failed}}
57 |
58 |
{{ report.report.date.$date | date:'dd-MM-yyyy HH:mm:ss' }} 61 | 64 |
68 |
69 | 70 |
71 |
    72 |
  • 73 | « 74 |
  • 75 |
  • 76 | {{n + 1}} 77 |
  • 78 |
  • 79 | » 80 |
  • 81 |
82 |
-------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/scenarios.html: -------------------------------------------------------------------------------- 1 | 8 |

Feature: {{ sCtrl.feature.name }}

9 |
10 | {{ tag.name }}
11 |
12 |
13 |
{{ sCtrl.feature.uri }}
14 |
15 | 16 |

Scenarios

17 |
18 |
19 |

Scenario: {{ scenario.name }}

20 |
21 | {{ tag.name }}
22 |
23 |
24 |
Description
25 |

{{ scenario.description }}

26 |

27 | {{ scenario.background.keyword }} {{ scenario.background.name }} 28 |

29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 59 | 62 | 63 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
StepStatusDurationAttachments
{{ step.keyword }} {{ step.name }} 42 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 |
48 | {{ headerCols }}
54 | {{ dataCols }}
58 |
{{ step.result.status }} 60 | {{ step.result.duration ? sCtrl.formatTime(step.result.duration) : '' }} 65 | 68 | 69 | 72 | 73 | {{getFileEndingWithCapitalFirstLetter(n)}} 77 | 78 | 79 |
Sum{{ scenario.status }}{{ scenario.result.duration ? sCtrl.formatTime(scenario.result.duration) : '' }}
89 |
90 |
91 |
-------------------------------------------------------------------------------- /cucumber-report-web/src/main/resources/static/pages/statistics.html: -------------------------------------------------------------------------------- 1 | 2 | Filter ({{statsCtrl.limit}}) 3 | 10 | 11 | 12 | Chart Type ({{statsCtrl.type}}) 13 | 17 | 18 | 19 | 24 | 25 |
-------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/app.module.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb { 2 | 'use strict'; 3 | 4 | 5 | angular.module('ccrdb', [ 6 | 'ccrdb.core', 7 | 'ccrdb.pages', 8 | 'ccrdb.components', 9 | 'ccrdb.directives', 10 | 'ui.router', 11 | 'ui.bootstrap', 12 | 'googlechart' 13 | ]); 14 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/components/components.module.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.components { 2 | 'use strict'; 3 | 4 | angular.module('ccrdb.components', []); 5 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/components/menu.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.components { 2 | 'use strict'; 3 | 4 | export class MenuController { 5 | private products: String[]; 6 | 7 | static $inject = ['$state', 'productsService', 'authenticationService', 'deletionModeService', 'backButtonService', 8 | 'searchBarService']; 9 | 10 | constructor( 11 | private $state: ng.ui.IStateService, 12 | private productsService: ccrdb.pages.IProductsService, 13 | private authenticationService: ccrdb.core.AuthenticationService, 14 | private deletionModeService: ccrdb.core.DeletionModeService, 15 | private backButtonService: ccrdb.core.BackButtonService, 16 | private searchBar: ccrdb.core.SearchBarService) { 17 | } 18 | 19 | toggleDeletionMode() { 20 | this.deletionModeService.enabled = !this.deletionModeService.enabled; 21 | } 22 | 23 | isAdmin() { 24 | return this.authenticationService.isAdmin; 25 | } 26 | 27 | clearCache() { 28 | // TODO make it do whatever it's purpose is 29 | } 30 | 31 | goBack() { 32 | this.$state.go(this.backButtonService.prevState, this.backButtonService.prevParams); 33 | } 34 | } 35 | 36 | angular 37 | .module('ccrdb.components') 38 | .controller('MenuController', MenuController); 39 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/authentication.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 3 | 'use strict'; 4 | 5 | export class AuthenticationService { 6 | public isAdmin: boolean; 7 | 8 | static $inject = ['$http']; 9 | 10 | constructor(private $http: ng.IHttpService) { 11 | this.$http.get('rest/roles/current').success((roles: string[]) => { 12 | this.isAdmin = roles.indexOf('ROLE_ADMIN') >= 0; 13 | }); 14 | } 15 | 16 | private success: (response: any) => {} = (response) => response.data; 17 | } 18 | 19 | angular.module('ccrdb.core') 20 | .service('authenticationService', ccrdb.core.AuthenticationService); 21 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/back-button.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 3 | 'use strict'; 4 | 5 | export class BackButtonService { 6 | public enabled: boolean = false; 7 | public prevState: string = 'products'; 8 | public prevParams: any = {}; 9 | } 10 | 11 | angular.module('ccrdb.core') 12 | .service('backButtonService', BackButtonService); 13 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/core.module.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 'use strict'; 3 | 4 | angular.module('ccrdb.core', ['ui.router']); 5 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/core.router.config.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 'use strict'; 3 | angular 4 | .module('ccrdb.core') 5 | .config(routerConfig); 6 | 7 | routerConfig.$inject = ['$locationProvider', '$stateProvider', '$urlRouterProvider', '$httpProvider']; 8 | function routerConfig($locationProvider: ng.ILocationProvider, $stateProvider: ng.ui.IStateProvider, 9 | $urlRouterProvider: ng.ui.IUrlRouterProvider, $httpProvider: ng.IHttpProvider) { 10 | 11 | $locationProvider.html5Mode(true); 12 | $urlRouterProvider.otherwise('/'); 13 | 14 | $stateProvider.state('base', { 15 | abstract: true, 16 | template: '', 17 | views: { 18 | 'header': { 19 | templateUrl: 'components/header.html', 20 | controller: 'MenuController', 21 | controllerAs: 'menuCtrl' 22 | } 23 | } 24 | }); 25 | 26 | $stateProvider.state('products', { 27 | url: '/', 28 | parent: 'base', 29 | views: { 30 | '@': { 31 | templateUrl: 'pages/products.html', 32 | controller: 'ProductsController', 33 | controllerAs: 'pCtrl' 34 | } 35 | } 36 | }); 37 | 38 | $stateProvider.state('reports', { 39 | url: '/reports/{productId:string}/version/{versionId:string}/category/{categoryId:string}', 40 | params: { 41 | page: 0 42 | }, 43 | parent: 'base', 44 | views: { 45 | '@': { 46 | templateUrl: 'pages/reports.html', 47 | controller: 'ReportsController', 48 | controllerAs: 'rCtrl' 49 | } 50 | } 51 | }); 52 | 53 | $stateProvider.state('features', { 54 | url: '/reports/{productId:string}/features/{reportId:string}', 55 | parent: 'base', 56 | views: { 57 | '@': { 58 | templateUrl: 'pages/features.html', 59 | controller: 'FeaturesController', 60 | controllerAs: 'fCtrl' 61 | } 62 | } 63 | }); 64 | 65 | $stateProvider.state('scenarios', { 66 | url: '/reports/{productId:string}/features/{reportId:string}/scenarios/{featureId:string}', 67 | parent: 'base', 68 | views: { 69 | '@': { 70 | templateUrl: 'pages/scenarios.html', 71 | controller: 'ScenariosController', 72 | controllerAs: 'sCtrl' 73 | } 74 | } 75 | }); 76 | 77 | $stateProvider.state('charts', { 78 | url: '/statistics/{productId: string}/type/{type: string}/limit/{limit: string}', 79 | parent: 'base', 80 | views: { 81 | '@': { 82 | templateUrl: 'pages/statistics.html', 83 | controller: 'StatisticsController', 84 | controllerAs: 'statsCtrl' 85 | } 86 | } 87 | }); 88 | 89 | $stateProvider.state('rankings', { 90 | url: '/statistics/rankings/{productId: string}', 91 | parent: 'base', 92 | views: { 93 | '@': { 94 | templateUrl: 'pages/rankings.html', 95 | controller: 'RankingsController', 96 | controllerAs: 'ranksCtrl' 97 | } 98 | } 99 | }); 100 | 101 | $stateProvider.state('assignments', { 102 | url: '/assignments', 103 | parent: 'base', 104 | views: { 105 | '@': { 106 | templateUrl: 'pages/assignments.html', 107 | controller: 'AssignmentsController', 108 | controllerAs: 'assignCtrl' 109 | } 110 | } 111 | }); 112 | 113 | $stateProvider.state('help', { 114 | url: '/help', 115 | parent: 'base', 116 | views: { 117 | '@': { 118 | templateUrl: 'pages/help.html', 119 | controller: 'HelpController', 120 | controllerAs: 'helpCtrl' 121 | } 122 | } 123 | }); 124 | } 125 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/deletion-mode.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 3 | 'use strict'; 4 | 5 | export class DeletionModeService { 6 | public enabled = false; 7 | } 8 | 9 | angular.module('ccrdb.core') 10 | .service('deletionModeService', DeletionModeService); 11 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/index.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 'use strict'; 3 | 4 | export class IndexController { 5 | static $inject = ['loadingBarService']; 6 | 7 | constructor(private loadingBarService: ccrdb.core.LoadingBarService) { 8 | } 9 | } 10 | 11 | angular 12 | .module('ccrdb.core') 13 | .controller('IndexController', IndexController); 14 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/loading-bar.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 3 | 'use strict'; 4 | 5 | export class LoadingBarService { 6 | public reqCount = 0; 7 | } 8 | 9 | 10 | angular.module('ccrdb.core') 11 | .service('loadingBarService', LoadingBarService); 12 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/core/search-bar.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.core { 2 | 3 | 'use strict'; 4 | 5 | export class SearchBarService { 6 | public searchText = ''; 7 | } 8 | 9 | angular.module('ccrdb.core') 10 | .service('searchBarService', SearchBarService); 11 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/directives/confirm.directive.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.directives { 2 | 'use strict'; 3 | 4 | 5 | export function confirmation(): ng.IDirective { 6 | return { 7 | restrict: 'A', 8 | link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: any) => { 9 | element.bind('click', function () { 10 | let message = attrs.ngReallyMessage; 11 | if (message && confirm(message)) { 12 | scope.$apply(attrs.confirmation); 13 | } 14 | }); 15 | } 16 | }; 17 | } 18 | 19 | angular 20 | .module('ccrdb.directives') 21 | .directive('confirmation', confirmation); 22 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/directives/directives.module.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.directives { 2 | 'use strict'; 3 | 4 | angular.module('ccrdb.directives', []); 5 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/assignments.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class AssignmentsController { 5 | private products: String[]; 6 | 7 | 8 | static $inject = ['$uibModal', 'productsService', 'backButtonService', 'searchBarService', 'loadingBarService']; 9 | 10 | constructor(private $uibModal: ng.ui.bootstrap.IModalService, 11 | private productsService: ccrdb.pages.IProductsService, 12 | private backButtonService: ccrdb.core.BackButtonService, 13 | private searchBar: ccrdb.core.SearchBarService, 14 | private loadingBarService: ccrdb.core.LoadingBarService) { 15 | this.loadProducts(); 16 | 17 | backButtonService.enabled = true; 18 | backButtonService.prevState = 'products'; 19 | backButtonService.prevParams = {}; 20 | } 21 | 22 | loadProducts() { 23 | this.loadingBarService.reqCount++; 24 | this.productsService.getProducts().then((products) => { 25 | this.loadingBarService.reqCount--; 26 | this.products = products.slice().reverse(); 27 | }); 28 | } 29 | 30 | openModal(productId: string) { 31 | let modalInstance = this.$uibModal.open({ 32 | templateUrl: 'GroupModalContent.html', 33 | controller: 'GroupModalInstanceController', 34 | controllerAs: 'gmiCtrl', 35 | resolve: { 36 | productId: function () { 37 | return productId; 38 | } 39 | } 40 | }); 41 | } 42 | } 43 | 44 | angular 45 | .module('ccrdb.pages') 46 | .controller('AssignmentsController', AssignmentsController); 47 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/features.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class FeaturesController { 5 | private productId: string; 6 | private reportId: string; 7 | private features: any[]; 8 | private featureSums: any; 9 | 10 | 11 | static $inject = ['$stateParams', 'featuresService', 'backButtonService', 'searchBarService', 'loadingBarService']; 12 | 13 | constructor(private $stateParams: ng.ui.IStateParamsService, 14 | private featuresService: ccrdb.pages.IFeaturesService, 15 | private backButtonService: ccrdb.core.BackButtonService, 16 | private searchBar: ccrdb.core.SearchBarService, 17 | private loadingBarService: ccrdb.core.LoadingBarService) { 18 | this.getParams(); 19 | this.loadFeatures(); 20 | 21 | backButtonService.enabled = true; 22 | backButtonService.prevState = 'reports'; 23 | backButtonService.prevParams = { productId: this.productId, versionId: 'ALL', categoryId: 'ALL' }; 24 | } 25 | 26 | getParams() { 27 | this.productId = this.$stateParams['productId']; 28 | this.reportId = this.$stateParams['reportId']; 29 | } 30 | 31 | loadFeatures() { 32 | this.loadingBarService.reqCount++; 33 | this.featuresService.getFeatures(this.productId, this.reportId).then((features) => { 34 | this.loadingBarService.reqCount--; 35 | 36 | this.features = features; 37 | this.featureSums = this.featuresService.generateSums(this.features); 38 | }); 39 | } 40 | 41 | formatTime(ns: number) { 42 | return Time.nsToString(ns); 43 | } 44 | } 45 | 46 | angular 47 | .module('ccrdb.pages') 48 | .controller('FeaturesController', FeaturesController); 49 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/features.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IFeaturesService { 6 | getFeatures: (productId: string, reportId: string) => ng.IPromise; 7 | generateSums: (features: any[]) => any; 8 | } 9 | 10 | export class FeaturesService implements IFeaturesService { 11 | static $inject = ['$http']; 12 | 13 | constructor(private $http: ng.IHttpService) { 14 | } 15 | 16 | getFeatures(productId: string, reportId: string) { 17 | return this.$http.get('rest/query/' + productId + '/' + reportId).then((response: any) => { 18 | let features: any[] = response.data[0].report.features; 19 | features.forEach(feature => { 20 | // need to be supplemented 21 | feature.result.passedScenarioCount = 0; 22 | feature.result.unknownScenarioCount = 0; 23 | feature.result.failedScenarioCount = 0; 24 | 25 | // feature.result.stepCount seems broken, has to be recalculated from scenarios 26 | feature.result.stepCount = 0; 27 | 28 | feature.scenarios.forEach((scenario: any) => { 29 | if (scenario.result.failedStepCount) { 30 | feature.result.failedScenarioCount++; 31 | } else if (scenario.result.skippedStepCount) { 32 | feature.result.unknownScenarioCount++; 33 | } else { 34 | feature.result.passedScenarioCount++; 35 | } 36 | feature.result.stepCount += scenario.result.stepCount; 37 | }); 38 | }); 39 | 40 | return features; 41 | }); 42 | } 43 | 44 | generateSums(features: any[]) { 45 | let featureSums = { 46 | duration: String, 47 | passedScenarioCount: Number, 48 | unknownScenarioCount: Number, 49 | failedScenarioCount: Number, 50 | scenarioCount: Number, 51 | passedStepCount: Number, 52 | unknownStepCount: Number, 53 | failedStepCount: Number, 54 | skippedStepCount: Number, 55 | stepCount: Number 56 | }; 57 | 58 | featureSums.duration = this.sum(features, 'duration'); 59 | featureSums.passedScenarioCount = this.sum(features, 'passedScenarioCount'); 60 | featureSums.unknownScenarioCount = this.sum(features, 'unknownScenarioCount'); 61 | featureSums.failedScenarioCount = this.sum(features, 'failedScenarioCount'); 62 | featureSums.scenarioCount = this.sum(features, 'scenarioCount'); 63 | featureSums.passedStepCount = this.sum(features, 'passedStepCount'); 64 | featureSums.unknownStepCount = this.sum(features, 'unknownStepCount'); 65 | featureSums.failedStepCount = this.sum(features, 'failedStepCount'); 66 | featureSums.skippedStepCount = this.sum(features, 'skippedStepCount'); 67 | featureSums.stepCount = this.sum(features, 'stepCount'); 68 | 69 | return featureSums; 70 | } 71 | 72 | sum(features: any[], field: string) { 73 | if (!(features.length > 0)) { return null; } 74 | let sum = features.reduce(function (sum, feature) { 75 | let value = parseInt(feature.result[field], 10); 76 | return isNaN(value) ? sum : sum + value; 77 | }, 0); 78 | return sum; 79 | }; 80 | 81 | private success: (response: any) => {} = (response) => response.data; 82 | } 83 | 84 | angular.module('ccrdb.pages') 85 | .service('featuresService', ccrdb.pages.FeaturesService); 86 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/groupModalInstance.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class GroupModalInstanceController { 5 | private groups: string[]; 6 | private newGroup: any; 7 | 8 | 9 | static $inject = ['$uibModalInstance', 'groupModalInstanceService', 'productId', 'loadingBarService']; 10 | 11 | constructor(private $uibModalInstance: ng.ui.bootstrap.IModalServiceInstance, 12 | private groupModalInstanceService: ccrdb.pages.IGroupModalInstanceService, 13 | private productId: string, 14 | private loadingBarService: ccrdb.core.LoadingBarService) { 15 | this.loadGroups(); 16 | this.newGroup = { name: '' }; 17 | } 18 | 19 | loadGroups() { 20 | console.log(this); 21 | this.loadingBarService.reqCount++; 22 | this.groupModalInstanceService.getGroups(this.productId).then((groups: string[]) => { 23 | this.loadingBarService.reqCount--; 24 | 25 | this.groups = groups; 26 | }); 27 | } 28 | 29 | addGroup() { 30 | this.loadingBarService.reqCount++; 31 | if (this.newGroup.name !== '' && this.groups.indexOf(this.newGroup.name) <= -1) { 32 | this.groupModalInstanceService.postGroup(this.productId, this.newGroup.name).then((success: boolean) => { 33 | this.loadingBarService.reqCount--; 34 | 35 | if (success) { 36 | this.groups.push(this.newGroup.name); 37 | } else { 38 | console.error('Failed to add group.'); 39 | } 40 | }); 41 | } 42 | } 43 | 44 | removeGroup(index: number) { 45 | this.loadingBarService.reqCount++; 46 | this.groupModalInstanceService.deleteGroup(this.productId, this.groups[index]).then((success: boolean) => { 47 | this.loadingBarService.reqCount--; 48 | 49 | if (success) { 50 | this.groups.splice(index, 1); 51 | } else { 52 | console.error('Failed to remove group.'); 53 | } 54 | }); 55 | } 56 | 57 | close() { 58 | this.$uibModalInstance.close(); 59 | } 60 | } 61 | 62 | angular 63 | .module('ccrdb.pages') 64 | .controller('GroupModalInstanceController', GroupModalInstanceController); 65 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/groupModalInstance.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IGroupModalInstanceService { 6 | getGroups: (productId: string) => ng.IPromise; 7 | postGroup: (productId: string, groupName: string) => ng.IPromise; 8 | deleteGroup: (productId: string, groupName: string) => ng.IPromise; 9 | } 10 | 11 | export class GroupModalInstanceService implements IGroupModalInstanceService { 12 | static $inject = ['$http']; 13 | 14 | constructor(private $http: ng.IHttpService) { 15 | } 16 | 17 | getGroups(productId: string) { 18 | return this.$http.get('rest/rights/' + productId).then(this.success); 19 | } 20 | 21 | postGroup(productId: string, groupName: string) { 22 | return this.$http.post('rest/rights/' + productId, groupName).success(() => { 23 | return true; 24 | }).error(() => { 25 | return false; 26 | }); 27 | } 28 | 29 | deleteGroup(productId: string, groupName: string) { 30 | return this.$http.delete('rest/rights/' + productId + '/' + groupName).success(() => { 31 | return true; 32 | }).error(() => { 33 | return false; 34 | }); 35 | } 36 | 37 | private success: (response: any) => {} = (response) => response.data; 38 | } 39 | 40 | angular.module('ccrdb.pages') 41 | .service('groupModalInstanceService', ccrdb.pages.GroupModalInstanceService); 42 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/help.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class HelpController { 5 | 6 | 7 | static $inject = ['backButtonService']; 8 | 9 | constructor(private backButtonService: ccrdb.core.BackButtonService, 10 | private loadingBarService: ccrdb.core.LoadingBarService) { 11 | backButtonService.enabled = true; 12 | backButtonService.prevState = 'products'; 13 | backButtonService.prevParams = {}; 14 | } 15 | } 16 | 17 | angular 18 | .module('ccrdb.pages') 19 | .controller('HelpController', HelpController); 20 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/pages.module.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | angular.module('ccrdb.pages', ['ccrdb.core']); 5 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/products.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class ProductsController { 5 | private products: String[]; 6 | 7 | 8 | static $inject = ['$rootScope', '$state', 'productsService', 'deletionModeService', 'backButtonService', 9 | 'searchBarService', 'loadingBarService']; 10 | 11 | constructor(private $rootScope: ng.IRootScopeService, 12 | private $state: ng.ui.IStateService, 13 | private productsService: ccrdb.pages.IProductsService, 14 | private deletionModeService: ccrdb.core.DeletionModeService, 15 | private backButtonService: ccrdb.core.BackButtonService, 16 | private searchBar: ccrdb.core.SearchBarService, 17 | private loadingBarService: ccrdb.core.LoadingBarService) { 18 | this.loadProducts(); 19 | 20 | backButtonService.enabled = false; 21 | } 22 | 23 | loadProducts() { 24 | this.loadingBarService.reqCount++; 25 | this.productsService.getProducts().then((products) => { 26 | this.loadingBarService.reqCount--; 27 | 28 | this.products = products.slice().reverse(); 29 | }); 30 | } 31 | 32 | bulkDelete(productId: string) { 33 | this.loadingBarService.reqCount++; 34 | this.productsService.deleteProduct(productId).then((success: boolean) => { 35 | this.loadingBarService.reqCount--; 36 | 37 | if (success) { 38 | this.$state.reload(); 39 | } else { 40 | console.error('Failed to delete product.'); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | angular 47 | .module('ccrdb.pages') 48 | .controller('ProductsController', ProductsController); 49 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/products.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IProductsService { 6 | getProducts: () => ng.IPromise; 7 | deleteProduct: (productId: string) => ng.IPromise; 8 | } 9 | 10 | export class ProductsService implements IProductsService { 11 | static $inject = ['$http']; 12 | 13 | constructor(private $http: ng.IHttpService) { 14 | } 15 | 16 | getProducts() { 17 | return this.$http.get('rest/collection/products') 18 | .then(this.success); 19 | } 20 | 21 | deleteProduct(productId: string) { 22 | return this.$http.delete('rest/query/' + productId + '/_ALL').success(() => { 23 | return true; 24 | }).error(() => { 25 | return false; 26 | }); 27 | } 28 | 29 | private success: (response: any) => {} = (response) => response.data; 30 | } 31 | 32 | angular.module('ccrdb.pages') 33 | .service('productsService', ccrdb.pages.ProductsService); 34 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/rankings.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class RankingsController { 5 | private productId: string; 6 | 7 | private failedSteps: any[]; 8 | private sumOverAllFailed: number; 9 | 10 | private executedSteps: any[]; 11 | private sumOverAllExecuted: number; 12 | 13 | private singleSteps: any[]; 14 | private sumOverAllSingle: number; 15 | 16 | private cumulatedSteps: any[]; 17 | private sumOverAllCumulated: number; 18 | 19 | private selectedStep: string = ''; 20 | private showAll_Failed: boolean = false; 21 | private showAll_MostExecuted: boolean = false; 22 | private showAll_SingleStepDurations: boolean = false; 23 | private showAll_CumulatedStepDuration: boolean = false; 24 | 25 | static $inject = ['$stateParams', 'rankingsService', 'backButtonService', 'loadingBarService']; 26 | 27 | constructor(private $stateParams: ng.ui.IStateParamsService, 28 | private rankingsService: ccrdb.pages.IRankingsService, 29 | private backButtonService: ccrdb.core.BackButtonService, 30 | private loadingBarService: ccrdb.core.LoadingBarService) { 31 | this.getParams(); 32 | this.loadRankings(); 33 | 34 | backButtonService.enabled = true; 35 | backButtonService.prevState = 'products'; 36 | backButtonService.prevParams = {}; 37 | } 38 | 39 | getParams() { 40 | this.productId = this.$stateParams['productId']; 41 | } 42 | 43 | loadRankings() { 44 | this.loadingBarService.reqCount += 4; 45 | this.rankingsService.getRankings(this.productId, 'failed').then((failed: any) => { 46 | this.loadingBarService.reqCount--; 47 | 48 | this.failedSteps = failed.steps; 49 | this.sumOverAllFailed = failed.sum; 50 | }); 51 | 52 | this.rankingsService.getRankings(this.productId, 'executed').then((executed: any) => { 53 | this.loadingBarService.reqCount--; 54 | 55 | this.executedSteps = executed.steps; 56 | this.sumOverAllExecuted = executed.sum; 57 | }); 58 | 59 | this.rankingsService.getRankings(this.productId, 'single').then((single: any) => { 60 | this.loadingBarService.reqCount--; 61 | 62 | this.singleSteps = single.steps; 63 | this.sumOverAllSingle = single.sum; 64 | }); 65 | 66 | this.rankingsService.getRankings(this.productId, 'cumulated').then((cumulated: any) => { 67 | this.loadingBarService.reqCount--; 68 | 69 | this.cumulatedSteps = cumulated.steps; 70 | this.sumOverAllCumulated = cumulated.sum; 71 | }); 72 | } 73 | 74 | selectStep(step: any) { 75 | if (this.selectedStep === step) { 76 | this.selectedStep = ''; 77 | } else { 78 | this.selectedStep = step; 79 | } 80 | } 81 | 82 | formatTime(ns: number) { 83 | return Time.nsToString(ns); 84 | } 85 | } 86 | 87 | angular 88 | .module('ccrdb.pages') 89 | .controller('RankingsController', RankingsController); 90 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/rankings.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IRankingsService { 6 | getRankings: (productId: string, type: string) => ng.IPromise; 7 | } 8 | 9 | export class RankingsService implements IRankingsService { 10 | static $inject = ['$http']; 11 | 12 | constructor(private $http: ng.IHttpService) { 13 | } 14 | 15 | getRankings(productId: string, type: string): ng.IPromise { 16 | let typePath: string; 17 | if (type === 'failed') { 18 | typePath = '/mostFailedStepsRanking'; 19 | } else if (type === 'executed') { 20 | typePath = '/mostExecutedStepsRanking'; 21 | } else if (type === 'single') { 22 | typePath = '/highestSingleStepDurationRanking'; 23 | } else { 24 | typePath = '/CumulatedStepDurationRanking'; 25 | } 26 | return this.$http.get('rest/statistics/rankings/' + productId + typePath).then((response: any) => { 27 | let result: any = {}; 28 | result.steps = response.data; 29 | result.sum = 0; 30 | 31 | result.steps.forEach((step: any) => { 32 | result.sum += step.value; 33 | }); 34 | 35 | result.steps.sort(function (a: any, b: any) { 36 | let x = a['value']; let y = b['value']; 37 | return ((x > y) ? +1 : ((x < y) ? -1 : 0)); 38 | }); 39 | 40 | return result; 41 | }); 42 | } 43 | 44 | private success: (response: any) => {} = (response) => response.data; 45 | } 46 | 47 | angular.module('ccrdb.pages') 48 | .service('rankingsService', ccrdb.pages.RankingsService); 49 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/reports.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class ReportsController { 5 | private productId: string; 6 | private versionId: string; 7 | private categoryId: string; 8 | private versions: String[]; 9 | private categories: String[]; 10 | private pageLimit = 10; 11 | private page: number; 12 | private pages: number[]; 13 | private reports: any[]; 14 | private getStatistics: Function; 15 | 16 | static $inject = ['$state', '$stateParams', 'reportsService', 'authenticationService', 'backButtonService', 17 | 'searchBarService', 'loadingBarService']; 18 | 19 | constructor(private $state: ng.ui.IStateService, 20 | private $stateParams: ng.ui.IStateParamsService, 21 | private reportsService: ccrdb.pages.IReportsService, 22 | private authenticationService: ccrdb.core.AuthenticationService, 23 | private backButtonService: ccrdb.core.BackButtonService, 24 | private searchBar: ccrdb.core.SearchBarService, 25 | private loadingBarService: ccrdb.core.LoadingBarService) { 26 | this.getParams(); 27 | this.loadVersions(); 28 | this.loadCategories(); 29 | this.loadReports(); 30 | 31 | backButtonService.enabled = true; 32 | backButtonService.prevState = 'products'; 33 | backButtonService.prevParams = {}; 34 | } 35 | 36 | getParams() { 37 | this.productId = this.$stateParams['productId']; 38 | this.versionId = this.$stateParams['versionId']; 39 | this.categoryId = this.$stateParams['categoryId']; 40 | this.page = this.$stateParams['page'] === undefined ? 0 : parseInt(this.$stateParams['page']); 41 | 42 | this.loadingBarService.reqCount++; 43 | this.reportsService.getReportCount(this.productId).then((count: number) => { 44 | this.loadingBarService.reqCount--; 45 | 46 | this.pages = []; 47 | const pageCount = count / this.pageLimit; 48 | for (let i = 0; i < pageCount; i++) { 49 | this.pages.push(i); 50 | } 51 | }); 52 | } 53 | 54 | loadVersions() { 55 | this.loadingBarService.reqCount++; 56 | this.reportsService.getVersions(this.productId).then((versions) => { 57 | this.loadingBarService.reqCount--; 58 | 59 | this.versions = versions; 60 | }); 61 | } 62 | 63 | loadCategories() { 64 | this.loadingBarService.reqCount++; 65 | this.reportsService.getCategories(this.productId).then((categories) => { 66 | this.loadingBarService.reqCount--; 67 | 68 | this.categories = categories; 69 | }); 70 | } 71 | 72 | loadReports() { 73 | this.loadingBarService.reqCount++; 74 | this.reportsService.prepareReportData(this.productId, this.pageLimit, (this.page * this.pageLimit), this.versionId, this.categoryId).then((reports) => { 75 | this.loadingBarService.reqCount--; 76 | 77 | this.reports = reports; 78 | }); 79 | } 80 | 81 | deleteDocument(id: string) { 82 | this.loadingBarService.reqCount++; 83 | this.reportsService.deleteDocument(this.productId, id).then((success: boolean) => { 84 | this.loadingBarService.reqCount--; 85 | 86 | if (success) { 87 | this.$state.reload(); 88 | } else { 89 | console.error('Failed to delete report.'); 90 | } 91 | }); 92 | } 93 | 94 | isAdmin() { 95 | return this.authenticationService.isAdmin; 96 | } 97 | } 98 | 99 | angular 100 | .module('ccrdb.pages') 101 | .controller('ReportsController', ReportsController); 102 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/reports.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IReportsService { 6 | getReportCount: (productId: string) => ng.IPromise; 7 | getReports: (productId: string, limit: number, skip: number, versionId: string, categoryId: string) => ng.IPromise; 8 | getVersions: (productId: string) => ng.IPromise; 9 | getCategories: (productId: string) => ng.IPromise; 10 | prepareReportData: (productId: string, limit: number, skip: number, versionId: string, categoryId: string) => ng.IPromise; 11 | deleteDocument: (productId: string, id: string) => ng.IPromise; 12 | } 13 | 14 | export class ReportsService implements IReportsService { 15 | static $inject = ['$http']; 16 | 17 | constructor(private $http: ng.IHttpService) { 18 | } 19 | 20 | getReportCount(productId: string) { 21 | return this.$http.get('rest/collection/' + productId + '/count') 22 | .then(this.success); 23 | } 24 | 25 | getReports(productId: string, limit: number, skip: number, versionId: string, categoryId: string) { 26 | let filters = ''; 27 | if (versionId !== 'ALL') { 28 | filters += '&version=' + versionId; 29 | } 30 | if (categoryId !== 'ALL') { 31 | filters += '&category=' + categoryId; 32 | } 33 | return this.$http.get('rest/query/' + productId + '/?sort=true&limit=' + limit + '&skip=' + skip + filters) 34 | .then(this.success); 35 | } 36 | 37 | getVersions(productId: string) { 38 | return this.$http.get('rest/collection/' + productId + '/versions') 39 | .then(this.success); 40 | } 41 | 42 | getCategories(productId: string) { 43 | return this.$http.get('rest/collection/' + productId + '/categories') 44 | .then(this.success); 45 | } 46 | 47 | prepareReportData(productId: string, limit: number, skip: number, versionId: string, categoryId: string) { 48 | return this.getReports(productId, limit, skip, versionId, categoryId).then((reports: any[]) => { 49 | reports.forEach(report => { 50 | report.statistics = { 51 | passed: 0, 52 | failed: 0, 53 | unknown: 0, 54 | passedPercent: 0, 55 | failedPercent: 0, 56 | unknownPercent: 0 57 | }; 58 | report.featureNames = ''; 59 | 60 | report.report.features.forEach((feature: any) => { 61 | feature.scenarios.forEach((scenario: any) => { 62 | if (scenario.result.failedStepCount) { 63 | report.statistics.failed++; 64 | } else if (scenario.result.skippedStepCount) { 65 | report.statistics.unknown++; 66 | } else { 67 | report.statistics.passed++; 68 | } 69 | }); 70 | 71 | report.featureNames += feature.name + ', '; 72 | }); 73 | report.featureNames = report.featureNames.slice(0, report.featureNames.length - 2); 74 | 75 | let sum = (report.statistics.passed + report.statistics.failed + report.statistics.unknown); 76 | 77 | report.statistics.passedPercent = (report.statistics.passed / sum) * 100; 78 | report.statistics.failedPercent = (report.statistics.failed / sum) * 100; 79 | report.statistics.unknownPercent = (report.statistics.unknown / sum) * 100; 80 | }); 81 | return reports; 82 | }); 83 | } 84 | 85 | deleteDocument(productId: string, id: string) { 86 | return this.$http.delete('rest/query/' + productId + '/' + id).success(() => { 87 | return true; 88 | }).error(() => { 89 | return false; 90 | }); 91 | } 92 | 93 | private success: (response: any) => {} = (response) => response.data; 94 | } 95 | 96 | angular.module('ccrdb.pages') 97 | .service('reportsService', ccrdb.pages.ReportsService); 98 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/scenarios.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class ScenariosController { 5 | private productId: string; 6 | private reportId: string; 7 | private featureId: string; 8 | private feature: any; 9 | 10 | static $inject = ['$stateParams', '$uibModal', 'scenariosService', 'backButtonService', 'searchBarService', 'loadingBarService']; 11 | 12 | constructor(private $stateParams: ng.ui.IStateParamsService, 13 | private $uibModal: ng.ui.bootstrap.IModalService, 14 | private scenariosService: ccrdb.pages.IScenariosService, 15 | private backButtonService: ccrdb.core.BackButtonService, 16 | private searchBar: ccrdb.core.SearchBarService, 17 | private loadingBarService: ccrdb.core.LoadingBarService) { 18 | this.getParams(); 19 | this.loadFeature(); 20 | 21 | backButtonService.enabled = true; 22 | backButtonService.prevState = 'features'; 23 | backButtonService.prevParams = { productId: this.productId, reportId: this.reportId }; 24 | } 25 | 26 | getParams() { 27 | this.productId = this.$stateParams['productId']; 28 | this.reportId = this.$stateParams['reportId']; 29 | this.featureId = this.$stateParams['featureId']; 30 | } 31 | 32 | loadFeature() { 33 | this.loadingBarService.reqCount++; 34 | this.scenariosService.getFeature(this.productId, this.reportId, this.featureId).then((feature) => { 35 | this.loadingBarService.reqCount--; 36 | 37 | this.feature = feature; 38 | }); 39 | } 40 | 41 | errorLogLightbox(step: any) { 42 | let featureUri = this.feature.uri; 43 | let comments = ''; 44 | if (step.comments) { 45 | $.each(step.comments, function (index, comment) { 46 | comments += (comments === '' ? '' : '
') + '
' + 47 | comment.value + '
'; 48 | }); 49 | } 50 | 51 | let template = '' + 52 | '' + 63 | ''; 64 | 65 | this.$uibModal.open({ 66 | template: template, 67 | size: 'lg' 68 | }); 69 | } 70 | 71 | htmlspecialchars(str: string) { 72 | return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); 73 | } 74 | 75 | formatTime(ns: number) { 76 | return Time.nsToString(ns); 77 | } 78 | } 79 | 80 | angular 81 | .module('ccrdb.pages') 82 | .controller('ScenariosController', ScenariosController); 83 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/scenarios.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IScenariosService { 6 | getFeature: (productId: string, reportId: string, featureId: string) => ng.IPromise; 7 | } 8 | 9 | export class ScenariosService implements IScenariosService { 10 | static $inject = ['$http']; 11 | 12 | constructor(private $http: ng.IHttpService) { 13 | } 14 | 15 | getFeature(productId: string, reportId: string, featureId: string) { 16 | return this.$http.get('rest/query/' + productId + '/' + reportId).then((response: any) => { 17 | for (let i = 0; i < response.data[0].report.features.length; i++) { 18 | if (response.data[0].report.features[i].id === featureId) { 19 | let feature = response.data[0].report.features[i]; 20 | 21 | feature.scenarios.forEach((scenario: any) => { 22 | if (scenario.result.failedStepCount) { 23 | scenario.status = 'failed'; 24 | } else if (scenario.result.skippedStepCount || scenario.result.unknownStepCount) { 25 | scenario.status = 'unknown'; 26 | } else { 27 | scenario.status = 'passed'; 28 | } 29 | }); 30 | 31 | return feature; 32 | } 33 | } 34 | }); 35 | } 36 | 37 | private success: (response: any) => {} = (response) => response.data; 38 | } 39 | 40 | angular.module('ccrdb.pages') 41 | .service('scenariosService', ccrdb.pages.ScenariosService); 42 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/statistics.controller.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 'use strict'; 3 | 4 | export class StatisticsController { 5 | private productId: string; 6 | private type: string; 7 | private limit: any; 8 | private reports: any[]; 9 | 10 | private chartObject: any = { 11 | options: { 12 | title: this.productId, 13 | vAxis: { title: 'Scenarios', titleTextStyle: { color: 'black' } }, 14 | hAxis: { title: 'Date', titleTextStyle: { color: 'black' } }, 15 | isStacked: true, 16 | colors: ['#5cb85c', '#f0ad4e', '#d9534f'] 17 | } 18 | }; 19 | 20 | static $inject = ['$stateParams', '$window', 'statisticsService', 'backButtonService', 'loadingBarService']; 21 | 22 | constructor(private $stateParams: ng.ui.IStateParamsService, 23 | private $window: ng.IWindowService, 24 | private statisticsService: ccrdb.pages.IStatisticsService, 25 | private backButtonService: ccrdb.core.BackButtonService, 26 | private loadingBarService: ccrdb.core.LoadingBarService) { 27 | this.getParams(); 28 | this.loadStatistics(); 29 | 30 | backButtonService.enabled = true; 31 | backButtonService.prevState = 'products'; 32 | backButtonService.prevParams = {}; 33 | } 34 | 35 | getParams() { 36 | this.productId = this.$stateParams['productId']; 37 | this.type = this.$stateParams['type']; 38 | this.chartObject.type = this.type; 39 | this.limit = this.$stateParams['limit']; 40 | if (this.limit === null) { 41 | this.limit = 'ALL'; 42 | } 43 | } 44 | 45 | loadStatistics() { 46 | this.loadingBarService.reqCount++; 47 | this.statisticsService.getStatistics(this.productId, this.limit).then((reports: any[]) => { 48 | this.loadingBarService.reqCount--; 49 | 50 | this.reports = reports; 51 | this.drawCharts(); 52 | }); 53 | } 54 | 55 | drawCharts() { 56 | this.chartObject.data = []; 57 | this.chartObject.data.push(['Date', 'Passed', 'Unknown', 'Failed']); 58 | this.reports.forEach((report: any) => { 59 | let row: any[] = []; 60 | let date = new Date(report.report.date.$date); 61 | 62 | let failedScenariosSum = 0; 63 | let unknownScenariosSum = 0; 64 | let passedScenariosSum = 0; 65 | 66 | report.report.features.forEach((feature: any) => { 67 | failedScenariosSum += this.getFailedScenarioCount(feature); 68 | unknownScenariosSum += this.getUnknownScenarioCount(feature); 69 | passedScenariosSum += this.getPassedScenarioCount(feature); 70 | }); 71 | 72 | row.push(date.toString()); 73 | row.push(); 74 | row.push(passedScenariosSum); 75 | row.push(unknownScenariosSum); 76 | row.push(failedScenariosSum); 77 | this.chartObject.data.push(row); 78 | }); 79 | } 80 | 81 | getFailedScenarioCount(feature: any) { 82 | let failedScenarios = 0; 83 | if (feature.scenarios !== undefined) { 84 | feature.scenarios.forEach(function (scenario: any) { 85 | if (scenario.result.failedStepCount) { 86 | failedScenarios++; 87 | } 88 | }); 89 | } 90 | return failedScenarios; 91 | } 92 | 93 | 94 | getUnknownScenarioCount(feature: any) { 95 | let unknownScenarios = 0; 96 | if (feature.scenarios !== undefined) { 97 | feature.scenarios.forEach(function (scenario: any) { 98 | if (scenario.result.unknownStepCount && !scenario.result.failedStepCount) { 99 | unknownScenarios++; 100 | } 101 | }); 102 | } 103 | return unknownScenarios; 104 | } 105 | 106 | getPassedScenarioCount(feature: any) { 107 | let passedScenarios = 0; 108 | if (feature.scenarios !== undefined) { 109 | feature.scenarios.forEach(function (scenario: any) { 110 | if (scenario.result.passedStepCount && !scenario.result.failedStepCount) { 111 | passedScenarios++; 112 | } 113 | }); 114 | } 115 | return passedScenarios; 116 | } 117 | } 118 | 119 | angular 120 | .module('ccrdb.pages') 121 | .controller('StatisticsController', StatisticsController); 122 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/pages/statistics.service.ts: -------------------------------------------------------------------------------- 1 | namespace ccrdb.pages { 2 | 3 | 'use strict'; 4 | 5 | export interface IStatisticsService { 6 | getStatistics: (productId: string, limit: number) => ng.IPromise; 7 | } 8 | 9 | export class StatisticsService implements IStatisticsService { 10 | static $inject = ['$http']; 11 | 12 | constructor(private $http: ng.IHttpService) { 13 | } 14 | 15 | getStatistics(productId: string, limit: any) { 16 | let query: string; 17 | if (limit !== null && !isNaN(limit) && limit !== '' && limit !== '0') { 18 | query = '?last=' + limit; 19 | } else { 20 | query = ''; 21 | } 22 | return this.$http.get('rest/query/' + productId + '/' + query).then((response: any) => { 23 | return response.data; 24 | }); 25 | } 26 | 27 | private success: (response: any) => {} = (response) => response.data; 28 | } 29 | 30 | angular.module('ccrdb.pages') 31 | .service('statisticsService', ccrdb.pages.StatisticsService); 32 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/ccrdb/util/Time.ts: -------------------------------------------------------------------------------- 1 | class Time { 2 | static nsToString(ns: number): string { 3 | let result: string; 4 | if (ns != null) { 5 | if (ns < 1000000000) { 6 | if (ns / 1000000 >= 0) { 7 | result = Math.round(ns / 1000000) + 'ms'; 8 | } else { 9 | result = '<1ms'; 10 | } 11 | } else { 12 | const days = ns / 86400000000000; 13 | result = (days >= 1 ? (days.toFixed() + ' Day' + (days >= 2 ? 's ' : ' ')) : '') 14 | + new Date(ns / 1000000).toISOString().substr(11, 8); 15 | } 16 | } else { 17 | result = ''; 18 | } 19 | 20 | return result; 21 | } 22 | } -------------------------------------------------------------------------------- /cucumber-report-web/src/main/typescript/import.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /cucumber-report-web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "noImplicitAny": true, 6 | "rootDir": "src/main/typescript/", 7 | "sourceMap": true 8 | }, 9 | "exclude": [ 10 | "node_modules" 11 | ] 12 | } -------------------------------------------------------------------------------- /cucumber-report-web/tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "angularjs/angular.d.ts": { 9 | "commit": "02b4b5c8e0f56cc576a3480dea362173ba7e277b" 10 | }, 11 | "angular-ui-router/angular-ui-router.d.ts": { 12 | "commit": "02b4b5c8e0f56cc576a3480dea362173ba7e277b" 13 | }, 14 | "angular-ui-bootstrap/angular-ui-bootstrap.d.ts": { 15 | "commit": "02b4b5c8e0f56cc576a3480dea362173ba7e277b" 16 | }, 17 | "jquery/jquery.d.ts": { 18 | "commit": "02b4b5c8e0f56cc576a3480dea362173ba7e277b" 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /cucumber-report-web/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "indent": [ 9 | true, 10 | "spaces" 11 | ], 12 | "no-duplicate-variable": true, 13 | "no-eval": true, 14 | "no-internal-module": true, 15 | "no-trailing-whitespace": true, 16 | "no-var-keyword": true, 17 | "one-line": [ 18 | true, 19 | "check-open-brace", 20 | "check-whitespace" 21 | ], 22 | "quotemark": [ 23 | true, 24 | "single", 25 | "avoid-escape" 26 | ], 27 | "semicolon": [ 28 | true, 29 | "always" 30 | ], 31 | "triple-equals": [ 32 | true, 33 | "allow-null-check" 34 | ], 35 | "typedef-whitespace": [ 36 | true, 37 | { 38 | "call-signature": "nospace", 39 | "index-signature": "nospace", 40 | "parameter": "nospace", 41 | "property-declaration": "nospace", 42 | "variable-declaration": "nospace" 43 | } 44 | ], 45 | "variable-name": [ 46 | true, 47 | "ban-keywords" 48 | ], 49 | "whitespace": [ 50 | true, 51 | "check-branch", 52 | "check-decl", 53 | "check-operator", 54 | "check-separator", 55 | "check-type" 56 | ] 57 | } 58 | } -------------------------------------------------------------------------------- /cucumber-report-web/typings/tsd.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | -------------------------------------------------------------------------------- /docs/Charts.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/docs/Charts.PNG -------------------------------------------------------------------------------- /docs/Feature-based Reports.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/docs/Feature-based Reports.PNG -------------------------------------------------------------------------------- /docs/Selected Step.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/docs/Selected Step.PNG -------------------------------------------------------------------------------- /docs/Top 5 and Complete List.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/docs/Top 5 and Complete List.PNG -------------------------------------------------------------------------------- /docs/Version-based Reports.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/docs/Version-based Reports.PNG -------------------------------------------------------------------------------- /docs/different execution dates of a version.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/docs/different execution dates of a version.PNG -------------------------------------------------------------------------------- /silk-bdd-result-plugin/createJarWithDependencies.sh: -------------------------------------------------------------------------------- 1 | mvn clean compile assembly:single -------------------------------------------------------------------------------- /silk-bdd-result-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | Porsche Informatik Silk Central BDD Result Plugin 5 | silk-bdd-result-plugin 6 | jar 7 | 8 | 9 | at.porscheinformatik.cucumber-report-db 10 | cucumber-report-db-parent 11 | 1.2.0.BUILD-SNAPSHOT 12 | .. 13 | 14 | 15 | 16 | 17 | commons-io 18 | commons-io 19 | 20 | 21 | 22 | ch.qos.logback 23 | logback-classic 24 | 25 | 26 | 27 | com.sun.jersey 28 | jersey-client 29 | 30 | 31 | 32 | junit 33 | junit 34 | 35 | 36 | 37 | 38 | 39 | 40 | maven-assembly-plugin 41 | 42 | 43 | 44 | at.porscheinformatik.common.utils.silkplugin.gui.ConfigurationGUI 45 | at.porscheinformatik.common.utils.silkplugin.gui 46 | 47 | 48 | silk-bdd-result-plugin 49 | 50 | jar-with-dependencies 51 | 52 | 53 | 54 | 55 | make-assembly 56 | package 57 | 58 | attached 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/SilkSynchronizer.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin; 2 | 3 | import static org.junit.Assert.fail; 4 | 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.Properties; 10 | import java.util.regex.Pattern; 11 | 12 | import org.junit.After; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | import com.sun.jersey.api.client.Client; 19 | import com.sun.jersey.api.client.UniformInterfaceException; 20 | import com.sun.jersey.api.client.WebResource; 21 | import com.sun.jersey.api.client.config.DefaultClientConfig; 22 | import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; 23 | 24 | public class SilkSynchronizer 25 | { 26 | private static final String CONFIG_FILE = "config.properties"; 27 | 28 | private static final String DEFAULT_SILK_IDENTIFIER = "#sctm_execdef_id"; 29 | private static final String DEFAULT_SILK_VERSION = "#sctm_version"; 30 | private static final String DEFAULT_SILK_EXECUTION_DEF_NAME = "#sctm_execdef_name"; 31 | public static final String SERVER_BASE_URL = "server.base.url"; 32 | public static final String REPORT_OBSOLETE_LIMIT_PROPERTY = "report.obsolete-limit"; 33 | public static final String COLLECTION_NAMING_REGEX_CONVENTION = "collection.naming-convention"; 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(SilkSynchronizer.class); 36 | public static final String SERVER_PASSWORD = "server.password"; 37 | public static final String SERVER_USERNAME = "server.username"; 38 | 39 | private Properties config; 40 | private WebResource restResource; 41 | 42 | @Before 43 | public void preCondition() 44 | { 45 | LOGGER.info("Starting Turntable-Silk-BDD-Plugin"); 46 | 47 | loadConfigurations(CONFIG_FILE); 48 | 49 | String baseUrl = config.getProperty(SERVER_BASE_URL); 50 | String username = config.getProperty(SERVER_USERNAME); 51 | String password = config.getProperty(SERVER_PASSWORD); 52 | 53 | Client client = Client.create(new DefaultClientConfig()); 54 | restResource = client.resource(baseUrl); 55 | restResource.addFilter(new HTTPBasicAuthFilter(username, password)); 56 | } 57 | 58 | public Properties loadConfigurations(String file) 59 | { 60 | config = new Properties(); 61 | try 62 | { 63 | LOGGER.info("Loading properties from URL \"" + getClass().getResource("/" + file) + "\""); 64 | InputStream in = new FileInputStream(new File(System.getProperty("user.home"), file)); 65 | config.load(in); 66 | in.close(); 67 | } 68 | catch (IOException e) 69 | { 70 | LOGGER.error("Error loading properties", e); 71 | fail("[Error] Could not read the configuration file!"); 72 | } 73 | return config; 74 | } 75 | 76 | private String getCollectionName() 77 | { 78 | final String version = System.getProperty(DEFAULT_SILK_VERSION); 79 | final String product = System.getProperty(DEFAULT_SILK_EXECUTION_DEF_NAME); 80 | final String collection = String.format("%s_%s", product.split("_", 2)[0], version); 81 | final String namingConvention = config.getProperty(COLLECTION_NAMING_REGEX_CONVENTION); 82 | 83 | if (!Pattern.matches(namingConvention, collection)) 84 | { 85 | final String error = 86 | String.format("[ERROR] Invalid naming convention for the given collection (current: %s, expected: %s)", 87 | collection, namingConvention); 88 | LOGGER.error(error); 89 | fail(error); 90 | } 91 | 92 | return collection; 93 | } 94 | 95 | @After 96 | public void postCondition() 97 | { 98 | LOGGER.info("Finished SilkBDDPlugin"); 99 | } 100 | 101 | @Test 102 | public void run() 103 | { 104 | String executionId = System.getProperty(DEFAULT_SILK_IDENTIFIER); 105 | String obsoleteLimitInDays = config.getProperty(REPORT_OBSOLETE_LIMIT_PROPERTY); 106 | 107 | try 108 | { 109 | String result = restResource.path("rest").path("reports").path(getCollectionName()).path(executionId) 110 | .queryParam("obsoleteLimitInDays", obsoleteLimitInDays).get(String.class); 111 | LOGGER.info("Test passed: "+ result); 112 | } 113 | catch (UniformInterfaceException e) 114 | { 115 | String errormessage = e.getResponse().getEntity(String.class); 116 | fail(errormessage); 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/DateDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | public class DateDTO 4 | { 5 | private String $date; 6 | 7 | public String get$date() 8 | { 9 | return $date; 10 | } 11 | 12 | public void set$date(String $date) 13 | { 14 | this.$date = $date; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/EmbeddingDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | public class EmbeddingDTO 4 | { 5 | private String mime_type; 6 | private String url; 7 | 8 | public String getMime_type() 9 | { 10 | return mime_type; 11 | } 12 | 13 | public void setMime_type(String mime_type) 14 | { 15 | this.mime_type = mime_type; 16 | } 17 | 18 | public String getUrl() 19 | { 20 | return url; 21 | } 22 | 23 | public void setUrl(String url) 24 | { 25 | this.url = url; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/FeatureDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | import java.util.List; 4 | 5 | public class FeatureDTO 6 | { 7 | private List tags; 8 | private String id; 9 | private ResultDTO result; 10 | private String description; 11 | private String keyword; 12 | private String name; 13 | private Integer line; 14 | private String uri; 15 | private List scenarios; 16 | 17 | public List getTags() 18 | { 19 | return tags; 20 | } 21 | 22 | public void setTags(List tags) 23 | { 24 | this.tags = tags; 25 | } 26 | 27 | public String getId() 28 | { 29 | return id; 30 | } 31 | 32 | public void setId(String id) 33 | { 34 | this.id = id; 35 | } 36 | 37 | public ResultDTO getResult() 38 | { 39 | return result; 40 | } 41 | 42 | public void setResult(ResultDTO result) 43 | { 44 | this.result = result; 45 | } 46 | 47 | public String getDescription() 48 | { 49 | return description; 50 | } 51 | 52 | public void setDescription(String description) 53 | { 54 | this.description = description; 55 | } 56 | 57 | public String getKeyword() 58 | { 59 | return keyword; 60 | } 61 | 62 | public void setKeyword(String keyword) 63 | { 64 | this.keyword = keyword; 65 | } 66 | 67 | public String getName() 68 | { 69 | return name; 70 | } 71 | 72 | public void setName(String name) 73 | { 74 | this.name = name; 75 | } 76 | 77 | public Integer getLine() 78 | { 79 | return line; 80 | } 81 | 82 | public void setLine(Integer line) 83 | { 84 | this.line = line; 85 | } 86 | 87 | public String getUri() 88 | { 89 | return uri; 90 | } 91 | 92 | public void setUri(String uri) 93 | { 94 | this.uri = uri; 95 | } 96 | 97 | public List getScenarios() 98 | { 99 | return scenarios; 100 | } 101 | 102 | public void setScenarios(List scenarios) 103 | { 104 | this.scenarios = scenarios; 105 | } 106 | 107 | public ScenarioDTO getScenarioById(String id) 108 | { 109 | for (ScenarioDTO scenario : scenarios) 110 | { 111 | if (scenario.getId().equals(id)) 112 | { 113 | return scenario; 114 | } 115 | } 116 | return null; 117 | } 118 | 119 | public ScenarioDTO getScenarioByTag(String tag) 120 | { 121 | for (ScenarioDTO scenario : scenarios) 122 | { 123 | if (scenario.getTags() != null) 124 | { 125 | for(TagDTO currentTag : scenario.getTags()) 126 | { 127 | if (currentTag.getName().equals(tag)) 128 | { 129 | return scenario; 130 | } 131 | } 132 | } 133 | } 134 | return null; 135 | } 136 | } -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/ReportDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | import java.util.Calendar; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.TimeZone; 7 | 8 | /** 9 | * @author Stefan Mayer (yms) 10 | */ 11 | public class ReportDTO 12 | { 13 | private List features; 14 | private DateDTO date; 15 | 16 | public List getFeatures() 17 | { 18 | return features; 19 | } 20 | 21 | public void setFeatures(List features) 22 | { 23 | this.features = features; 24 | } 25 | 26 | public Date getDate() 27 | { 28 | return getCalendar().getTime(); 29 | } 30 | 31 | public Calendar getCalendar() 32 | { 33 | Calendar cal = javax.xml.bind.DatatypeConverter.parseDateTime(date.get$date()); 34 | cal.setTimeZone(TimeZone.getTimeZone("GMT+02:00")); 35 | return cal; 36 | } 37 | 38 | public DateDTO getDateDTO() 39 | { 40 | return date; 41 | } 42 | 43 | public void setDateDTO(DateDTO date) 44 | { 45 | this.date = date; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/ResultDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class ResultDTO 6 | { 7 | private BigInteger duration; 8 | private Integer stepCount; 9 | private Integer scenarioCount; 10 | private Integer passedStepCount; 11 | private Integer skippedStepCount; 12 | private Integer failedStepCount; 13 | 14 | public BigInteger getDuration() 15 | { 16 | return duration; 17 | } 18 | 19 | public void setDuration(BigInteger duration) 20 | { 21 | this.duration = duration; 22 | } 23 | 24 | public Integer getStepCount() 25 | { 26 | return stepCount; 27 | } 28 | 29 | public void setStepCount(Integer stepCount) 30 | { 31 | this.stepCount = stepCount; 32 | } 33 | 34 | public Integer getScenarioCount() 35 | { 36 | return scenarioCount; 37 | } 38 | 39 | public void setScenarioCount(Integer scenarioCount) 40 | { 41 | this.scenarioCount = scenarioCount; 42 | } 43 | 44 | public Integer getPassedStepCount() 45 | { 46 | return passedStepCount; 47 | } 48 | 49 | public void setPassedStepCount(Integer passedStepCount) 50 | { 51 | this.passedStepCount = passedStepCount; 52 | } 53 | 54 | public Integer getSkippedStepCount() 55 | { 56 | return skippedStepCount; 57 | } 58 | 59 | public void setSkippedStepCount(Integer skippedStepCount) 60 | { 61 | this.skippedStepCount = skippedStepCount; 62 | } 63 | 64 | public Integer getFailedStepCount() 65 | { 66 | return failedStepCount; 67 | } 68 | 69 | public void setFailedStepCount(Integer failedStepCount) 70 | { 71 | this.failedStepCount = failedStepCount; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/ScenarioDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | import java.util.List; 4 | 5 | public class ScenarioDTO 6 | { 7 | private String id; 8 | private List tags; 9 | private ResultDTO result; 10 | private String description; 11 | private String name; 12 | private String keyword; 13 | private Integer line; 14 | private List steps; 15 | private String type; 16 | 17 | public List getTags() 18 | { 19 | return tags; 20 | } 21 | 22 | public void setTags(List tags) 23 | { 24 | this.tags = tags; 25 | } 26 | 27 | public String getId() 28 | { 29 | return id; 30 | } 31 | 32 | public void setId(String id) 33 | { 34 | this.id = id; 35 | } 36 | 37 | public ResultDTO getResult() 38 | { 39 | return result; 40 | } 41 | 42 | public void setResult(ResultDTO result) 43 | { 44 | this.result = result; 45 | } 46 | 47 | public String getDescription() 48 | { 49 | return description; 50 | } 51 | 52 | public void setDescription(String description) 53 | { 54 | this.description = description; 55 | } 56 | 57 | public String getName() 58 | { 59 | return name; 60 | } 61 | 62 | public void setName(String name) 63 | { 64 | this.name = name; 65 | } 66 | 67 | public String getKeyword() 68 | { 69 | return keyword; 70 | } 71 | 72 | public void setKeyword(String keyword) 73 | { 74 | this.keyword = keyword; 75 | } 76 | 77 | public Integer getLine() 78 | { 79 | return line; 80 | } 81 | 82 | public void setLine(Integer line) 83 | { 84 | this.line = line; 85 | } 86 | 87 | public List getSteps() 88 | { 89 | return steps; 90 | } 91 | 92 | public void setSteps(List steps) 93 | { 94 | this.steps = steps; 95 | } 96 | 97 | public String getType() 98 | { 99 | return type; 100 | } 101 | 102 | public void setType(String type) 103 | { 104 | this.type = type; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/StepDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | import java.util.List; 4 | 5 | public class StepDTO 6 | { 7 | private StepResultDTO result; 8 | private List embeddings; 9 | private String name; 10 | private String keyword; 11 | private Integer line; 12 | 13 | public StepResultDTO getResult() 14 | { 15 | return result; 16 | } 17 | 18 | public void setResult(StepResultDTO result) 19 | { 20 | this.result = result; 21 | } 22 | 23 | public List getEmbeddings() 24 | { 25 | return embeddings; 26 | } 27 | 28 | public void setEmbeddings(List embeddings) 29 | { 30 | this.embeddings = embeddings; 31 | } 32 | 33 | public String getName() 34 | { 35 | return name; 36 | } 37 | 38 | public void setName(String name) 39 | { 40 | this.name = name; 41 | } 42 | 43 | public String getKeyword() 44 | { 45 | return keyword; 46 | } 47 | 48 | public void setKeyword(String keyword) 49 | { 50 | this.keyword = keyword; 51 | } 52 | 53 | public Integer getLine() 54 | { 55 | return line; 56 | } 57 | 58 | public void setLine(Integer line) 59 | { 60 | this.line = line; 61 | } 62 | } -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/StepResultDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | import java.math.BigInteger; 4 | 5 | public class StepResultDTO 6 | { 7 | private BigInteger duration; 8 | private String status; 9 | private String error_message; 10 | 11 | public BigInteger getDuration() 12 | { 13 | return duration; 14 | } 15 | 16 | public void setDuration(BigInteger duration) 17 | { 18 | this.duration = duration; 19 | } 20 | 21 | public String getStatus() 22 | { 23 | return status; 24 | } 25 | 26 | public void setStatus(String status) 27 | { 28 | this.status = status; 29 | } 30 | 31 | public String getError_message() 32 | { 33 | return error_message; 34 | } 35 | 36 | public void setError_message(String error_message) 37 | { 38 | this.error_message = error_message; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/java/at/porscheinformatik/common/utils/silkplugin/dto/TagDTO.java: -------------------------------------------------------------------------------- 1 | package at.porscheinformatik.common.utils.silkplugin.dto; 2 | 3 | public class TagDTO 4 | { 5 | private String name; 6 | private Long line; 7 | 8 | public String getName() 9 | { 10 | return name; 11 | } 12 | 13 | public void setName(String name) 14 | { 15 | this.name = name; 16 | } 17 | 18 | public Long getLine() 19 | { 20 | return line; 21 | } 22 | 23 | public void setLine(Long line) 24 | { 25 | this.line = line; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/resources/config.properties: -------------------------------------------------------------------------------- 1 | ##################################################################################### 2 | # # 3 | # Configurations for Mongo DBMS # 4 | # # 5 | ##################################################################################### 6 | server.base.url=http://localhost:8081 7 | 8 | # %d will be replaced by the given Silk execution id 9 | silk.execution-id-name = @SILK_ID_%s 10 | report.obsolete-limit = 2 11 | collection.naming-convention = [A-Z0-9]*\\_[A-Z0-9\\.\\-]* -------------------------------------------------------------------------------- /silk-bdd-result-plugin/src/main/resources/poi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/porscheinformatik/cucumber-report-db/4c80efcc6767fd12e29ac3cbc2f367908a17901a/silk-bdd-result-plugin/src/main/resources/poi.jpg -------------------------------------------------------------------------------- /system.properties: -------------------------------------------------------------------------------- 1 | java.runtime.version=1.7 --------------------------------------------------------------------------------