From a17c0a14bfac46e0b978a7cce22cc52ad1103cfa Mon Sep 17 00:00:00 2001 From: bvo2002 Date: Tue, 17 Oct 2017 14:55:19 +0300 Subject: [PATCH 1/4] exprot to csv --- allure-generator/build.gradle | 1 + .../allure/category/CategoriesPlugin.java | 14 ++- .../qameta/allure/csv/CsvExportCategory.java | 86 +++++++++++++++++++ .../io/qameta/allure/csv/CsvExportSuite.java | 69 +++++++++++++++ .../qameta/allure/csv/CsvMappingStrategy.java | 36 ++++++++ .../io/qameta/allure/suites/SuitesPlugin.java | 14 ++- .../testresult-tree/TestResultTreeView.js | 6 +- .../tree-view-container/TreeViewContainer.hbs | 4 + .../tree-view-container/TreeViewContainer.js | 3 +- .../javascript/components/tree/TreeView.js | 2 +- .../javascript/components/tree/styles.scss | 4 +- .../javascript/layouts/tree/TreeLayout.js | 4 +- .../javascript/plugins/tab-category/index.js | 3 +- .../javascript/plugins/tab-suites/index.js | 3 +- .../src/main/javascript/translations/en.json | 1 + allure-plugin-api/build.gradle | 3 + .../java/io/qameta/allure/CsvExporter.java | 37 ++++++++ .../allure/util/CsvMappingStrategy.java | 35 ++++++++ build.gradle | 1 + 19 files changed, 313 insertions(+), 13 deletions(-) create mode 100644 allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java create mode 100644 allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java create mode 100644 allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java create mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java create mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java diff --git a/allure-generator/build.gradle b/allure-generator/build.gradle index 314e4763d..ce55b834d 100644 --- a/allure-generator/build.gradle +++ b/allure-generator/build.gradle @@ -60,6 +60,7 @@ dependencies { compile('com.fasterxml.jackson.dataformat:jackson-dataformat-yaml') compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml' compile('org.apache.httpcomponents:httpclient') + compile('com.opencsv:opencsv') compileOnly('org.projectlombok:lombok') diff --git a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java index 2428cfaa7..ac413e44d 100644 --- a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java +++ b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java @@ -2,12 +2,14 @@ import com.fasterxml.jackson.core.type.TypeReference; import io.qameta.allure.Aggregator; +import io.qameta.allure.CsvExporter; import io.qameta.allure.Reader; import io.qameta.allure.Widget; import io.qameta.allure.context.JacksonContext; import io.qameta.allure.core.Configuration; import io.qameta.allure.core.LaunchResults; import io.qameta.allure.core.ResultsVisitor; +import io.qameta.allure.csv.CsvExportCategory; import io.qameta.allure.entity.Status; import io.qameta.allure.entity.TestResult; import io.qameta.allure.tree.DefaultTreeLayer; @@ -45,7 +47,7 @@ * @since 2.0 */ @SuppressWarnings("PMD.ExcessiveImports") -public class CategoriesPlugin implements Aggregator, Reader, Widget { +public class CategoriesPlugin extends CsvExporter implements Aggregator, Reader, Widget { public static final String CATEGORIES_BLOCK_NAME = "categories"; @@ -55,6 +57,8 @@ public class CategoriesPlugin implements Aggregator, Reader, Widget { private static final String CATEGORIES_FILE_NAME = "categories.json"; + public static final String CSV_FILE_NAME = "categories.csv"; + //@formatter:off private static final TypeReference> CATEGORIES_TYPE = new TypeReference>() {}; @@ -85,10 +89,11 @@ public void aggregate(final Configuration configuration, final JacksonContext jacksonContext = configuration.requireContext(JacksonContext.class); final Path dataFolder = Files.createDirectories(outputDirectory.resolve("data")); - final Path dataFile = dataFolder.resolve("categories.json"); + final Path dataFile = dataFolder.resolve(CATEGORIES_FILE_NAME); try (OutputStream os = Files.newOutputStream(dataFile)) { jacksonContext.getValue().writeValue(os, getData(launchesResults)); } + createCsvExportFile(launchesResults, dataFolder, CSV_FILE_NAME, CsvExportCategory.class); } @Override @@ -184,4 +189,9 @@ protected TreeWidgetItem toWidgetItem(final TestResultTreeGroup group) { .setName(group.getName()) .setStatistic(calculateStatisticByLeafs(group)); } + + @Override + public List getCollectionToCsvExport(List launchesResults) { + return null; + } } diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java new file mode 100644 index 000000000..cb11d159c --- /dev/null +++ b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java @@ -0,0 +1,86 @@ +package io.qameta.allure.csv; + +import com.opencsv.bean.CsvBindByName; +import com.opencsv.bean.CsvBindByPosition; +import io.qameta.allure.entity.TestResult; + +import java.io.Serializable; + +/** + * Class contains the information for the category csv export. + * + */ +public class CsvExportCategory implements Serializable { + + @CsvBindByName(column = "Category") + @CsvBindByPosition(position = 0) + private String name; + + @CsvBindByName(column = "FAILED") + @CsvBindByPosition(position = 1) + private String failed; + + @CsvBindByName(column = "BROKEN") + @CsvBindByPosition(position = 2) + private String broken; + + @CsvBindByName(column = "PASSED") + @CsvBindByPosition(position = 3) + private String passed; + + @CsvBindByName(column = "SKIPPED") + @CsvBindByPosition(position = 4) + private String skipped; + + @CsvBindByName(column = "UNKNOWN") + @CsvBindByPosition(position = 5) + private String unknown; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFailed() { + return failed; + } + + public void setFailed(String failed) { + this.failed = failed; + } + + public String getBroken() { + return broken; + } + + public void setBroken(String broken) { + this.broken = broken; + } + + public String getPassed() { + return passed; + } + + public void setPassed(String passed) { + this.passed = passed; + } + + public String getSkipped() { + return skipped; + } + + public void setSkipped(String skipped) { + this.skipped = skipped; + } + + public String getUnknown() { + return unknown; + } + + public void setUnknown(String unknown) { + this.unknown = unknown; + } +} diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java new file mode 100644 index 000000000..b03d278ff --- /dev/null +++ b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java @@ -0,0 +1,69 @@ +package io.qameta.allure.csv; + +import com.opencsv.bean.CsvBindByName; +import com.opencsv.bean.CsvBindByPosition; +import io.qameta.allure.entity.TestResult; + +import java.io.Serializable; + +/** + * Class contains the information for the suites csv export. + * + */ +public class CsvExportSuite implements Serializable { + + @CsvBindByName(column = "Status") + @CsvBindByPosition(position = 0) + private String status; + + @CsvBindByName(column = "Name") + @CsvBindByPosition(position = 1) + private String name; + + @CsvBindByName(column = "Duration in ms") + @CsvBindByPosition(position = 2) + private String duration; + + @CsvBindByName(column = "Description") + @CsvBindByPosition(position = 3) + private String message; + + public CsvExportSuite(TestResult result) { + this.status = result.getStatus().value(); + this.name = result.getFullName(); + this.duration = "" + result.getTime().getDuration(); + this.message = result.getDescription(); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getDuration() { + return duration; + } + + public void setDuration(String duration) { + this.duration = duration; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java b/allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java new file mode 100644 index 000000000..8a86a0305 --- /dev/null +++ b/allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java @@ -0,0 +1,36 @@ +package io.qameta.allure.csv; + +import com.opencsv.bean.BeanField; +import com.opencsv.bean.ColumnPositionMappingStrategy; +import com.opencsv.bean.CsvBindByName; +import org.apache.commons.lang3.StringUtils; + +public class CsvMappingStrategy extends ColumnPositionMappingStrategy { + + @Override + public String[] generateHeader() { + final int numColumns = findMaxFieldIndex(); + if (!isAnnotationDriven() || numColumns == -1) { + return super.generateHeader(); + } + + header = new String[numColumns + 1]; + + BeanField beanField; + for (int i = 0; i <= numColumns; i++) { + beanField = findField(i); + String columnHeaderName = extractHeaderName(beanField); + header[i] = columnHeaderName; + } + return header; + } + + private String extractHeaderName(final BeanField beanField) { + if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { + return StringUtils.EMPTY; + } + + final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0]; + return bindByNameAnnotation.column(); + } +} diff --git a/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java b/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java index 8ea293ed3..9df66e5a4 100644 --- a/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java +++ b/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java @@ -1,10 +1,12 @@ package io.qameta.allure.suites; import io.qameta.allure.Aggregator; +import io.qameta.allure.CsvExporter; import io.qameta.allure.Widget; import io.qameta.allure.context.JacksonContext; import io.qameta.allure.core.Configuration; import io.qameta.allure.core.LaunchResults; +import io.qameta.allure.csv.CsvExportSuite; import io.qameta.allure.entity.TestResult; import io.qameta.allure.tree.TestResultTree; import io.qameta.allure.tree.TestResultTreeGroup; @@ -34,7 +36,9 @@ * * @since 2.0 */ -public class SuitesPlugin implements Aggregator, Widget { +public class SuitesPlugin extends CsvExporter implements Aggregator, Widget { + + public static final String CSV_FILE_NAME = "suites.csv"; @Override public void aggregate(final Configuration configuration, @@ -46,6 +50,7 @@ public void aggregate(final Configuration configuration, try (OutputStream os = Files.newOutputStream(dataFile)) { jacksonContext.getValue().writeValue(os, getData(launchesResults)); } + createCsvExportFile(launchesResults, dataFolder, CSV_FILE_NAME,CsvExportSuite.class); } @SuppressWarnings("PMD.DefaultPackage") @@ -90,4 +95,11 @@ protected TreeWidgetItem toWidgetItem(final TestResultTreeGroup group) { .setName(group.getName()) .setStatistic(calculateStatisticByLeafs(group)); } + + @Override + public List getCollectionToCsvExport(List launchesResults) { + return launchesResults.stream() + .flatMap(launch -> launch.getResults().stream()) + .map(CsvExportSuite::new).collect(Collectors.toList()); + } } diff --git a/allure-generator/src/main/javascript/components/testresult-tree/TestResultTreeView.js b/allure-generator/src/main/javascript/components/testresult-tree/TestResultTreeView.js index ed3b36328..24132a454 100644 --- a/allure-generator/src/main/javascript/components/testresult-tree/TestResultTreeView.js +++ b/allure-generator/src/main/javascript/components/testresult-tree/TestResultTreeView.js @@ -9,8 +9,9 @@ import ErrorSplashView from '../error-splash/ErrorSplashView'; @className('side-by-side') class TestResultTreeView extends SideBySideView { - initialize({tree, routeState}) { + initialize({tree, routeState, csvUrl}) { super.initialize(); + this.csvUrl = csvUrl; this.tree = tree; this.routeState = routeState; this.listenTo(this.routeState, 'change:treeNode', (_, treeNode) => this.showLeaf(treeNode)); @@ -39,7 +40,8 @@ class TestResultTreeView extends SideBySideView { routeState: this.routeState, treeSorters: [], tabName: tabName, - baseUrl: baseUrl + baseUrl: baseUrl, + csvUrl: this.csvUrl }); this.showChildView('left', left); } diff --git a/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.hbs b/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.hbs index 6de4e1000..b3e980590 100644 --- a/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.hbs +++ b/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.hbs @@ -9,6 +9,10 @@
+ {{#if csvUrl}} + + {{/if}} diff --git a/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js b/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js index 65dade7c5..d6fd59021 100644 --- a/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js +++ b/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js @@ -20,10 +20,11 @@ import {getSettingsForTreePlugin} from '../../utils/settingsFactory'; class TreeViewContainer extends View { template = template; - initialize({routeState, state = new Model(), tabName, baseUrl, settings = getSettingsForTreePlugin(baseUrl)}) { + initialize({routeState, state = new Model(), tabName, baseUrl, csvUrl=null, settings = getSettingsForTreePlugin(baseUrl)}) { this.state = state; this.routeState = routeState; this.baseUrl = baseUrl; + this.csvUrl = csvUrl; this.tabName = tabName; this.listenTo(this.routeState, 'change:testResultTab', this.render); diff --git a/allure-generator/src/main/javascript/components/tree/TreeView.js b/allure-generator/src/main/javascript/components/tree/TreeView.js index 3ceba5bfa..6ca8a2f59 100644 --- a/allure-generator/src/main/javascript/components/tree/TreeView.js +++ b/allure-generator/src/main/javascript/components/tree/TreeView.js @@ -181,7 +181,7 @@ class TreeView extends View { uid: this.collection.uid, tabName: this.tabName, items: this.collection.toJSON(), - testResultTab: this.routeState.get('testResultTab') || '', + testResultTab: this.routeState.get('testResultTab') || '' }; } } diff --git a/allure-generator/src/main/javascript/components/tree/styles.scss b/allure-generator/src/main/javascript/components/tree/styles.scss index 3f2e2b927..5a8d9dd46 100644 --- a/allure-generator/src/main/javascript/components/tree/styles.scss +++ b/allure-generator/src/main/javascript/components/tree/styles.scss @@ -62,7 +62,7 @@ } } } - &__info { + &__info, &__download { color: $text-muted-color; padding: 7px; } @@ -101,4 +101,4 @@ [dir=ltr] .node__expanded > .node__title > .node__arrow { transform: rotate(90deg); -} \ No newline at end of file +} diff --git a/allure-generator/src/main/javascript/layouts/tree/TreeLayout.js b/allure-generator/src/main/javascript/layouts/tree/TreeLayout.js index 3e500684a..153771bff 100644 --- a/allure-generator/src/main/javascript/layouts/tree/TreeLayout.js +++ b/allure-generator/src/main/javascript/layouts/tree/TreeLayout.js @@ -17,8 +17,8 @@ export default class TreeLayout extends AppLayout { } getContentView() { - const {baseUrl, tabName} = this.options; - return new TestResultTreeView({tree: this.tree, routeState: this.routeState, tabName, baseUrl}); + const {baseUrl, tabName, csvUrl} = this.options; + return new TestResultTreeView({tree: this.tree, routeState: this.routeState, tabName, baseUrl, csvUrl}); } onViewReady() { diff --git a/allure-generator/src/main/javascript/plugins/tab-category/index.js b/allure-generator/src/main/javascript/plugins/tab-category/index.js index 090e12f07..845177f89 100644 --- a/allure-generator/src/main/javascript/plugins/tab-category/index.js +++ b/allure-generator/src/main/javascript/plugins/tab-category/index.js @@ -7,6 +7,7 @@ allure.api.addTab('categories', { testResultTab, tabName: 'tab.categories.name', baseUrl: 'categories', - url: 'data/categories.json' + url: 'data/categories.json', + csvUrl: 'data/categories.csv' }) }); \ No newline at end of file diff --git a/allure-generator/src/main/javascript/plugins/tab-suites/index.js b/allure-generator/src/main/javascript/plugins/tab-suites/index.js index 5fa614fa7..4a4b76762 100644 --- a/allure-generator/src/main/javascript/plugins/tab-suites/index.js +++ b/allure-generator/src/main/javascript/plugins/tab-suites/index.js @@ -8,6 +8,7 @@ allure.api.addTab('suites', { testResultTab, tabName: 'tab.suites.name', baseUrl: 'suites', - url: 'data/suites.json' + url: 'data/suites.json', + csvUrl: 'data/suites.csv' }) }); diff --git a/allure-generator/src/main/javascript/translations/en.json b/allure-generator/src/main/javascript/translations/en.json index 5eefc95b3..365607138 100644 --- a/allure-generator/src/main/javascript/translations/en.json +++ b/allure-generator/src/main/javascript/translations/en.json @@ -151,6 +151,7 @@ "tree": { "filter": "Filter by status", "groups": "Toggle groups info", + "download": "Download CSV", "empty": "There are no items", "time": { "total" : { diff --git a/allure-plugin-api/build.gradle b/allure-plugin-api/build.gradle index 1d65e65c2..5ee3cff31 100644 --- a/allure-plugin-api/build.gradle +++ b/allure-plugin-api/build.gradle @@ -12,6 +12,9 @@ dependencies { compile('org.freemarker:freemarker') compile('org.slf4j:slf4j-api') compileOnly('org.projectlombok:lombok') + + compile('com.opencsv:opencsv') + testCompile('junit:junit') testCompile('org.assertj:assertj-core') testCompile('org.slf4j:slf4j-simple') diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java b/allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java new file mode 100644 index 000000000..b38f03f43 --- /dev/null +++ b/allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java @@ -0,0 +1,37 @@ +package io.qameta.allure; + +import com.opencsv.bean.StatefulBeanToCsv; +import com.opencsv.bean.StatefulBeanToCsvBuilder; +import io.qameta.allure.core.LaunchResults; +import io.qameta.allure.util.CsvMappingStrategy; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Path; +import java.util.List; + +/** + * Csv exporter extension. Can be used to process results in csv file + * + * @since 2.0 + */ +public abstract class CsvExporter implements Extension { + + public void createCsvExportFile(List launchesResults, Path dataFolder, String fileName, Class type) throws IOException { + final Path csv = dataFolder.resolve(fileName); + try(Writer writer = new FileWriter(csv.toFile())) { + StatefulBeanToCsvBuilder builder = new StatefulBeanToCsvBuilder<>(writer); + CsvMappingStrategy mappingStrategy = new CsvMappingStrategy<>(); + mappingStrategy.setType(type); + StatefulBeanToCsv beanWriter = builder.withMappingStrategy(mappingStrategy).build(); + try { + beanWriter.write(getCollectionToCsvExport(launchesResults)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public abstract List getCollectionToCsvExport(List launchesResults); +} diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java b/allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java new file mode 100644 index 000000000..0ea3ad452 --- /dev/null +++ b/allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java @@ -0,0 +1,35 @@ +package io.qameta.allure.util; + +import com.opencsv.bean.BeanField; +import com.opencsv.bean.ColumnPositionMappingStrategy; +import com.opencsv.bean.CsvBindByName; + +public class CsvMappingStrategy extends ColumnPositionMappingStrategy { + + @Override + public String[] generateHeader() { + final int numColumns = findMaxFieldIndex(); + if (!isAnnotationDriven() || numColumns == -1) { + return super.generateHeader(); + } + + header = new String[numColumns + 1]; + + BeanField beanField; + for (int i = 0; i <= numColumns; i++) { + beanField = findField(i); + String columnHeaderName = extractHeaderName(beanField); + header[i] = columnHeaderName; + } + return header; + } + + private String extractHeaderName(final BeanField beanField) { + if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { + return ""; + } + + final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0]; + return bindByNameAnnotation.column(); + } +} diff --git a/build.gradle b/build.gradle index fa8baac2e..d257866fe 100644 --- a/build.gradle +++ b/build.gradle @@ -107,6 +107,7 @@ allprojects { dependency 'org.slf4j:slf4j-nop:1.7.25' dependency 'org.slf4j:slf4j-simple:1.7.25' dependency 'org.zeroturnaround:zt-zip:1.12' + dependency 'com.opencsv:opencsv:4.0' } } From 7ad6c31dbd1270e17907dda006e73288458dab7b Mon Sep 17 00:00:00 2001 From: bvo2002 Date: Sun, 22 Oct 2017 21:46:21 +0300 Subject: [PATCH 2/4] add category csv export --- .../allure/category/CategoriesPlugin.java | 103 +++++++++++------- .../qameta/allure/csv/CsvExportCategory.java | 57 +++++----- .../io/qameta/allure/csv/CsvExportSuite.java | 14 +-- .../qameta/allure/csv/CsvMappingStrategy.java | 36 ------ .../io/qameta/allure/suites/SuitesPlugin.java | 72 +++++++----- .../tree-view-container/TreeViewContainer.js | 4 +- .../tree-view-container/styles.scss | 7 ++ .../javascript/components/tree/styles.scss | 4 - .../allure/category/CategoriesPluginTest.java | 39 +++---- .../allure/suites/SuitesPluginTest.java | 71 +++++++++--- .../allure/CommonCsvExportAggregator.java | 87 +++++++++++++++ .../qameta/allure/CommonJsonAggregator.java | 39 +++++++ .../io/qameta/allure/CompositeAggregator.java | 31 ++++++ .../java/io/qameta/allure/CsvExporter.java | 37 ------- .../allure/util/CsvMappingStrategy.java | 35 ------ 15 files changed, 384 insertions(+), 252 deletions(-) delete mode 100644 allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java create mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java create mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/CommonJsonAggregator.java create mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/CompositeAggregator.java delete mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java delete mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java diff --git a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java index ac413e44d..29ad9e4fc 100644 --- a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java +++ b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java @@ -1,8 +1,9 @@ package io.qameta.allure.category; import com.fasterxml.jackson.core.type.TypeReference; -import io.qameta.allure.Aggregator; -import io.qameta.allure.CsvExporter; +import io.qameta.allure.CommonCsvExportAggregator; +import io.qameta.allure.CommonJsonAggregator; +import io.qameta.allure.CompositeAggregator; import io.qameta.allure.Reader; import io.qameta.allure.Widget; import io.qameta.allure.context.JacksonContext; @@ -22,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -30,7 +30,9 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -47,15 +49,15 @@ * @since 2.0 */ @SuppressWarnings("PMD.ExcessiveImports") -public class CategoriesPlugin extends CsvExporter implements Aggregator, Reader, Widget { +public class CategoriesPlugin extends CompositeAggregator implements Reader, Widget { - public static final String CATEGORIES_BLOCK_NAME = "categories"; + public static final String CATEGORIES = "categories"; public static final Category FAILED_TESTS = new Category().setName("Product defects"); public static final Category BROKEN_TESTS = new Category().setName("Test defects"); - private static final String CATEGORIES_FILE_NAME = "categories.json"; + public static final String JSON_FILE_NAME = "categories.json"; public static final String CSV_FILE_NAME = "categories.csv"; @@ -64,41 +66,29 @@ public class CategoriesPlugin extends CsvExporter implements new TypeReference>() {}; //@formatter:on + public CategoriesPlugin() { + super(Arrays.asList(new JsonAggregator(), new CsvExportAggregator())); + } + @Override public void readResults(final Configuration configuration, final ResultsVisitor visitor, final Path directory) { final JacksonContext context = configuration.requireContext(JacksonContext.class); - final Path categoriesFile = directory.resolve(CATEGORIES_FILE_NAME); + final Path categoriesFile = directory.resolve(JSON_FILE_NAME); if (Files.exists(categoriesFile)) { try (InputStream is = Files.newInputStream(categoriesFile)) { final List categories = context.getValue().readValue(is, CATEGORIES_TYPE); - visitor.visitExtra(CATEGORIES_BLOCK_NAME, categories); + visitor.visitExtra(CATEGORIES, categories); } catch (IOException e) { visitor.error("Could not read categories file " + categoriesFile, e); } } } - @Override - public void aggregate(final Configuration configuration, - final List launchesResults, - final Path outputDirectory) throws IOException { - - addCategoriesForResults(launchesResults); - - final JacksonContext jacksonContext = configuration.requireContext(JacksonContext.class); - final Path dataFolder = Files.createDirectories(outputDirectory.resolve("data")); - final Path dataFile = dataFolder.resolve(CATEGORIES_FILE_NAME); - try (OutputStream os = Files.newOutputStream(dataFile)) { - jacksonContext.getValue().writeValue(os, getData(launchesResults)); - } - createCsvExportFile(launchesResults, dataFolder, CSV_FILE_NAME, CsvExportCategory.class); - } - @Override public String getName() { - return "categories"; + return CATEGORIES; } @Override @@ -115,11 +105,10 @@ public Object getData(final Configuration configuration, final List getData(final List launchResults) { - + /* default */ static Tree getData(final List launchResults) { // @formatter:off - final Tree categories = new TestResultTree("categories", this::groupByCategories); + final Tree categories = new TestResultTree(CATEGORIES, CategoriesPlugin::groupByCategories); // @formatter:on launchResults.stream() @@ -131,29 +120,29 @@ public Object getData(final Configuration configuration, final List launchesResults) { + /* default */ static void addCategoriesForResults(final List launchesResults) { launchesResults.forEach(launch -> { - final List categories = launch.getExtra(CATEGORIES_BLOCK_NAME, Collections::emptyList); + final List categories = launch.getExtra(CATEGORIES, Collections::emptyList); launch.getResults().forEach(result -> { - final List resultCategories = result.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList<>()); + final List resultCategories = result.getExtraBlock(CATEGORIES, new ArrayList<>()); categories.forEach(category -> { if (matches(result, category)) { resultCategories.add(category); } }); if (resultCategories.isEmpty() && Status.FAILED.equals(result.getStatus())) { - result.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList()).add(FAILED_TESTS); + result.getExtraBlock(CATEGORIES, new ArrayList()).add(FAILED_TESTS); } if (resultCategories.isEmpty() && Status.BROKEN.equals(result.getStatus())) { - result.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList()).add(BROKEN_TESTS); + result.getExtraBlock(CATEGORIES, new ArrayList()).add(BROKEN_TESTS); } }); }); } - protected List groupByCategories(final TestResult testResult) { + protected static List groupByCategories(final TestResult testResult) { final Set categories = testResult - .>getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList<>()) + .>getExtraBlock(CATEGORIES, new ArrayList<>()) .stream() .map(Category::getName) .collect(Collectors.toSet()); @@ -191,7 +180,47 @@ protected TreeWidgetItem toWidgetItem(final TestResultTreeGroup group) { } @Override - public List getCollectionToCsvExport(List launchesResults) { - return null; + public void aggregate(final Configuration configuration, final List launchesResults, final Path outputDirectory) throws IOException { + addCategoriesForResults(launchesResults); + super.aggregate(configuration, launchesResults, outputDirectory); + } + + private static class JsonAggregator extends CommonJsonAggregator { + + JsonAggregator() { + super(JSON_FILE_NAME); + } + + @Override + protected Tree getData(final List launchResults) { + return CategoriesPlugin.getData(launchResults); + } + } + + private static class CsvExportAggregator extends CommonCsvExportAggregator { + + CsvExportAggregator() { + super(CSV_FILE_NAME, CsvExportCategory.class); + } + + @Override + protected List getData(final List launchesResults) { + Map exportCategories = new HashMap<>(); + launchesResults.forEach(launch -> launch.getResults().forEach(result -> { + final List categories = result.getExtraBlock(CATEGORIES, new ArrayList<>()); + categories.forEach(category -> { + CsvExportCategory exportCategory = exportCategories.get(category.getName()); + if (exportCategory != null) { + exportCategory.addTestResult(result); + } else { + exportCategory = new CsvExportCategory(); + exportCategory.setName(category.getName()); + exportCategory.addTestResult(result); + exportCategories.put(exportCategory.getName(), exportCategory); + } + }); + })); + return exportCategories.values().stream().collect(Collectors.toList()); + } } } diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java index cb11d159c..0e0e27120 100644 --- a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java +++ b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java @@ -2,6 +2,7 @@ import com.opencsv.bean.CsvBindByName; import com.opencsv.bean.CsvBindByPosition; +import io.qameta.allure.entity.Status; import io.qameta.allure.entity.TestResult; import java.io.Serializable; @@ -18,69 +19,67 @@ public class CsvExportCategory implements Serializable { @CsvBindByName(column = "FAILED") @CsvBindByPosition(position = 1) - private String failed; + private int failed; @CsvBindByName(column = "BROKEN") @CsvBindByPosition(position = 2) - private String broken; + private int broken; @CsvBindByName(column = "PASSED") @CsvBindByPosition(position = 3) - private String passed; + private int passed; @CsvBindByName(column = "SKIPPED") @CsvBindByPosition(position = 4) - private String skipped; + private int skipped; @CsvBindByName(column = "UNKNOWN") @CsvBindByPosition(position = 5) - private String unknown; + private int unknown; public String getName() { return name; } - public void setName(String name) { + public void setName(final String name) { this.name = name; } - public String getFailed() { + public int getFailed() { return failed; } - public void setFailed(String failed) { - this.failed = failed; - } - - public String getBroken() { + public int getBroken() { return broken; } - public void setBroken(String broken) { - this.broken = broken; - } - - public String getPassed() { + public int getPassed() { return passed; } - public void setPassed(String passed) { - this.passed = passed; - } - - public String getSkipped() { + public int getSkipped() { return skipped; } - public void setSkipped(String skipped) { - this.skipped = skipped; - } - - public String getUnknown() { + public int getUnknown() { return unknown; } - public void setUnknown(String unknown) { - this.unknown = unknown; + public void addTestResult(final TestResult result) { + if (Status.FAILED.equals(result.getStatus())) { + this.failed++; + } + if (Status.BROKEN.equals(result.getStatus())) { + this.broken++; + } + if (Status.PASSED.equals(result.getStatus())) { + this.passed++; + } + if (Status.SKIPPED.equals(result.getStatus())) { + this.skipped++; + } + if (Status.UNKNOWN.equals(result.getStatus())) { + this.unknown++; + } } } diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java index b03d278ff..ea18b98c9 100644 --- a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java +++ b/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java @@ -28,10 +28,10 @@ public class CsvExportSuite implements Serializable { @CsvBindByPosition(position = 3) private String message; - public CsvExportSuite(TestResult result) { - this.status = result.getStatus().value(); + public CsvExportSuite(final TestResult result) { + this.status = result.getStatus() != null ? result.getStatus().value() : null; this.name = result.getFullName(); - this.duration = "" + result.getTime().getDuration(); + this.duration = result.getTime() != null ? result.getTime().getDuration().toString() : null; this.message = result.getDescription(); } @@ -39,7 +39,7 @@ public String getMessage() { return message; } - public void setMessage(String message) { + public void setMessage(final String message) { this.message = message; } @@ -47,7 +47,7 @@ public String getDuration() { return duration; } - public void setDuration(String duration) { + public void setDuration(final String duration) { this.duration = duration; } @@ -55,7 +55,7 @@ public String getName() { return name; } - public void setName(String name) { + public void setName(final String name) { this.name = name; } @@ -63,7 +63,7 @@ public String getStatus() { return status; } - public void setStatus(String status) { + public void setStatus(final String status) { this.status = status; } } diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java b/allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java deleted file mode 100644 index 8a86a0305..000000000 --- a/allure-generator/src/main/java/io/qameta/allure/csv/CsvMappingStrategy.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.qameta.allure.csv; - -import com.opencsv.bean.BeanField; -import com.opencsv.bean.ColumnPositionMappingStrategy; -import com.opencsv.bean.CsvBindByName; -import org.apache.commons.lang3.StringUtils; - -public class CsvMappingStrategy extends ColumnPositionMappingStrategy { - - @Override - public String[] generateHeader() { - final int numColumns = findMaxFieldIndex(); - if (!isAnnotationDriven() || numColumns == -1) { - return super.generateHeader(); - } - - header = new String[numColumns + 1]; - - BeanField beanField; - for (int i = 0; i <= numColumns; i++) { - beanField = findField(i); - String columnHeaderName = extractHeaderName(beanField); - header[i] = columnHeaderName; - } - return header; - } - - private String extractHeaderName(final BeanField beanField) { - if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { - return StringUtils.EMPTY; - } - - final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0]; - return bindByNameAnnotation.column(); - } -} diff --git a/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java b/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java index 9df66e5a4..cc048b8e3 100644 --- a/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java +++ b/allure-generator/src/main/java/io/qameta/allure/suites/SuitesPlugin.java @@ -1,9 +1,9 @@ package io.qameta.allure.suites; -import io.qameta.allure.Aggregator; -import io.qameta.allure.CsvExporter; +import io.qameta.allure.CommonCsvExportAggregator; +import io.qameta.allure.CommonJsonAggregator; +import io.qameta.allure.CompositeAggregator; import io.qameta.allure.Widget; -import io.qameta.allure.context.JacksonContext; import io.qameta.allure.core.Configuration; import io.qameta.allure.core.LaunchResults; import io.qameta.allure.csv.CsvExportSuite; @@ -13,11 +13,7 @@ import io.qameta.allure.tree.Tree; import io.qameta.allure.tree.TreeWidgetData; import io.qameta.allure.tree.TreeWidgetItem; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; @@ -36,29 +32,26 @@ * * @since 2.0 */ -public class SuitesPlugin extends CsvExporter implements Aggregator, Widget { +public class SuitesPlugin extends CompositeAggregator implements Widget { - public static final String CSV_FILE_NAME = "suites.csv"; + private static final String SUITES = "suites"; - @Override - public void aggregate(final Configuration configuration, - final List launchesResults, - final Path outputDirectory) throws IOException { - final JacksonContext jacksonContext = configuration.requireContext(JacksonContext.class); - final Path dataFolder = Files.createDirectories(outputDirectory.resolve("data")); - final Path dataFile = dataFolder.resolve("suites.json"); - try (OutputStream os = Files.newOutputStream(dataFile)) { - jacksonContext.getValue().writeValue(os, getData(launchesResults)); - } - createCsvExportFile(launchesResults, dataFolder, CSV_FILE_NAME,CsvExportSuite.class); + /** Name of the json file. */ + protected static final String JSON_FILE_NAME = "suites.json"; + + /** Name of the csv file. */ + protected static final String CSV_FILE_NAME = "suites.csv"; + + public SuitesPlugin() { + super(Arrays.asList(new JsonAggregator(), new CsvExportAggregator())); } @SuppressWarnings("PMD.DefaultPackage") - /* default */ Tree getData(final List launchResults) { + static /* default */ Tree getData(final List launchResults) { // @formatter:off final Tree xunit = new TestResultTree( - "suites", + SUITES, testResult -> groupByLabels(testResult, PARENT_SUITE, SUITE, SUB_SUITE) ); // @formatter:on @@ -86,20 +79,39 @@ public Object getData(final Configuration configuration, final List getCollectionToCsvExport(List launchesResults) { - return launchesResults.stream() - .flatMap(launch -> launch.getResults().stream()) - .map(CsvExportSuite::new).collect(Collectors.toList()); + private static class JsonAggregator extends CommonJsonAggregator { + + JsonAggregator() { + super(JSON_FILE_NAME); + } + + @Override + protected Tree getData(final List launchResults) { + return SuitesPlugin.getData(launchResults); + } + } + + private static class CsvExportAggregator extends CommonCsvExportAggregator { + + CsvExportAggregator() { + super(CSV_FILE_NAME, CsvExportSuite.class); + } + + @Override + protected List getData(final List launchesResults) { + return launchesResults.stream() + .flatMap(launch -> launch.getResults().stream()) + .map(CsvExportSuite::new).collect(Collectors.toList()); + } } } diff --git a/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js b/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js index d6fd59021..6bd9edb19 100644 --- a/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js +++ b/allure-generator/src/main/javascript/components/tree-view-container/TreeViewContainer.js @@ -27,7 +27,6 @@ class TreeViewContainer extends View { this.csvUrl = csvUrl; this.tabName = tabName; this.listenTo(this.routeState, 'change:testResultTab', this.render); - this.settings = settings; } @@ -67,7 +66,8 @@ class TreeViewContainer extends View { tabName: this.tabName, shownCases: 0, totalCases: 0, - filtered: false + filtered: false, + csvUrl: this.csvUrl }; } } diff --git a/allure-generator/src/main/javascript/components/tree-view-container/styles.scss b/allure-generator/src/main/javascript/components/tree-view-container/styles.scss index 0858db65a..1ddfe03a5 100644 --- a/allure-generator/src/main/javascript/components/tree-view-container/styles.scss +++ b/allure-generator/src/main/javascript/components/tree-view-container/styles.scss @@ -19,4 +19,11 @@ border-top: 1px solid #ECEFF1; padding: 8px 0 0 15px; } + .pane__controls { + display: flex; + } + &__info, &__download { + color: $text-muted-color; + padding: 7px; + } } \ No newline at end of file diff --git a/allure-generator/src/main/javascript/components/tree/styles.scss b/allure-generator/src/main/javascript/components/tree/styles.scss index 5a8d9dd46..1fc12d947 100644 --- a/allure-generator/src/main/javascript/components/tree/styles.scss +++ b/allure-generator/src/main/javascript/components/tree/styles.scss @@ -62,10 +62,6 @@ } } } - &__info, &__download { - color: $text-muted-color; - padding: 7px; - } &__time { padding: 5px; } diff --git a/allure-generator/src/test/java/io/qameta/allure/category/CategoriesPluginTest.java b/allure-generator/src/test/java/io/qameta/allure/category/CategoriesPluginTest.java index 664e48323..23bcdff17 100644 --- a/allure-generator/src/test/java/io/qameta/allure/category/CategoriesPluginTest.java +++ b/allure-generator/src/test/java/io/qameta/allure/category/CategoriesPluginTest.java @@ -25,7 +25,9 @@ import java.util.Set; import static io.qameta.allure.category.CategoriesPlugin.BROKEN_TESTS; -import static io.qameta.allure.category.CategoriesPlugin.CATEGORIES_BLOCK_NAME; +import static io.qameta.allure.category.CategoriesPlugin.CATEGORIES; +import static io.qameta.allure.category.CategoriesPlugin.CSV_FILE_NAME; +import static io.qameta.allure.category.CategoriesPlugin.JSON_FILE_NAME; import static io.qameta.allure.category.CategoriesPlugin.FAILED_TESTS; import static io.qameta.allure.testdata.TestData.createSingleLaunchResults; import static java.util.Collections.singletonList; @@ -67,12 +69,12 @@ public void shouldDefaultCategoriesToResults() throws Exception { categoriesPlugin.addCategoriesForResults(createSingleLaunchResults(first, second)); - assertThat(first.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList())) + assertThat(first.getExtraBlock(CATEGORIES, new ArrayList())) .hasSize(1) .extracting(Category::getName) .containsExactlyInAnyOrder(FAILED_TESTS.getName()); - assertThat(second.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList())) + assertThat(second.getExtraBlock(CATEGORIES, new ArrayList())) .hasSize(1) .extracting(Category::getName) .containsExactlyInAnyOrder(BROKEN_TESTS.getName()); @@ -103,12 +105,12 @@ public void shouldSetCustomCategoriesToResults() throws Exception { categoriesPlugin.addCategoriesForResults(createSingleLaunchResults(meta, first, second)); - assertThat(first.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList())) + assertThat(first.getExtraBlock(CATEGORIES, new ArrayList())) .hasSize(1) .extracting(Category::getName) .containsExactlyInAnyOrder(FAILED_TESTS.getName()); - assertThat(second.getExtraBlock(CATEGORIES_BLOCK_NAME, new ArrayList())) + assertThat(second.getExtraBlock(CATEGORIES, new ArrayList())) .hasSize(1) .extracting(Category::getName) .containsExactlyInAnyOrder(categoryName); @@ -116,8 +118,6 @@ public void shouldSetCustomCategoriesToResults() throws Exception { @Test public void shouldCreateTree() throws Exception { - final CategoriesPlugin categoriesPlugin = new CategoriesPlugin(); - final TestResult first = new TestResult() .setName("first") .setStatus(Status.BROKEN) @@ -135,13 +135,13 @@ public void shouldCreateTree() throws Exception { .setStatus(Status.PASSED) .setStatusDetails(new StatusDetails().setMessage("M4")); - first.setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(new Category().setName("C1"))); - second.setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(new Category().setName("C2"))); - third.setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(new Category().setName("C1"))); - other.setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(new Category().setName("C3"))); + first.setExtraBlock(CATEGORIES, singletonList(new Category().setName("C1"))); + second.setExtraBlock(CATEGORIES, singletonList(new Category().setName("C2"))); + third.setExtraBlock(CATEGORIES, singletonList(new Category().setName("C1"))); + other.setExtraBlock(CATEGORIES, singletonList(new Category().setName("C3"))); final List launchResults = createSingleLaunchResults(first, second, third, other); - final Tree tree = categoriesPlugin.getData(launchResults); + final Tree tree = CategoriesPlugin.getData(launchResults); assertThat(tree.getChildren()) .hasSize(3) @@ -189,9 +189,11 @@ meta, createTestResult("asd\n", Status.BROKEN) .extracting(Category::getName) .containsExactly(category.getName()); - assertThat(reportPath.resolve("data").resolve("categories.json")) + assertThat(reportPath.resolve("data").resolve(JSON_FILE_NAME)) .exists(); + assertThat(reportPath.resolve("data").resolve(CSV_FILE_NAME)) + .exists(); } @Test @@ -220,7 +222,7 @@ meta, createTestResult("asd\n", Status.FAILED, true) .extracting(Category::getName) .containsExactly(category.getName()); - assertThat(reportPath.resolve("data").resolve("categories.json")) + assertThat(reportPath.resolve("data").resolve(JSON_FILE_NAME)) .exists(); } @@ -234,19 +236,18 @@ public void shouldSortByStartTimeAsc() throws Exception { .setName("first") .setStatus(Status.FAILED) .setTime(new Time().setStart(10L)) - .setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(category)); + .setExtraBlock(CATEGORIES, singletonList(category)); final TestResult second = new TestResult() .setName("second") .setStatus(Status.FAILED) .setTime(new Time().setStart(12L)) - .setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(category)); + .setExtraBlock(CATEGORIES, singletonList(category)); final TestResult timeless = new TestResult() .setName("timeless") .setStatus(Status.FAILED) - .setExtraBlock(CATEGORIES_BLOCK_NAME, singletonList(category)); + .setExtraBlock(CATEGORIES, singletonList(category)); - final CategoriesPlugin plugin = new CategoriesPlugin(); - final Tree tree = plugin.getData( + final Tree tree = CategoriesPlugin.getData( createSingleLaunchResults(second, first, timeless) ); diff --git a/allure-generator/src/test/java/io/qameta/allure/suites/SuitesPluginTest.java b/allure-generator/src/test/java/io/qameta/allure/suites/SuitesPluginTest.java index 6d9c8672b..f87139d7b 100644 --- a/allure-generator/src/test/java/io/qameta/allure/suites/SuitesPluginTest.java +++ b/allure-generator/src/test/java/io/qameta/allure/suites/SuitesPluginTest.java @@ -1,14 +1,27 @@ package io.qameta.allure.suites; +import io.qameta.allure.ConfigurationBuilder; import io.qameta.allure.Issue; +import io.qameta.allure.core.Configuration; +import io.qameta.allure.core.LaunchResults; import io.qameta.allure.entity.Label; import io.qameta.allure.entity.TestResult; import io.qameta.allure.entity.Time; import io.qameta.allure.tree.Tree; import io.qameta.allure.tree.TreeNode; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; import static io.qameta.allure.testdata.TestData.createSingleLaunchResults; +import static io.qameta.allure.suites.SuitesPlugin.CSV_FILE_NAME; +import static io.qameta.allure.suites.SuitesPlugin.JSON_FILE_NAME; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -17,23 +30,23 @@ */ public class SuitesPluginTest { + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + private Configuration configuration; + + private Path reportPath; + + @Before + public void setUp() throws IOException { + reportPath = Paths.get(folder.newFolder("report").getAbsolutePath()); + configuration = new ConfigurationBuilder().useDefault().build(); + } + @Test public void shouldCreateTree() throws Exception { - final SuitesPlugin plugin = new SuitesPlugin(); - final TestResult first = new TestResult() - .setName("first") - .setLabels(singletonList(new Label().setName("suite").setValue("s1"))); - final TestResult second = new TestResult() - .setName("second") - .setLabels(singletonList(new Label().setName("suite").setValue("s1"))); - final TestResult third = new TestResult() - .setName("third") - .setLabels(singletonList(new Label().setName("suite").setValue("s2"))); - - final Tree tree = plugin.getData( - createSingleLaunchResults(first, second, third) - ); + final Tree tree = SuitesPlugin.getData(getSimpleLaunchResults()); assertThat(tree.getChildren()) .hasSize(2) @@ -54,8 +67,7 @@ public void shouldSortByStartTimeAsc() throws Exception { final TestResult timeless = new TestResult() .setName("timeless"); - final SuitesPlugin plugin = new SuitesPlugin(); - final Tree tree = plugin.getData( + final Tree tree = SuitesPlugin.getData( createSingleLaunchResults(second, first, timeless) ); @@ -63,4 +75,31 @@ public void shouldSortByStartTimeAsc() throws Exception { .extracting(TreeNode::getName) .containsExactly("timeless", "first", "second"); } + + @Test + public void shouldCreateCsvFile() throws IOException { + + final SuitesPlugin plugin = new SuitesPlugin(); + + plugin.aggregate(configuration, getSimpleLaunchResults(), reportPath); + + assertThat(reportPath.resolve("data").resolve(JSON_FILE_NAME)) + .exists(); + + assertThat(reportPath.resolve("data").resolve(CSV_FILE_NAME)) + .exists(); + } + + private List getSimpleLaunchResults() { + final TestResult first = new TestResult() + .setName("first") + .setLabels(singletonList(new Label().setName("suite").setValue("s1"))); + final TestResult second = new TestResult() + .setName("second") + .setLabels(singletonList(new Label().setName("suite").setValue("s1"))); + final TestResult third = new TestResult() + .setName("third") + .setLabels(singletonList(new Label().setName("suite").setValue("s2"))); + return createSingleLaunchResults(second, first, third); + } } \ No newline at end of file diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java b/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java new file mode 100644 index 000000000..98ff3c22a --- /dev/null +++ b/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java @@ -0,0 +1,87 @@ +package io.qameta.allure; + +import com.opencsv.bean.BeanField; +import com.opencsv.bean.ColumnPositionMappingStrategy; +import com.opencsv.bean.CsvBindByName; +import com.opencsv.bean.StatefulBeanToCsv; +import com.opencsv.bean.StatefulBeanToCsvBuilder; +import io.qameta.allure.core.Configuration; +import io.qameta.allure.core.LaunchResults; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +/** + * Csv exporter extension. Can be used to process results in csv file + * + * @since 2.0 + */ +public abstract class CommonCsvExportAggregator implements Aggregator { + + private final String fileName; + + private final Class type; + + public CommonCsvExportAggregator(final String fileName, final Class type) { + this.fileName = fileName; + this.type = type; + } + + @Override + public void aggregate(final Configuration configuration, + final List launchesResults, + final Path outputDirectory) throws IOException { + final Path dataFolder = Files.createDirectories(outputDirectory.resolve("data")); + final Path csv = dataFolder.resolve(fileName); + + try (Writer writer = Files.newBufferedWriter(csv)) { + StatefulBeanToCsvBuilder builder = new StatefulBeanToCsvBuilder<>(writer); + CsvMappingStrategy mappingStrategy = new CsvMappingStrategy<>(); + mappingStrategy.setType(type); + StatefulBeanToCsv beanWriter = builder.withMappingStrategy(mappingStrategy).build(); + try { + beanWriter.write(getData(launchesResults)); + } catch (Exception e) { + throw new IOException(e); + } + } + } + + protected abstract List getData(final List launchesResults); + + private static class CsvMappingStrategy extends ColumnPositionMappingStrategy { + + @Override + public String[] generateHeader() { + final int numColumns = findMaxFieldIndex(); + if (!isAnnotationDriven() || numColumns == -1) { + return super.generateHeader(); + } + + header = new String[numColumns + 1]; + + BeanField beanField; + for (int i = 0; i <= numColumns; i++) { + beanField = findField(i); + String columnHeaderName = extractHeaderName(beanField); + header[i] = columnHeaderName; + } + return header; + } + + private String extractHeaderName(final BeanField beanField) { + if (beanField == null + || beanField.getField() == null + || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { + return StringUtils.EMPTY; + } + final CsvBindByName bindByNameAnnotation = + beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0]; + return bindByNameAnnotation.column(); + } + } +} diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CommonJsonAggregator.java b/allure-plugin-api/src/main/java/io/qameta/allure/CommonJsonAggregator.java new file mode 100644 index 000000000..a1bc547af --- /dev/null +++ b/allure-plugin-api/src/main/java/io/qameta/allure/CommonJsonAggregator.java @@ -0,0 +1,39 @@ +package io.qameta.allure; + +import io.qameta.allure.context.JacksonContext; +import io.qameta.allure.core.Configuration; +import io.qameta.allure.core.LaunchResults; +import io.qameta.allure.entity.TestResult; +import io.qameta.allure.tree.Tree; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +/** + * Common json aggregator. + */ +public abstract class CommonJsonAggregator implements Aggregator { + + private final String fileName; + + protected CommonJsonAggregator(final String fileName) { + this.fileName = fileName; + } + + @Override + public void aggregate(final Configuration configuration, + final List launchesResults, + final Path outputDirectory) throws IOException { + final JacksonContext jacksonContext = configuration.requireContext(JacksonContext.class); + final Path dataFolder = Files.createDirectories(outputDirectory.resolve("data")); + final Path dataFile = dataFolder.resolve(this.fileName); + try (OutputStream os = Files.newOutputStream(dataFile)) { + jacksonContext.getValue().writeValue(os, getData(launchesResults)); + } + } + + protected abstract Tree getData(final List launchResults); +} diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CompositeAggregator.java b/allure-plugin-api/src/main/java/io/qameta/allure/CompositeAggregator.java new file mode 100644 index 000000000..6ceec033a --- /dev/null +++ b/allure-plugin-api/src/main/java/io/qameta/allure/CompositeAggregator.java @@ -0,0 +1,31 @@ +package io.qameta.allure; + +import io.qameta.allure.core.Configuration; +import io.qameta.allure.core.LaunchResults; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +/** + * Composite aggregator extension. Can be used to process the list of aggregator. + * + * @since 2.0 + */ +public class CompositeAggregator implements Aggregator { + + private final List aggregators; + + public CompositeAggregator(final List aggregators) { + this.aggregators = aggregators; + } + + @Override + public void aggregate(final Configuration configuration, + final List launchesResults, + final Path outputDirectory) throws IOException { + for (Aggregator aggregator : aggregators) { + aggregator.aggregate(configuration, launchesResults, outputDirectory); + } + } +} diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java b/allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java deleted file mode 100644 index b38f03f43..000000000 --- a/allure-plugin-api/src/main/java/io/qameta/allure/CsvExporter.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.qameta.allure; - -import com.opencsv.bean.StatefulBeanToCsv; -import com.opencsv.bean.StatefulBeanToCsvBuilder; -import io.qameta.allure.core.LaunchResults; -import io.qameta.allure.util.CsvMappingStrategy; - -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.nio.file.Path; -import java.util.List; - -/** - * Csv exporter extension. Can be used to process results in csv file - * - * @since 2.0 - */ -public abstract class CsvExporter implements Extension { - - public void createCsvExportFile(List launchesResults, Path dataFolder, String fileName, Class type) throws IOException { - final Path csv = dataFolder.resolve(fileName); - try(Writer writer = new FileWriter(csv.toFile())) { - StatefulBeanToCsvBuilder builder = new StatefulBeanToCsvBuilder<>(writer); - CsvMappingStrategy mappingStrategy = new CsvMappingStrategy<>(); - mappingStrategy.setType(type); - StatefulBeanToCsv beanWriter = builder.withMappingStrategy(mappingStrategy).build(); - try { - beanWriter.write(getCollectionToCsvExport(launchesResults)); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public abstract List getCollectionToCsvExport(List launchesResults); -} diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java b/allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java deleted file mode 100644 index 0ea3ad452..000000000 --- a/allure-plugin-api/src/main/java/io/qameta/allure/util/CsvMappingStrategy.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.qameta.allure.util; - -import com.opencsv.bean.BeanField; -import com.opencsv.bean.ColumnPositionMappingStrategy; -import com.opencsv.bean.CsvBindByName; - -public class CsvMappingStrategy extends ColumnPositionMappingStrategy { - - @Override - public String[] generateHeader() { - final int numColumns = findMaxFieldIndex(); - if (!isAnnotationDriven() || numColumns == -1) { - return super.generateHeader(); - } - - header = new String[numColumns + 1]; - - BeanField beanField; - for (int i = 0; i <= numColumns; i++) { - beanField = findField(i); - String columnHeaderName = extractHeaderName(beanField); - header[i] = columnHeaderName; - } - return header; - } - - private String extractHeaderName(final BeanField beanField) { - if (beanField == null || beanField.getField() == null || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { - return ""; - } - - final CsvBindByName bindByNameAnnotation = beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0]; - return bindByNameAnnotation.column(); - } -} From c2c1fb48ef44c1b717d44bc9960c7d3509c01145 Mon Sep 17 00:00:00 2001 From: bvo2002 Date: Mon, 23 Oct 2017 14:20:23 +0300 Subject: [PATCH 3/4] add behavior csv export --- allure-generator/build.gradle | 1 - .../allure/category/CategoriesPlugin.java | 32 ++-- .../allure/CommonCsvExportAggregator.java | 7 +- .../qameta/allure/csv/CsvExportBehavior.java | 111 ++++++++++++++ .../qameta/allure/csv/CsvExportCategory.java | 52 +++---- .../io/qameta/allure/csv/CsvExportSuite.java | 0 .../behaviors-plugin/src/dist/static/index.js | 3 +- .../allure/behaviors/BehaviorsPlugin.java | 145 +++++++++++++++--- 8 files changed, 269 insertions(+), 82 deletions(-) create mode 100644 allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java rename {allure-generator => allure-plugin-api}/src/main/java/io/qameta/allure/csv/CsvExportCategory.java (54%) rename {allure-generator => allure-plugin-api}/src/main/java/io/qameta/allure/csv/CsvExportSuite.java (100%) diff --git a/allure-generator/build.gradle b/allure-generator/build.gradle index ce55b834d..314e4763d 100644 --- a/allure-generator/build.gradle +++ b/allure-generator/build.gradle @@ -60,7 +60,6 @@ dependencies { compile('com.fasterxml.jackson.dataformat:jackson-dataformat-yaml') compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml' compile('org.apache.httpcomponents:httpclient') - compile('com.opencsv:opencsv') compileOnly('org.projectlombok:lombok') diff --git a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java index 29ad9e4fc..f0dcf298f 100644 --- a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java +++ b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java @@ -30,9 +30,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -97,7 +95,7 @@ public Object getData(final Configuration configuration, final List items = data.getChildren().stream() .filter(TestResultTreeGroup.class::isInstance) .map(TestResultTreeGroup.class::cast) - .map(this::toWidgetItem) + .map(CategoriesPlugin::toWidgetItem) .sorted(Comparator.comparing(TreeWidgetItem::getStatistic, comparator()).reversed()) .limit(10) .collect(Collectors.toList()); @@ -172,7 +170,7 @@ private static boolean matches(final String message, final String pattern) { return Pattern.compile(pattern, Pattern.DOTALL).matcher(message).matches(); } - protected TreeWidgetItem toWidgetItem(final TestResultTreeGroup group) { + protected static TreeWidgetItem toWidgetItem(final TestResultTreeGroup group) { return new TreeWidgetItem() .setUid(group.getUid()) .setName(group.getName()) @@ -205,22 +203,16 @@ private static class CsvExportAggregator extends CommonCsvExportAggregator getData(final List launchesResults) { - Map exportCategories = new HashMap<>(); - launchesResults.forEach(launch -> launch.getResults().forEach(result -> { - final List categories = result.getExtraBlock(CATEGORIES, new ArrayList<>()); - categories.forEach(category -> { - CsvExportCategory exportCategory = exportCategories.get(category.getName()); - if (exportCategory != null) { - exportCategory.addTestResult(result); - } else { - exportCategory = new CsvExportCategory(); - exportCategory.setName(category.getName()); - exportCategory.addTestResult(result); - exportCategories.put(exportCategory.getName(), exportCategory); - } - }); - })); - return exportCategories.values().stream().collect(Collectors.toList()); + final List exportLabels = new ArrayList<>(); + final Tree data = CategoriesPlugin.getData(launchesResults); + final List items = data.getChildren().stream() + .filter(TestResultTreeGroup.class::isInstance) + .map(TestResultTreeGroup.class::cast) + .map(CategoriesPlugin::toWidgetItem) + .sorted(Comparator.comparing(TreeWidgetItem::getStatistic, comparator()).reversed()) + .collect(Collectors.toList()); + items.forEach(item -> exportLabels.add(new CsvExportCategory(item))); + return exportLabels; } } } diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java b/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java index 98ff3c22a..38ec1566d 100644 --- a/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java +++ b/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java @@ -67,8 +67,7 @@ public String[] generateHeader() { BeanField beanField; for (int i = 0; i <= numColumns; i++) { beanField = findField(i); - String columnHeaderName = extractHeaderName(beanField); - header[i] = columnHeaderName; + header[i] = extractHeaderName(beanField); } return header; } @@ -79,9 +78,7 @@ private String extractHeaderName(final BeanField beanField) { || beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class).length == 0) { return StringUtils.EMPTY; } - final CsvBindByName bindByNameAnnotation = - beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0]; - return bindByNameAnnotation.column(); + return beanField.getField().getDeclaredAnnotationsByType(CsvBindByName.class)[0].column(); } } } diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java new file mode 100644 index 000000000..54d3a1b85 --- /dev/null +++ b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java @@ -0,0 +1,111 @@ +package io.qameta.allure.csv; + +import com.opencsv.bean.CsvBindByName; +import com.opencsv.bean.CsvBindByPosition; +import io.qameta.allure.entity.Status; +import io.qameta.allure.entity.TestResult; +import io.qameta.allure.tree.TreeWidgetItem; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * Class contains the information for the behavior csv export. + * + */ +public class CsvExportBehavior implements Serializable { + + @CsvBindByName(column = "Epic") + @CsvBindByPosition(position = 0) + private String epic; + + @CsvBindByName(column = "Feature") + @CsvBindByPosition(position = 1) + private String feature; + + @CsvBindByName(column = "Story") + @CsvBindByPosition(position = 2) + private String story; + + @CsvBindByName(column = "FAILED") + @CsvBindByPosition(position = 3) + private long failed; + + @CsvBindByName(column = "BROKEN") + @CsvBindByPosition(position = 4) + private long broken; + + @CsvBindByName(column = "PASSED") + @CsvBindByPosition(position = 5) + private long passed; + + @CsvBindByName(column = "SKIPPED") + @CsvBindByPosition(position = 6) + private long skipped; + + @CsvBindByName(column = "UNKNOWN") + @CsvBindByPosition(position = 7) + private long unknown; + + public CsvExportBehavior(final String epic, final String feature, final String story) { + this.epic = epic; + this.feature = feature; + this.story = story; + } + + public String getEpic() { + return epic; + } + + public String getFeature() { + return feature; + } + + public String getStory() { + return story; + } + + public long getFailed() { + return failed; + } + + public long getBroken() { + return broken; + } + + public long getPassed() { + return passed; + } + + public long getSkipped() { + return skipped; + } + + public long getUnknown() { + return unknown; + } + + public boolean isPassed(final String epic, final String feature, final String story) { + return StringUtils.equals(this.epic, epic) + && StringUtils.equals(this.feature, feature) + && StringUtils.equals(this.story, story); + } + + public void addTestResult(final TestResult result) { + if (Status.FAILED.equals(result.getStatus())) { + this.failed++; + } + if (Status.BROKEN.equals(result.getStatus())) { + this.broken++; + } + if (Status.PASSED.equals(result.getStatus())) { + this.passed++; + } + if (Status.SKIPPED.equals(result.getStatus())) { + this.skipped++; + } + if (Status.UNKNOWN.equals(result.getStatus())) { + this.unknown++; + } + } +} diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java similarity index 54% rename from allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java rename to allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java index 0e0e27120..f9f4c8335 100644 --- a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportCategory.java +++ b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java @@ -4,6 +4,7 @@ import com.opencsv.bean.CsvBindByPosition; import io.qameta.allure.entity.Status; import io.qameta.allure.entity.TestResult; +import io.qameta.allure.tree.TreeWidgetItem; import java.io.Serializable; @@ -19,67 +20,54 @@ public class CsvExportCategory implements Serializable { @CsvBindByName(column = "FAILED") @CsvBindByPosition(position = 1) - private int failed; + private long failed; @CsvBindByName(column = "BROKEN") @CsvBindByPosition(position = 2) - private int broken; + private long broken; @CsvBindByName(column = "PASSED") @CsvBindByPosition(position = 3) - private int passed; + private long passed; @CsvBindByName(column = "SKIPPED") @CsvBindByPosition(position = 4) - private int skipped; + private long skipped; @CsvBindByName(column = "UNKNOWN") @CsvBindByPosition(position = 5) - private int unknown; + private long unknown; + + public CsvExportCategory(final TreeWidgetItem item) { + this.name = item.getName(); + this.failed = item.getStatistic().getFailed(); + this.broken = item.getStatistic().getBroken(); + this.passed = item.getStatistic().getPassed(); + this.skipped = item.getStatistic().getSkipped(); + this.unknown = item.getStatistic().getUnknown(); + } public String getName() { return name; } - public void setName(final String name) { - this.name = name; - } - - public int getFailed() { + public long getFailed() { return failed; } - public int getBroken() { + public long getBroken() { return broken; } - public int getPassed() { + public long getPassed() { return passed; } - public int getSkipped() { + public long getSkipped() { return skipped; } - public int getUnknown() { + public long getUnknown() { return unknown; } - - public void addTestResult(final TestResult result) { - if (Status.FAILED.equals(result.getStatus())) { - this.failed++; - } - if (Status.BROKEN.equals(result.getStatus())) { - this.broken++; - } - if (Status.PASSED.equals(result.getStatus())) { - this.passed++; - } - if (Status.SKIPPED.equals(result.getStatus())) { - this.skipped++; - } - if (Status.UNKNOWN.equals(result.getStatus())) { - this.unknown++; - } - } } diff --git a/allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportSuite.java similarity index 100% rename from allure-generator/src/main/java/io/qameta/allure/csv/CsvExportSuite.java rename to allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportSuite.java diff --git a/plugins/behaviors-plugin/src/dist/static/index.js b/plugins/behaviors-plugin/src/dist/static/index.js index 432f9f1a4..1726d3b20 100644 --- a/plugins/behaviors-plugin/src/dist/static/index.js +++ b/plugins/behaviors-plugin/src/dist/static/index.js @@ -80,7 +80,8 @@ allure.api.addTab('behaviors', { testResultTab: testResultTab, tabName: 'tab.behaviors.name', baseUrl: 'behaviors', - url: 'data/behaviors.json' + url: 'data/behaviors.json', + csvUrl: 'data/behaviors.csv' }); }) }); diff --git a/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java b/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java index d3ae2e8d7..dc2f2b52e 100644 --- a/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java +++ b/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java @@ -1,24 +1,32 @@ package io.qameta.allure.behaviors; -import io.qameta.allure.Aggregator; +import io.qameta.allure.CommonCsvExportAggregator; +import io.qameta.allure.CommonJsonAggregator; +import io.qameta.allure.CompositeAggregator; import io.qameta.allure.Widget; -import io.qameta.allure.context.JacksonContext; import io.qameta.allure.core.Configuration; import io.qameta.allure.core.LaunchResults; +import io.qameta.allure.csv.CsvExportBehavior; +import io.qameta.allure.csv.CsvExportCategory; +import io.qameta.allure.entity.LabelName; import io.qameta.allure.entity.TestResult; import io.qameta.allure.tree.TestResultTree; import io.qameta.allure.tree.TestResultTreeGroup; import io.qameta.allure.tree.Tree; +import io.qameta.allure.tree.TreeNode; import io.qameta.allure.tree.TreeWidgetData; import io.qameta.allure.tree.TreeWidgetItem; +import org.apache.commons.collections.CollectionUtils; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import static io.qameta.allure.entity.LabelName.EPIC; @@ -28,33 +36,34 @@ import static io.qameta.allure.entity.TestResult.comparingByTimeAsc; import static io.qameta.allure.tree.TreeUtils.calculateStatisticByChildren; import static io.qameta.allure.tree.TreeUtils.groupByLabels; +import static java.util.Optional.ofNullable; /** * The plugin adds behaviors tab to the report. * * @since 2.0 */ -public class BehaviorsPlugin implements Aggregator, Widget { +public class BehaviorsPlugin extends CompositeAggregator implements Widget { - @Override - public void aggregate(final Configuration configuration, - final List launchesResults, - final Path outputDirectory) throws IOException { - final JacksonContext jacksonContext = configuration.requireContext(JacksonContext.class); - final Path dataFolder = Files.createDirectories(outputDirectory.resolve("data")); - final Path dataFile = dataFolder.resolve("behaviors.json"); - try (OutputStream os = Files.newOutputStream(dataFile)) { - jacksonContext.getValue().writeValue(os, getData(launchesResults)); - } + public static final String BEHAVIORS = "behaviors"; + + public static final String JSON_FILE_NAME = "behaviors.json"; + + public static final String CSV_FILE_NAME = "behaviors.csv"; + + private static final LabelName[] labelNames = new LabelName[] {EPIC, FEATURE, STORY}; + + public BehaviorsPlugin() { + super(Arrays.asList(new JsonAggregator(), new CsvExportAggregator())); } @SuppressWarnings("PMD.DefaultPackage") - /* default */ Tree getData(final List launchResults) { + /* default */ static Tree getData(final List launchResults) { // @formatter:off final Tree behaviors = new TestResultTree( - "behaviors", - testResult -> groupByLabels(testResult, EPIC, FEATURE, STORY) + BEHAVIORS, + testResult -> groupByLabels(testResult, labelNames) ); // @formatter:on @@ -72,7 +81,7 @@ public Object getData(final Configuration configuration, final List items = data.getChildren().stream() .filter(TestResultTreeGroup.class::isInstance) .map(TestResultTreeGroup.class::cast) - .map(this::toWidgetItem) + .map(BehaviorsPlugin::toWidgetItem) .sorted(Comparator.comparing(TreeWidgetItem::getStatistic, comparator()).reversed()) .limit(10) .collect(Collectors.toList()); @@ -81,13 +90,103 @@ public Object getData(final Configuration configuration, final List getData(final List launchResults) { + return BehaviorsPlugin.getData(launchResults); + } + } + + private static class CsvExportAggregator extends CommonCsvExportAggregator { + + CsvExportAggregator() { + super(CSV_FILE_NAME, CsvExportBehavior.class); + } + + @Override + protected List getData(final List launchesResults) { + final List exportBehaviors = new ArrayList<>(); + launchesResults.stream().flatMap(launch -> launch.getResults().stream()).forEach(result -> { + + Map> epicFeatureStoryMap = new HashMap<>(); + Arrays.asList(labelNames).forEach(labelName -> + epicFeatureStoryMap.put(labelName, result.findAllLabels(labelName))); + + addTestResult(exportBehaviors, result, epicFeatureStoryMap); + }); + return exportBehaviors; + } + + private void addTestResult(final List exportBehaviors, final TestResult result, + final Map> epicFeatureStoryMap) { + if (epicFeatureStoryMap.isEmpty()) { + addTestResult(exportBehaviors, result, null, null, null); + } else { + addTestResultWithEpic(exportBehaviors, result, epicFeatureStoryMap); + } + } + + private void addTestResultWithEpic(final List exportBehaviors, final TestResult result, + final Map> epicFeatureStoryMap) { + if (!CollectionUtils.isEmpty(epicFeatureStoryMap.get(EPIC))) { + epicFeatureStoryMap.get(EPIC).forEach(epic -> + addTestResultWithFeature(exportBehaviors, result, epicFeatureStoryMap, epic) + ); + } else { + addTestResultWithFeature(exportBehaviors, result, epicFeatureStoryMap, null); + } + } + + private void addTestResultWithFeature(final List exportBehaviors, final TestResult result, + final Map> epicFeatureStoryMap, + final String epic) { + if (!CollectionUtils.isEmpty(epicFeatureStoryMap.get(FEATURE))) { + epicFeatureStoryMap.get(FEATURE).forEach(feature -> + addTestResultWithStories(exportBehaviors, result, epicFeatureStoryMap, epic, feature) + ); + } else { + addTestResultWithStories(exportBehaviors, result, epicFeatureStoryMap, epic, null); + } + } + + private void addTestResultWithStories(final List exportBehaviors, final TestResult result, + final Map> epicFeatureStoryMap, + final String epic, final String feature) { + if (!CollectionUtils.isEmpty(epicFeatureStoryMap.get(STORY))) { + epicFeatureStoryMap.get(STORY).forEach(story -> + addTestResult(exportBehaviors, result, epic, feature, story) + ); + } else { + addTestResult(exportBehaviors, result, epic, feature, null); + } + } + + private void addTestResult(final List exportBehaviors, final TestResult result, + final String epic, final String feature, final String story) { + Optional behavior = exportBehaviors.stream() + .filter(exportBehavior -> exportBehavior.isPassed(epic, feature, story)).findFirst(); + if (behavior.isPresent()) { + behavior.get().addTestResult(result); + } else { + CsvExportBehavior exportBehavior = new CsvExportBehavior(epic, feature, story); + exportBehavior.addTestResult(result); + exportBehaviors.add(exportBehavior); + } + } + } } From 41fbcaeff0bac0afe172105642e60b6060e449c0 Mon Sep 17 00:00:00 2001 From: bvo2002 Date: Mon, 23 Oct 2017 15:32:56 +0300 Subject: [PATCH 4/4] pmd correction --- .../allure/category/CategoriesPlugin.java | 4 +- .../allure/CommonCsvExportAggregator.java | 6 +-- .../qameta/allure/csv/CsvExportBehavior.java | 7 ++-- .../qameta/allure/csv/CsvExportCategory.java | 14 +++---- .../io/qameta/allure/csv/CsvExportSuite.java | 26 +++---------- .../allure/behaviors/BehaviorsPlugin.java | 39 +++++++++---------- 6 files changed, 36 insertions(+), 60 deletions(-) diff --git a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java index f0dcf298f..548e580fd 100644 --- a/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java +++ b/allure-generator/src/main/java/io/qameta/allure/category/CategoriesPlugin.java @@ -178,7 +178,9 @@ protected static TreeWidgetItem toWidgetItem(final TestResultTreeGroup group) { } @Override - public void aggregate(final Configuration configuration, final List launchesResults, final Path outputDirectory) throws IOException { + public void aggregate(final Configuration configuration, + final List launchesResults, + final Path outputDirectory) throws IOException { addCategoriesForResults(launchesResults); super.aggregate(configuration, launchesResults, outputDirectory); } diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java b/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java index 38ec1566d..7df3a0de6 100644 --- a/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java +++ b/allure-plugin-api/src/main/java/io/qameta/allure/CommonCsvExportAggregator.java @@ -61,13 +61,9 @@ public String[] generateHeader() { if (!isAnnotationDriven() || numColumns == -1) { return super.generateHeader(); } - header = new String[numColumns + 1]; - - BeanField beanField; for (int i = 0; i <= numColumns; i++) { - beanField = findField(i); - header[i] = extractHeaderName(beanField); + header[i] = extractHeaderName(findField(i)); } return header; } diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java index 54d3a1b85..825d66e2b 100644 --- a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java +++ b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportBehavior.java @@ -4,7 +4,6 @@ import com.opencsv.bean.CsvBindByPosition; import io.qameta.allure.entity.Status; import io.qameta.allure.entity.TestResult; -import io.qameta.allure.tree.TreeWidgetItem; import org.apache.commons.lang3.StringUtils; import java.io.Serializable; @@ -17,15 +16,15 @@ public class CsvExportBehavior implements Serializable { @CsvBindByName(column = "Epic") @CsvBindByPosition(position = 0) - private String epic; + private final String epic; @CsvBindByName(column = "Feature") @CsvBindByPosition(position = 1) - private String feature; + private final String feature; @CsvBindByName(column = "Story") @CsvBindByPosition(position = 2) - private String story; + private final String story; @CsvBindByName(column = "FAILED") @CsvBindByPosition(position = 3) diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java index f9f4c8335..917de198d 100644 --- a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java +++ b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportCategory.java @@ -2,8 +2,6 @@ import com.opencsv.bean.CsvBindByName; import com.opencsv.bean.CsvBindByPosition; -import io.qameta.allure.entity.Status; -import io.qameta.allure.entity.TestResult; import io.qameta.allure.tree.TreeWidgetItem; import java.io.Serializable; @@ -16,27 +14,27 @@ public class CsvExportCategory implements Serializable { @CsvBindByName(column = "Category") @CsvBindByPosition(position = 0) - private String name; + private final String name; @CsvBindByName(column = "FAILED") @CsvBindByPosition(position = 1) - private long failed; + private final long failed; @CsvBindByName(column = "BROKEN") @CsvBindByPosition(position = 2) - private long broken; + private final long broken; @CsvBindByName(column = "PASSED") @CsvBindByPosition(position = 3) - private long passed; + private final long passed; @CsvBindByName(column = "SKIPPED") @CsvBindByPosition(position = 4) - private long skipped; + private final long skipped; @CsvBindByName(column = "UNKNOWN") @CsvBindByPosition(position = 5) - private long unknown; + private final long unknown; public CsvExportCategory(final TreeWidgetItem item) { this.name = item.getName(); diff --git a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportSuite.java b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportSuite.java index ea18b98c9..217508313 100644 --- a/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportSuite.java +++ b/allure-plugin-api/src/main/java/io/qameta/allure/csv/CsvExportSuite.java @@ -14,24 +14,24 @@ public class CsvExportSuite implements Serializable { @CsvBindByName(column = "Status") @CsvBindByPosition(position = 0) - private String status; + private final String status; @CsvBindByName(column = "Name") @CsvBindByPosition(position = 1) - private String name; + private final String name; @CsvBindByName(column = "Duration in ms") @CsvBindByPosition(position = 2) - private String duration; + private final String duration; @CsvBindByName(column = "Description") @CsvBindByPosition(position = 3) - private String message; + private final String message; public CsvExportSuite(final TestResult result) { this.status = result.getStatus() != null ? result.getStatus().value() : null; this.name = result.getFullName(); - this.duration = result.getTime() != null ? result.getTime().getDuration().toString() : null; + this.duration = result.getTime().getDuration() != null ? result.getTime().getDuration().toString() : null; this.message = result.getDescription(); } @@ -39,31 +39,15 @@ public String getMessage() { return message; } - public void setMessage(final String message) { - this.message = message; - } - public String getDuration() { return duration; } - public void setDuration(final String duration) { - this.duration = duration; - } - public String getName() { return name; } - public void setName(final String name) { - this.name = name; - } - public String getStatus() { return status; } - - public void setStatus(final String status) { - this.status = status; - } } diff --git a/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java b/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java index dc2f2b52e..1afee730e 100644 --- a/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java +++ b/plugins/behaviors-plugin/src/main/java/io/qameta/allure/behaviors/BehaviorsPlugin.java @@ -7,13 +7,11 @@ import io.qameta.allure.core.Configuration; import io.qameta.allure.core.LaunchResults; import io.qameta.allure.csv.CsvExportBehavior; -import io.qameta.allure.csv.CsvExportCategory; import io.qameta.allure.entity.LabelName; import io.qameta.allure.entity.TestResult; import io.qameta.allure.tree.TestResultTree; import io.qameta.allure.tree.TestResultTreeGroup; import io.qameta.allure.tree.Tree; -import io.qameta.allure.tree.TreeNode; import io.qameta.allure.tree.TreeWidgetData; import io.qameta.allure.tree.TreeWidgetItem; import org.apache.commons.collections.CollectionUtils; @@ -21,7 +19,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -36,13 +33,13 @@ import static io.qameta.allure.entity.TestResult.comparingByTimeAsc; import static io.qameta.allure.tree.TreeUtils.calculateStatisticByChildren; import static io.qameta.allure.tree.TreeUtils.groupByLabels; -import static java.util.Optional.ofNullable; /** * The plugin adds behaviors tab to the report. * * @since 2.0 */ +@SuppressWarnings("PMD.ExcessiveImports") public class BehaviorsPlugin extends CompositeAggregator implements Widget { public static final String BEHAVIORS = "behaviors"; @@ -51,7 +48,8 @@ public class BehaviorsPlugin extends CompositeAggregator implements Widget { public static final String CSV_FILE_NAME = "behaviors.csv"; - private static final LabelName[] labelNames = new LabelName[] {EPIC, FEATURE, STORY}; + @SuppressWarnings("PMD.DefaultPackage") + /* default */ static final LabelName[] LABEL_NAMES = new LabelName[] {EPIC, FEATURE, STORY}; public BehaviorsPlugin() { super(Arrays.asList(new JsonAggregator(), new CsvExportAggregator())); @@ -63,7 +61,7 @@ public BehaviorsPlugin() { // @formatter:off final Tree behaviors = new TestResultTree( BEHAVIORS, - testResult -> groupByLabels(testResult, labelNames) + testResult -> groupByLabels(testResult, LABEL_NAMES) ); // @formatter:on @@ -122,20 +120,19 @@ private static class CsvExportAggregator extends CommonCsvExportAggregator getData(final List launchesResults) { final List exportBehaviors = new ArrayList<>(); launchesResults.stream().flatMap(launch -> launch.getResults().stream()).forEach(result -> { - Map> epicFeatureStoryMap = new HashMap<>(); - Arrays.asList(labelNames).forEach(labelName -> - epicFeatureStoryMap.put(labelName, result.findAllLabels(labelName))); - + Arrays.asList(LABEL_NAMES).forEach( + label -> epicFeatureStoryMap.put(label, result.findAllLabels(label)) + ); addTestResult(exportBehaviors, result, epicFeatureStoryMap); }); return exportBehaviors; } private void addTestResult(final List exportBehaviors, final TestResult result, - final Map> epicFeatureStoryMap) { + final Map> epicFeatureStoryMap) { if (epicFeatureStoryMap.isEmpty()) { - addTestResult(exportBehaviors, result, null, null, null); + addTestResultWithLabels(exportBehaviors, result, null, null, null); } else { addTestResultWithEpic(exportBehaviors, result, epicFeatureStoryMap); } @@ -144,8 +141,8 @@ private void addTestResult(final List exportBehaviors, final private void addTestResultWithEpic(final List exportBehaviors, final TestResult result, final Map> epicFeatureStoryMap) { if (!CollectionUtils.isEmpty(epicFeatureStoryMap.get(EPIC))) { - epicFeatureStoryMap.get(EPIC).forEach(epic -> - addTestResultWithFeature(exportBehaviors, result, epicFeatureStoryMap, epic) + epicFeatureStoryMap.get(EPIC).forEach( + epic -> addTestResultWithFeature(exportBehaviors, result, epicFeatureStoryMap, epic) ); } else { addTestResultWithFeature(exportBehaviors, result, epicFeatureStoryMap, null); @@ -156,8 +153,8 @@ private void addTestResultWithFeature(final List exportBehavi final Map> epicFeatureStoryMap, final String epic) { if (!CollectionUtils.isEmpty(epicFeatureStoryMap.get(FEATURE))) { - epicFeatureStoryMap.get(FEATURE).forEach(feature -> - addTestResultWithStories(exportBehaviors, result, epicFeatureStoryMap, epic, feature) + epicFeatureStoryMap.get(FEATURE).forEach( + feature -> addTestResultWithStories(exportBehaviors, result, epicFeatureStoryMap, epic, feature) ); } else { addTestResultWithStories(exportBehaviors, result, epicFeatureStoryMap, epic, null); @@ -168,16 +165,16 @@ private void addTestResultWithStories(final List exportBehavi final Map> epicFeatureStoryMap, final String epic, final String feature) { if (!CollectionUtils.isEmpty(epicFeatureStoryMap.get(STORY))) { - epicFeatureStoryMap.get(STORY).forEach(story -> - addTestResult(exportBehaviors, result, epic, feature, story) + epicFeatureStoryMap.get(STORY).forEach( + story -> addTestResultWithLabels(exportBehaviors, result, epic, feature, story) ); } else { - addTestResult(exportBehaviors, result, epic, feature, null); + addTestResultWithLabels(exportBehaviors, result, epic, feature, null); } } - private void addTestResult(final List exportBehaviors, final TestResult result, - final String epic, final String feature, final String story) { + private void addTestResultWithLabels(final List exportBehaviors, final TestResult result, + final String epic, final String feature, final String story) { Optional behavior = exportBehaviors.stream() .filter(exportBehavior -> exportBehavior.isPassed(epic, feature, story)).findFirst(); if (behavior.isPresent()) {