TemplateableOutput.java
- package org.jbehave.core.reporters;
- import static org.jbehave.core.steps.StepCreator.PARAMETER_TABLE_END;
- import static org.jbehave.core.steps.StepCreator.PARAMETER_TABLE_START;
- import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_END;
- import static org.jbehave.core.steps.StepCreator.PARAMETER_VALUE_START;
- import static org.jbehave.core.steps.StepCreator.PARAMETER_VERBATIM_END;
- import static org.jbehave.core.steps.StepCreator.PARAMETER_VERBATIM_START;
- import java.io.File;
- import java.io.FileWriter;
- import java.io.Writer;
- import java.text.MessageFormat;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.apache.commons.lang3.StringUtils;
- import org.jbehave.core.annotations.AfterScenario.Outcome;
- import org.jbehave.core.annotations.Scope;
- import org.jbehave.core.configuration.Keywords;
- import org.jbehave.core.embedder.MetaFilter;
- import org.jbehave.core.model.ExamplesTable;
- import org.jbehave.core.model.GivenStories;
- import org.jbehave.core.model.Lifecycle;
- import org.jbehave.core.model.Meta;
- import org.jbehave.core.model.Narrative;
- import org.jbehave.core.model.OutcomesTable;
- import org.jbehave.core.model.Scenario;
- import org.jbehave.core.model.Story;
- import org.jbehave.core.model.StoryDuration;
- import org.jbehave.core.model.Verbatim;
- import org.jbehave.core.steps.StepCollector;
- import org.jbehave.core.steps.StepCreator.PendingStep;
- import org.jbehave.core.steps.Timing;
- import freemarker.ext.beans.BeansWrapper;
- import freemarker.template.TemplateHashModel;
- import freemarker.template.TemplateModelException;
- /**
- * <p>
- * Story reporter that outputs to a template.
- * </p>
- */
- public class TemplateableOutput extends NullStoryReporter {
- private final File file;
- private final Keywords keywords;
- private final TemplateProcessor processor;
- private final String templatePath;
- private OutputStory outputStory = new OutputStory();
- private OutputScenario outputScenario = new OutputScenario();
- private OutputStep failedStep;
- private OutputStep pendingStep;
- private Scope scope;
- private StepCollector.Stage stage;
- public TemplateableOutput(File file, Keywords keywords, TemplateProcessor processor, String templatePath) {
- this.file = file;
- this.keywords = keywords;
- this.processor = processor;
- this.templatePath = templatePath;
- }
- @Override
- public void storyExcluded(Story story, String filter) {
- this.outputStory.excludedBy = filter;
- }
- @Override
- public void beforeStory(Story story, boolean givenStory) {
- if (!givenStory) {
- this.outputStory = new OutputStory();
- this.outputStory.description = story.getDescription().asString();
- this.outputStory.path = story.getPath();
- this.scope = Scope.STORY;
- this.stage = StepCollector.Stage.BEFORE;
- }
- if (!story.getMeta().isEmpty()) {
- this.outputStory.meta = new OutputMeta(story.getMeta());
- }
- }
- @Override
- public void narrative(Narrative narrative) {
- if (!narrative.isEmpty()) {
- this.outputStory.narrative = new OutputNarrative(narrative);
- }
- }
- @Override
- public void lifecycle(Lifecycle lifecycle) {
- if (!lifecycle.isEmpty()) {
- this.outputStory.lifecycle = new OutputLifecycle(lifecycle);
- }
- }
- @Override
- public void scenarioExcluded(Scenario scenario, String filter) {
- this.outputScenario.excludedBy = filter;
- }
- @Override
- public void beforeScenario(Scenario scenario) {
- if (this.outputScenario.currentExample == null) {
- this.outputScenario = new OutputScenario();
- }
- this.outputScenario.title = scenario.getTitle();
- this.scope = Scope.SCENARIO;
- Meta meta = scenario.getMeta();
- if (!meta.isEmpty()) {
- this.outputScenario.meta = new OutputMeta(meta);
- }
- }
- private void addStep(OutputStep outputStep) {
- if (scope == Scope.STORY) {
- if (stage == StepCollector.Stage.BEFORE) {
- this.outputStory.addBeforeStep(outputStep);
- } else {
- this.outputStory.addAfterStep(outputStep);
- }
- } else {
- this.outputScenario.addStep(outputStep);
- }
- }
- @Override
- public void successful(String step) {
- addStep(new OutputStep(step, "successful"));
- }
- @Override
- public void ignorable(String step) {
- addStep(new OutputStep(step, "ignorable"));
- }
- @Override
- public void comment(String step) {
- addStep(new OutputStep(step, "comment"));
- }
- @Override
- public void pending(PendingStep step) {
- this.pendingStep = new OutputStep(step.stepAsString(), "pending");
- pendingStep.pendingMethod = step.getPendingMethod();
- addStep(pendingStep);
- }
- @Override
- public void notPerformed(String step) {
- addStep(new OutputStep(step, "notPerformed"));
- }
- @Override
- public void failed(String step, Throwable storyFailure) {
- this.failedStep = new OutputStep(step, "failed");
- failedStep.failure = storyFailure;
- addStep(failedStep);
- }
- @Override
- public void failedOutcomes(String step, OutcomesTable table) {
- failed(step, table.failureCause());
- this.failedStep.outcomes = table;
- }
- @Override
- public void givenStories(GivenStories givenStories) {
- if (!givenStories.getStories().isEmpty()) {
- this.outputScenario.givenStories = givenStories;
- }
- }
- @Override
- public void givenStories(List<String> storyPaths) {
- givenStories(new GivenStories(StringUtils.join(storyPaths, ",")));
- }
- @Override
- public void beforeExamples(List<String> steps, ExamplesTable table) {
- this.outputScenario.examplesSteps = steps;
- this.outputScenario.examplesTable = table;
- }
- @Override
- public void example(Map<String, String> parameters, int exampleIndex) {
- this.outputScenario.examples.add(parameters);
- this.outputScenario.currentExample = parameters;
- }
- @Override
- public void afterExamples() {
- this.outputScenario.currentExample = null;
- }
- @Override
- public void afterScenario(Timing timing) {
- if (this.outputScenario.currentExample == null) {
- this.outputStory.scenarios.add(outputScenario);
- }
- this.scope = Scope.STORY;
- this.stage = StepCollector.Stage.AFTER;
- }
- @Override
- public void pendingMethods(List<String> methods) {
- this.outputStory.pendingMethods = methods;
- }
- @Override
- public void restarted(String step, Throwable cause) {
- addStep(new OutputRestart(step, cause.getMessage()));
- }
-
- @Override
- public void restartedStory(Story story, Throwable cause) {
- addStep(new OutputRestart(story.getName(), cause.getMessage()));
- }
- @Override
- public void storyCancelled(Story story, StoryDuration storyDuration) {
- this.outputStory.cancelled = true;
- this.outputStory.storyDuration = storyDuration;
- }
- @Override
- public void afterStory(boolean givenStory) {
- if (!givenStory) {
- Map<String, Object> model = newDataModel();
- model.put("story", outputStory);
- model.put("keywords", new OutputKeywords(keywords));
- TemplateHashModel enumModels = BeansWrapper.getDefaultInstance().getEnumModels();
- TemplateHashModel escapeEnums;
- try {
- String escapeModeEnum = EscapeMode.class.getCanonicalName();
- escapeEnums = (TemplateHashModel) enumModels.get(escapeModeEnum);
- model.put("EscapeMode", escapeEnums);
- } catch (TemplateModelException e) {
- throw new IllegalArgumentException(e);
- }
- write(file, templatePath, model);
- }
- }
- private File write(File file, String resource, Map<String, Object> dataModel) {
- try {
- file.getParentFile().mkdirs();
- Writer writer = new FileWriter(file);
- processor.process(resource, dataModel, writer);
- writer.close();
- return file;
- } catch (Exception e) {
- throw new RuntimeException(resource, e);
- }
- }
- private Map<String, Object> newDataModel() {
- return new HashMap<>();
- }
- public static class OutputKeywords {
- private final Keywords keywords;
- public OutputKeywords(Keywords keywords) {
- this.keywords = keywords;
- }
- public String getLifecycle() {
- return keywords.lifecycle();
- }
- public String getScope() {
- return keywords.scope();
- }
- public String getScopeScenario() {
- return keywords.scopeScenario();
- }
- public String getScopeStory() {
- return keywords.scopeStory();
- }
- public String getBefore() {
- return keywords.before();
- }
- public String getAfter() {
- return keywords.after();
- }
- public String getMeta() {
- return keywords.meta();
- }
- public String getMetaProperty() {
- return keywords.metaProperty();
- }
- public String getNarrative() {
- return keywords.narrative();
- }
- public String getInOrderTo() {
- return keywords.inOrderTo();
- }
- public String getAsA() {
- return keywords.asA();
- }
- public String getiWantTo() {
- return keywords.iWantTo();
- }
- public String getSoThat() {
- return keywords.soThat();
- }
- public String getScenario() {
- return keywords.scenario();
- }
- public String getGivenStories() {
- return keywords.givenStories();
- }
- public String getExamplesTable() {
- return keywords.examplesTable();
- }
- public String getExamplesTableRow() {
- return keywords.examplesTableRow();
- }
- public String getExamplesTableHeaderSeparator() {
- return keywords.examplesTableHeaderSeparator();
- }
- public String getExamplesTableValueSeparator() {
- return keywords.examplesTableValueSeparator();
- }
- public String getExamplesTableIgnorableSeparator() {
- return keywords.examplesTableIgnorableSeparator();
- }
- public String getGiven() {
- return keywords.given();
- }
- public String getWhen() {
- return keywords.when();
- }
- public String getThen() {
- return keywords.then();
- }
- public String getAnd() {
- return keywords.and();
- }
- public String getIgnorable() {
- return keywords.ignorable();
- }
- public String getPending() {
- return keywords.pending();
- }
- public String getNotPerformed() {
- return keywords.notPerformed();
- }
- public String getFailed() {
- return keywords.failed();
- }
- public String getDryRun() {
- return keywords.dryRun();
- }
- public String getStoryCancelled() {
- return keywords.storyCancelled();
- }
- public String getDuration() {
- return keywords.duration();
- }
- public String getOutcome() {
- return keywords.outcome();
- }
-
- public String getMetaFilter() {
- return keywords.metaFilter();
- }
-
- public String getYes() {
- return keywords.yes();
- }
- public String getNo() {
- return keywords.no();
- }
- }
- public static class OutputStory {
- private String description;
- private String path;
- private OutputMeta meta;
- private OutputNarrative narrative;
- private OutputLifecycle lifecycle;
- private String excludedBy;
- private List<String> pendingMethods;
- private List<OutputStep> beforeSteps = new ArrayList<>();
- private List<OutputStep> afterSteps = new ArrayList<>();
- private List<OutputScenario> scenarios = new ArrayList<>();
- private boolean cancelled;
- private StoryDuration storyDuration;
- public String getDescription() {
- return description;
- }
- public String getPath() {
- return path;
- }
- public OutputMeta getMeta() {
- return meta;
- }
- public OutputNarrative getNarrative() {
- return narrative;
- }
- public OutputLifecycle getLifecycle() {
- return lifecycle;
- }
- public String getExcludedBy() {
- return excludedBy;
- }
- public void addBeforeStep(OutputStep outputStep) {
- this.beforeSteps.add(outputStep);
- }
- public void addAfterStep(OutputStep outputStep) {
- this.afterSteps.add(outputStep);
- }
- public List<OutputStep> getBeforeSteps() {
- return beforeSteps;
- }
- public List<OutputStep> getAfterSteps() {
- return afterSteps;
- }
- public List<String> getPendingMethods() {
- return pendingMethods;
- }
- public List<OutputScenario> getScenarios() {
- return scenarios;
- }
- public boolean isCancelled() {
- return cancelled;
- }
- public StoryDuration getStoryDuration() {
- return storyDuration;
- }
- }
- public static class OutputMeta {
- private final Meta meta;
- public OutputMeta(Meta meta) {
- this.meta = meta;
- }
- public Map<String, String> getProperties() {
- Map<String, String> properties = new HashMap<>();
- for (String name : meta.getPropertyNames()) {
- properties.put(name, meta.getProperty(name));
- }
- return properties;
- }
- }
- public static class OutputNarrative {
- private final Narrative narrative;
- public OutputNarrative(Narrative narrative) {
- this.narrative = narrative;
- }
- public String getInOrderTo() {
- return narrative.inOrderTo();
- }
- public String getAsA() {
- return narrative.asA();
- }
- public String getiWantTo() {
- return narrative.iWantTo();
- }
-
- public String getSoThat() {
- return narrative.soThat();
- }
-
- public boolean isAlternative() {
- return narrative.isAlternative();
- }
- }
- public static class OutputLifecycle {
- private final Lifecycle lifecycle;
- public OutputLifecycle(Lifecycle lifecycle) {
- this.lifecycle = lifecycle;
- }
- public Set<Scope> getScopes() {
- return lifecycle.getScopes();
- }
- public boolean hasBeforeSteps() {
- return lifecycle.hasBeforeSteps();
- }
- public List<String> getBeforeSteps() {
- return lifecycle.getBeforeSteps();
- }
- public List<String> getBeforeSteps(Scope scope) {
- return lifecycle.getBeforeSteps(scope);
- }
- public boolean hasAfterSteps() {
- return lifecycle.hasAfterSteps();
- }
- public List<String> getAfterSteps() {
- return lifecycle.getAfterSteps();
- }
- public List<String> getAfterSteps(Scope scope) {
- return lifecycle.getAfterSteps(scope);
- }
- public List<String> getAfterSteps(Scope scope, Outcome outcome) {
- return lifecycle.getAfterSteps(scope, outcome);
- }
- public List<String> getAfterSteps(Outcome outcome) {
- return lifecycle.getAfterSteps(outcome);
- }
- public List<String> getAfterSteps(Outcome outcome, Meta meta) {
- return lifecycle.getAfterSteps(outcome, meta);
- }
- public Set<Outcome> getOutcomes() {
- return lifecycle.getOutcomes();
- }
- public MetaFilter getMetaFilter(Outcome outcome) {
- return lifecycle.getMetaFilter(outcome);
- }
- }
- public static class OutputScenario {
- private String title;
- private List<OutputStep> steps = new ArrayList<>();
- private OutputMeta meta;
- private GivenStories givenStories;
- private String excludedBy;
- private List<String> examplesSteps;
- private ExamplesTable examplesTable;
- private Map<String, String> currentExample;
- private List<Map<String, String>> examples = new ArrayList<>();
- private Map<Map<String, String>, List<OutputStep>> stepsByExample = new HashMap<>();
- public String getTitle() {
- return title;
- }
- public void addStep(OutputStep outputStep) {
- if (examplesTable == null) {
- steps.add(outputStep);
- } else {
- List<OutputStep> currentExampleSteps = stepsByExample.get(currentExample);
- if (currentExampleSteps == null) {
- currentExampleSteps = new ArrayList<>();
- stepsByExample.put(currentExample, currentExampleSteps);
- }
- currentExampleSteps.add(outputStep);
- }
- }
- public List<OutputStep> getSteps() {
- return steps;
- }
- public List<OutputStep> getStepsByExample(Map<String, String> example) {
- List<OutputStep> steps = stepsByExample.get(example);
- if (steps == null) {
- return new ArrayList<>();
- }
- return steps;
- }
- public OutputMeta getMeta() {
- return meta;
- }
- public GivenStories getGivenStories() {
- return givenStories;
- }
- public String getExcludedBy() {
- return excludedBy;
- }
- public List<String> getExamplesSteps() {
- return examplesSteps;
- }
- public ExamplesTable getExamplesTable() {
- return examplesTable;
- }
- public List<Map<String, String>> getExamples() {
- return examples;
- }
- }
- public static class OutputRestart extends OutputStep {
- public OutputRestart(String step, String outcome) {
- super(step, outcome);
- }
- }
- public static class OutputStep {
- private final String step;
- private final String outcome;
- private Throwable failure;
- private OutcomesTable outcomes;
- private List<OutputParameter> parameters;
- private String stepPattern;
- private String tableAsString;
- private ExamplesTable table;
- private String verbatimAsString;
- private Verbatim verbatim;
- private String pendingMethod;
- public OutputStep(String step, String outcome) {
- this.step = step;
- this.outcome = outcome;
- parseTableAsString();
- parseVerbatimAsString();
- parseParameters();
- createStepPattern();
- }
- public String getStep() {
- return step;
- }
- public String getStepPattern() {
- return stepPattern;
- }
- public List<OutputParameter> getParameters() {
- return parameters;
- }
- public String getOutcome() {
- return outcome;
- }
- public Throwable getFailure() {
- return failure;
- }
- public String getPendingMethod() {
- return pendingMethod;
- }
- public String getFailureCause() {
- if (failure != null) {
- return new StackTraceFormatter(true).stackTrace(failure);
- }
- return "";
- }
- public ExamplesTable getTable() {
- return table;
- }
- public Verbatim getVerbatim() {
- return verbatim;
- }
- public OutcomesTable getOutcomes() {
- return outcomes;
- }
- public String getOutcomesFailureCause() {
- if (outcomes.failureCause() != null) {
- return new StackTraceFormatter(true).stackTrace(outcomes.failureCause());
- }
- return "";
- }
- public String getFormattedStep(String parameterPattern) {
- return getFormattedStep(EscapeMode.NONE, parameterPattern);
- }
- public String getFormattedStep(EscapeMode escapeMode, String parameterPattern) {
- // note that escaping the stepPattern string only works
- // because placeholders for parameters do not contain
- // special chars (the placeholder is {0} etc)
- String escapedStep = escapeMode.escapeString(stepPattern);
- if (!parameters.isEmpty()) {
- try {
- return MessageFormat.format(escapedStep, formatParameters(escapeMode, parameterPattern));
- } catch (RuntimeException e) {
- throw new StepFormattingFailed(stepPattern, parameterPattern, parameters, e);
- }
- }
- return escapedStep;
- }
- private Object[] formatParameters(EscapeMode escapeMode, String parameterPattern) {
- Object[] arguments = new Object[parameters.size()];
- for (int a = 0; a < parameters.size(); a++) {
- arguments[a] = MessageFormat.format(parameterPattern,
- escapeMode.escapeString(parameters.get(a).getValue()));
- }
- return arguments;
- }
- private void parseParameters() {
- // first, look for parameterized scenarios
- parameters = findParameters(PARAMETER_VALUE_START + PARAMETER_VALUE_START, PARAMETER_VALUE_END
- + PARAMETER_VALUE_END);
- // second, look for normal scenarios
- if (parameters.isEmpty()) {
- parameters = findParameters(PARAMETER_VALUE_START, PARAMETER_VALUE_END);
- }
- }
- private List<OutputParameter> findParameters(String start, String end) {
- List<OutputParameter> parameters = new ArrayList<>();
- Matcher matcher = Pattern.compile("(" + start + ".*?" + end + ")(\\W|\\Z)",
- Pattern.DOTALL).matcher(step);
- while (matcher.find()) {
- parameters.add(new OutputParameter(step, matcher.start(), matcher.end()));
- }
- return parameters;
- }
- private void parseTableAsString() {
- if (step.contains(PARAMETER_TABLE_START) && step.contains(PARAMETER_TABLE_END)) {
- tableAsString = StringUtils.substringBetween(step, PARAMETER_TABLE_START, PARAMETER_TABLE_END);
- table = new ExamplesTable(tableAsString);
- }
- }
- private void parseVerbatimAsString() {
- if (step.contains(PARAMETER_VERBATIM_START) && step.contains(PARAMETER_VERBATIM_END)) {
- verbatimAsString = StringUtils.substringBetween(step, PARAMETER_VERBATIM_START, PARAMETER_VERBATIM_END);
- verbatim = new Verbatim(verbatimAsString);
- }
- }
- private void createStepPattern() {
- this.stepPattern = step;
- if (tableAsString != null) {
- this.stepPattern = StringUtils.replaceOnce(stepPattern, PARAMETER_TABLE_START + tableAsString
- + PARAMETER_TABLE_END, "");
- }
- if (verbatimAsString != null) {
- this.stepPattern = StringUtils.replaceOnce(stepPattern, PARAMETER_VERBATIM_START + verbatimAsString
- + PARAMETER_VERBATIM_END, "");
- }
- for (int count = 0; count < parameters.size(); count++) {
- String value = parameters.get(count).toString();
- this.stepPattern = stepPattern.replace(value, "{" + count + "}");
- }
- }
- @SuppressWarnings("serial")
- public static class StepFormattingFailed extends RuntimeException {
- public StepFormattingFailed(String stepPattern, String parameterPattern, List<OutputParameter> parameters,
- RuntimeException cause) {
- super("Failed to format step '" + stepPattern + "' with parameter pattern '" + parameterPattern
- + "' and parameters: " + parameters, cause);
- }
- }
- }
- public static class OutputParameter {
- private final String parameter;
- public OutputParameter(String pattern, int start, int end) {
- this.parameter = pattern.substring(start, end).trim();
- }
- public String getValue() {
- String value = StringUtils.remove(parameter, PARAMETER_VALUE_START);
- value = StringUtils.remove(value, PARAMETER_VALUE_END);
- return value;
- }
- @Override
- public String toString() {
- return parameter;
- }
- }
- }