package net.thucydides.core.reports.adaptors.specflow; import ch.lambdaj.function.convert.Converter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import net.thucydides.core.model.*; import net.thucydides.core.reports.adaptors.common.FilebasedOutcomeAdaptor; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.List; import static ch.lambdaj.Lambda.convert; /** * Loads TestOutcomes from a specflow output file */ public class SpecflowAdaptor extends FilebasedOutcomeAdaptor { private static final String TITLE_LEAD = "***** "; /** * Loads TestOutcomes from a SpecFlow output file or a directory containing output files. * * This is the console output, not the XML output file, which does not contain the details about each step * execution. */ @Override public List<TestOutcome> loadOutcomesFrom(File source) throws IOException { if (source.isDirectory()) { List<TestOutcome> outcomes = Lists.newArrayList(); for(File outputFile : source.listFiles()) { outcomes.addAll(outcomesFromFile(outputFile)); } return outcomes; } else { return outcomesFromFile(source); } } private List<TestOutcome> outcomesFromFile(File outputFile) throws IOException { List<String> outputLines = FileUtils.readLines(outputFile, Charset.defaultCharset()); return convert(scenarioOutputsFrom(outputLines), toTestOutcomes()); } private Converter<List<String>, TestOutcome> toTestOutcomes() { return new Converter<List<String>, TestOutcome>() { @Override public TestOutcome convert(List<String> outputLines) { SpecflowScenarioTitleLine titleLine = new SpecflowScenarioTitleLine(outputLines.get(0)); Story story = Story.called(titleLine.getStoryTitle()).withPath(titleLine.getStoryPath()); TestOutcome outcome = TestOutcome.forTestInStory(titleLine.getScenarioTitle(), story); for(SpecflowScenario scenario : ScenarioSplitter.on(outputLines).split()) { if (scenario.usesDataTable()) { DataTable dataTable = DataTable.withHeaders(headersFrom(titleLine)).build(); outcome.useExamplesFrom(dataTable); recordRowSteps(outcome, scenario); } else { outcome.recordSteps(stepsFrom(scenario.getSteps())); } } return outcome; } }; } private void recordRowSteps(TestOutcome outcome, SpecflowScenario scenario) { for(SpecflowTableRow row : scenario.getRows()) { List<TestStep> rowSteps = stepsFrom(row.getRowSteps()); SpecflowScenarioTitleLine rowTitle = new SpecflowScenarioTitleLine(row.getRowTitle()); TestResult rowResult = TestResultList.of(getTestResults(rowSteps)).getOverallResult(); DataTableRow dataTableRow = new DataTableRow(rowTitle.getArguments()); dataTableRow.setResult(rowResult); outcome.addRow(dataTableRow); outcome.recordStep(TestStep.forStepCalled(rowTitle.getRowTitle()).withResult(rowResult)); outcome.startGroup(); outcome.recordSteps(rowSteps); outcome.endGroup(); } } private List<String> headersFrom(SpecflowScenarioTitleLine titleLine) { // TODO: This should eventually come from the .feature file List<String> headers = Lists.newArrayList(); for(int i = 0; i < titleLine.getArguments().size(); i++) { headers.add(Character.toString( (char) (65 + i))); } return headers; } private List<TestStep> stepsFrom(List<String> scenarioOutput) { List<TestStep> discoveredSteps = Lists.newArrayList(); ScenarioStepReader stepReader = new ScenarioStepReader(); List<String> lines = Lists.newArrayList(scenarioOutput); while (!lines.isEmpty()) { discoveredSteps.add(stepReader.consumeNextStepFrom(lines)); } return ImmutableList.copyOf(discoveredSteps); } private List<List<String>> scenarioOutputsFrom(List<String> outputLines) { List<List<String>> scenarios = Lists.newArrayList(); List<String> currentScenario = null; SpecflowScenarioTitleLine currentTitle = null; for (String line : outputLines) { if (isTitle(line)) { SpecflowScenarioTitleLine newTitleLine = new SpecflowScenarioTitleLine(line); if (currentTitle == null || !newTitleLine.getTitleName().equals(currentTitle.getTitleName())) { currentTitle = new SpecflowScenarioTitleLine(line); currentScenario = Lists.newArrayList(); scenarios.add(currentScenario); } } if (currentScenario != null) { currentScenario.add(line); } } return ImmutableList.copyOf(scenarios); } private boolean isTitle(String line) { return line.trim().startsWith(TITLE_LEAD); } private List<TestResult> getTestResults(List<TestStep> testSteps) { return convert(testSteps, new ExtractTestResultsConverter()); } private static class ExtractTestResultsConverter implements Converter<TestStep, TestResult> { public TestResult convert(final TestStep step) { return step.getResult(); } } }