View Javadoc
1   /*
2    * Copyright 2023 Adrian Herscu
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package dev.aherscu.qa.jgiven.reporter;
18  
19  import static com.google.common.collect.Maps.*;
20  import static com.google.common.collect.Multimaps.*;
21  import static dev.aherscu.qa.testing.utils.FileUtilsExtensions.*;
22  import static dev.aherscu.qa.testing.utils.StringUtilsExtensions.*;
23  import static java.util.stream.Collectors.toMap;
24  import static org.apache.commons.io.FilenameUtils.*;
25  
26  import java.io.*;
27  import java.text.*;
28  import java.util.*;
29  
30  import org.jooq.lambda.*;
31  import org.testng.xml.*;
32  
33  import com.google.common.collect.*;
34  import com.tngtech.jgiven.report.json.*;
35  import com.tngtech.jgiven.report.model.*;
36  
37  import lombok.*;
38  import lombok.experimental.*;
39  import lombok.extern.slf4j.*;
40  
41  /**
42   * Per test method reporter.
43   */
44  @SuperBuilder(toBuilder = true)
45  @NoArgsConstructor(force = true)
46  @Slf4j
47  @ToString(callSuper = true)
48  public class QaJGivenPerMethodReporter
49      extends AbstractQaJgivenReporter<ScenarioModel, QaJGivenPerMethodReporter> {
50      public static final String DEFAULT_TEMPLATE_RESOURCE =
51          "/qa-jgiven-permethod-reporter.html";
52  
53      @SneakyThrows
54      public static Map<String, String> readAttributesOf(final File reportFile) {
55          try (val attributesReader = fileReader(
56              new File(reportFile.getAbsolutePath() + ".attributes"))) {
57              val p = new Properties();
58              p.load(attributesReader);
59              return fromProperties(p);
60          }
61      }
62  
63      /**
64       * Builds a new reporter configured with additional TestNG XML suite
65       * parameters. Currently, only {@code templateResource} is recognized.
66       *
67       * @param xmlSuite
68       *            TestNG XML suite
69       * @return reporter configured
70       */
71      @Override
72      protected QaJGivenPerMethodReporter with(final XmlSuite xmlSuite) {
73          return ((QaJGivenPerMethodReporter) super.with(xmlSuite))
74              .toBuilder()
75              .templateResource(templateResourceParamFrom(xmlSuite,
76                  DEFAULT_TEMPLATE_RESOURCE))
77              .build();
78      }
79  
80      @SneakyThrows
81      protected void applyAttributesFor(
82          final ScenarioModel scenarioModel,
83          final File reportFile) {
84          log.info("setting attributes for " + reportFile.getName());
85  
86          try (val attributesWriter = fileWriter(
87              new File(reportFile.getAbsolutePath() + ".attributes"))) {
88              val p = new Properties();
89              p.putAll(scenarioModel.getTagIds().stream()
90                  // TODO apply the mapping here
91                  .map(tag -> immutableEntry(substringBefore(tag, DASH),
92                      substringAfter(tag, DASH)))
93                  // NOTE there might be multiple
94                  // DeviceName/PlatformName/PlatformVersion tags
95                  .collect(toMultimap(Map.Entry::getKey, Map.Entry::getValue,
96                      MultimapBuilder.hashKeys().arrayListValues()::build))
97                  .asMap().entrySet().stream()
98                  // NOTE here we merge them all under one key
99                  .map(e -> immutableEntry(e.getKey(),
100                     String.join(COMMA, e.getValue())))
101                 .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)));
102             p.store(attributesWriter,
103                 "generated by qa-jgiven-reporter-maven-plugin");
104         }
105     }
106 
107     /**
108      * Generates a report for each test method (scenario in JGiven terms).
109      */
110     @Override
111     @SneakyThrows
112     public void generate() {
113         listJGivenReports()
114             .parallelStream()
115             .peek(reportModelFile -> log
116                 .debug("reading " + reportModelFile.getName()))
117             .flatMap(reportModelFile -> new ScenarioJsonReader()
118                 .apply(reportModelFile)
119                 .getScenarios()
120                 .stream()
121                 .filter(scenarioModel -> scenarioModel
122                     .getTagIds()
123                     .stream()
124                     .anyMatch(tagId -> tagId.contains(referenceTag))))
125             .peek(scenarioModel -> log
126                 .debug("processing " + targetNameFor(scenarioModel)))
127             .forEach(Unchecked.consumer(scenarioModel -> {
128                 val targetReportFile = new File(outputDirectory,
129                     targetNameFor(scenarioModel)
130                         + EXTENSION_SEPARATOR_STR
131                 // TODO make it read .mustache files and drop the extension here
132                         + getExtension(templateResource));
133                 try (val reportWriter = fileWriter(targetReportFile)) {
134                     template()
135                         .execute(reportModel(targetReportFile)
136                             .toBuilder()
137                             .jgivenReport(scenarioModel)
138                             .screenshotScale(screenshotScale)
139                             .datePattern(datePattern)
140                             .build(),
141                             reportWriter);
142                     applyAttributesFor(scenarioModel, targetReportFile);
143                 }
144 
145                 // FIXME should work only with html reports
146                 // if (pdf) {
147                 // renderToPDF(
148                 // reportFile(reportModelFile, ".html"),
149                 // reportFile(reportModelFile, ".pdf")
150                 // .getAbsolutePath());
151                 // }
152 
153                 reportGenerated(scenarioModel, targetReportFile);
154             }));
155     }
156 
157     protected void reportGenerated(
158         final ScenarioModel scenarioModel,
159         final File reportFile) {
160         log.debug("report generated for {} into {}",
161             scenarioModel.getClassName(), reportFile.getName());
162     }
163 
164     protected String targetNameFor(final ScenarioModel scenarioModel) {
165         return MessageFormat.format("{0}-{1}-{2}",
166             scenarioModel.getExecutionStatus(), scenarioModel.getClassName(),
167             scenarioModel.getTestMethodName());
168     }
169 }