package net.thucydides.core.reports.xml;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import net.thucydides.core.model.*;
import net.thucydides.core.model.features.ApplicationFeature;
import net.thucydides.core.screenshots.ScreenshotAndHtmlSource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import java.io.File;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
/**
* XStream converter used to generate the XML acceptance test report.
*
* @author johnsmart
*/
public class TestOutcomeConverter implements Converter {
private static final String TITLE_FIELD = "title";
private static final String NAME_FIELD = "name";
private static final String DESCRIPTION_FIELD = "description";
private static final String ID_FIELD = "id";
private static final String PATH_FIELD = "path";
private static final String STEPS_FIELD = "steps";
private static final String SUCCESSFUL_FIELD = "successful";
private static final String FAILURES_FIELD = "failures";
private static final String ERRORS_FIELD = "errors";
private static final String SKIPPED_FIELD = "skipped";
private static final String IGNORED_FIELD = "ignored";
private static final String PENDING_FIELD = "pending";
private static final String RESULT_FIELD = "result";
private static final String ANNOTATED_RESULT_FIELD = "annotated-result";
private static final String TEST_GROUP = "test-group";
private static final String TEST_STEP = "test-step";
private static final String USER_STORY = "user-story";
private static final String FEATURE = "feature";
private static final String ISSUES = "issues";
private static final String ISSUE = "issue";
private static final String VERSIONS = "versions";
private static final String VERSION = "version";
private static final String TAGS = "tags";
private static final String TAG = "tag";
private static final String QUALIFIER_FIELD = "qualifier";
private static final String TAG_NAME = "name";
private static final String TAG_TYPE = "type";
private static final String EXCEPTION = "exception";
private static final String ERROR = "error";
private static final String SCREENSHOT_LIST_FIELD = "screenshots";
private static final String SCREENSHOT_FIELD = "screenshot";
private static final String SCREENSHOT_IMAGE = "image";
private static final String SCREENSHOT_SOURCE = "source";
private static final String DESCRIPTION = "description";
private static final String DURATION = "duration";
private static final String TIMESTAMP = "timestamp";
private static final String SESSION_ID = "session-id";
private static final String EXAMPLES = "examples";
private static final String HEADERS = "headers";
private static final String HEADER = "header";
private static final String ROWS = "rows";
private static final String ROW = "row";
private static final String VALUE = "value";
private static final String MANUAL = "manual";
public static final String NEW_LINE_CHAR = "\n";
public static final String ESCAPE_CHAR_FOR_NEW_LINE = "
";
private static final String DEFAULT_ERROR_MESSAGE = "Unspecified failure";
public TestOutcomeConverter() {
}
/**
* Determines which classes this converter applies to.
*/
@SuppressWarnings("rawtypes")
public boolean canConvert(final Class clazz) {
return TestOutcome.class.isAssignableFrom(clazz);
}
/**
* Generate an XML report given an TestOutcome object.
*/
public void marshal(final Object value, final HierarchicalStreamWriter writer,
final MarshallingContext context) {
TestOutcome testOutcome = (TestOutcome) value;
Preconditions.checkNotNull(testOutcome, "The test run was null - WTF?");
writer.addAttribute(TITLE_FIELD, escape(titleFrom(testOutcome)));
writer.addAttribute(NAME_FIELD, nameFrom(testOutcome));
if (testOutcome.getDescription() != null) {
writer.addAttribute(DESCRIPTION_FIELD, escape(descriptionFrom(testOutcome)));
}
if (testOutcome.getQualifier() != null && testOutcome.getQualifier().isPresent()) {
writer.addAttribute(QUALIFIER_FIELD, escape(testOutcome.getQualifier().get()));
}
writer.addAttribute(STEPS_FIELD, Integer.toString(testOutcome.countTestSteps()));
writer.addAttribute(SUCCESSFUL_FIELD, Integer.toString(testOutcome.getSuccessCount()));
writer.addAttribute(FAILURES_FIELD, Integer.toString(testOutcome.getFailureCount()));
if (testOutcome.getErrorCount() > 0) {
writer.addAttribute(ERRORS_FIELD, Integer.toString(testOutcome.getErrorCount()));
}
writer.addAttribute(SKIPPED_FIELD, Integer.toString(testOutcome.getSkippedCount()));
writer.addAttribute(IGNORED_FIELD, Integer.toString(testOutcome.getIgnoredCount()));
writer.addAttribute(PENDING_FIELD, Integer.toString(testOutcome.getPendingCount()));
if (testOutcome.getAnnotatedResult() != null) {
writer.addAttribute(ANNOTATED_RESULT_FIELD, testOutcome.getAnnotatedResult().name());
}
writer.addAttribute(RESULT_FIELD, testOutcome.getResult().name());
writer.addAttribute(DURATION, Long.toString(testOutcome.getDuration()));
writer.addAttribute(TIMESTAMP, formattedTimestamp(testOutcome.getStartTime()));
if (testOutcome.isManual()) {
writer.addAttribute(MANUAL, "true");
}
if (isNotEmpty(testOutcome.getSessionId())) {
writer.addAttribute(SESSION_ID, testOutcome.getSessionId());
}
addUserStoryTo(writer, testOutcome.getUserStory());
addIssuesTo(writer, testOutcome.getIssues());
addVersionsTo(writer, testOutcome.getVersions());
addTagsTo(writer, testOutcome.getTags());
addExamplesTo(writer, testOutcome.getDataTable());
List<TestStep> steps = testOutcome.getTestSteps();
for (TestStep step : steps) {
writeStepTo(writer, step);
}
}
private String formattedTimestamp(DateTime startTime) {
return startTime.toString();
}
private String escape(String attribute) {
if (StringUtils.isNotEmpty(attribute)) {
attribute = StringUtils.replace(attribute, NEW_LINE_CHAR, ESCAPE_CHAR_FOR_NEW_LINE);
}
return attribute;
}
private String unescape(String attribute) {
if (StringUtils.isNotEmpty(attribute)) {
attribute = StringUtils.replace(attribute, ESCAPE_CHAR_FOR_NEW_LINE, NEW_LINE_CHAR);
}
return attribute;
}
private String titleFrom(final TestOutcome testOutcome) {
return testOutcome.getTitle();
}
private String descriptionFrom(final TestOutcome testOutcome) {
return testOutcome.getDescription();
}
private String nameFrom(final TestOutcome testOutcome) {
if (testOutcome.getMethodName() != null) {
return testOutcome.getMethodName();
} else {
return testOutcome.getTitle();
}
}
private void writeStepTo(final HierarchicalStreamWriter writer, final TestStep step) {
if (step.isAGroup()) {
writer.startNode(TEST_GROUP);
writer.addAttribute(NAME_FIELD, step.getDescription());
writeResult(writer, step);
writeScreenshotIfPresent(writer, step);
List<TestStep> nestedSteps = step.getChildren();
for (TestStep nestedStep : nestedSteps) {
writeStepTo(writer, nestedStep);
}
writer.endNode();
} else {
writer.startNode(TEST_STEP);
writeResult(writer, step);
writer.addAttribute(DURATION, Long.toString(step.getDuration()));
writeScreenshotIfPresent(writer, step);
writeDescription(writer, step);
writeErrorForFailingTest(writer, step);
writer.endNode();
}
}
private void addUserStoryTo(final HierarchicalStreamWriter writer, final Story userStory) {
if (userStory != null) {
writer.startNode(USER_STORY);
writer.addAttribute(ID_FIELD, userStory.getId());
writer.addAttribute(NAME_FIELD, userStory.getName());
if (userStory.getPath() != null) {
writer.addAttribute(PATH_FIELD, userStory.getPath());
}
if (userStory.getFeature() != null) {// userStory.getFeatureClass() != null) {
writeFeatureNode(writer, userStory);
}
writer.endNode();
}
}
private void writeFeatureNode(HierarchicalStreamWriter writer, Story userStory) {
ApplicationFeature feature = userStory.getFeature();// ApplicationFeature.from(userStory.getFeatureClass());
writer.startNode(FEATURE);
writer.addAttribute(ID_FIELD, feature.getId());
writer.addAttribute(NAME_FIELD, feature.getName());
writer.endNode();
}
private void addIssuesTo(final HierarchicalStreamWriter writer, final List<String> issues) {
if (!issues.isEmpty()) {
writer.startNode(ISSUES);
for (String issue : issues) {
writer.startNode(ISSUE);
writer.setValue(issue);
writer.endNode();
}
writer.endNode();
}
}
private void addVersionsTo(final HierarchicalStreamWriter writer, final List<String> versions) {
if (!versions.isEmpty()) {
writer.startNode(VERSIONS);
for (String version : versions) {
writer.startNode(VERSION);
writer.setValue(version);
writer.endNode();
}
writer.endNode();
}
}
private void addTagsTo(HierarchicalStreamWriter writer, Set<TestTag> tags) {
if (!CollectionUtils.isEmpty(tags)) {
writer.startNode(TAGS);
List<TestTag> orderedTags = Lists.newArrayList(tags);
Collections.sort(orderedTags);
for (TestTag tag : orderedTags) {
writer.startNode(TAG);
writer.addAttribute(TAG_NAME, tag.getName());
writer.addAttribute(TAG_TYPE, tag.getType());
writer.endNode();
}
writer.endNode();
}
}
private void addExamplesTo(HierarchicalStreamWriter writer, DataTable dataTable) {
if ((dataTable != null) && (!dataTable.getRows().isEmpty())) {
writer.startNode(EXAMPLES);
writeDatasetDescriptors(writer,dataTable);
writeHeaders(writer, dataTable);
writeRows(writer, dataTable);
writer.endNode();
}
}
private void writeHeaders(HierarchicalStreamWriter writer, DataTable dataTable) {
writer.startNode(HEADERS);
for(String header : dataTable.getHeaders()) {
writeHeader(writer, header);
}
writer.endNode();
}
private void writeRows(HierarchicalStreamWriter writer, DataTable dataTable) {
writer.startNode(ROWS);
for(DataTableRow rowData : dataTable.getRows()) {
writeRow(writer, rowData);
}
writer.endNode();
}
private void writeDatasetDescriptors(HierarchicalStreamWriter writer, DataTable dataTable) {
writer.startNode("datasets");
for(DataSetDescriptor descriptor : dataTable.getDataSetDescriptors()) {
writeDescriptor(writer, descriptor);
}
writer.endNode();
}
private void writeDescriptor(HierarchicalStreamWriter writer, DataSetDescriptor descriptor) {
writer.startNode("dataset");
writer.addAttribute("startRow", Integer.toString(descriptor.getStartRow()));
writer.addAttribute("rowCount", Integer.toString(descriptor.getRowCount()));
if (descriptor.getName() != null) {
writer.addAttribute("name", descriptor.getName());
}
if (descriptor.getDescription() != null) {
writer.addAttribute("description", descriptor.getDescription());
}
writer.endNode();
}
private void writeRow(HierarchicalStreamWriter writer, DataTableRow rowData) {
writer.startNode(ROW);
if (rowData.getResult() != TestResult.UNDEFINED) {
writer.addAttribute("result", rowData.getResult().toString());
}
for(Object cellValue : rowData.getValues()) {
writeCellValue(writer, cellValue.toString());
}
writer.endNode();
}
private void writeHeader(final HierarchicalStreamWriter writer, final String header) {
writer.startNode(HEADER);
writer.setValue(header);
writer.endNode();
}
private void writeCellValue(final HierarchicalStreamWriter writer, final String cellValue) {
writer.startNode(VALUE);
writer.setValue(cellValue);
writer.endNode();
}
private void writeErrorForFailingTest(final HierarchicalStreamWriter writer, final TestStep step) {
if (step.isFailure() || step.isError()) {
writeErrorMessageAndException(writer, step);
}
}
private void writeErrorMessageAndException(final HierarchicalStreamWriter writer,
final TestStep step) {
String errorMessage = isEmpty(step.getErrorMessage()) ? DEFAULT_ERROR_MESSAGE : step.getErrorMessage();
writeErrorMessageNode(writer, errorMessage);
if (step.getException() != null) {
writeFailureCauseNode(writer, step.getException());
}
}
private void writeFailureCauseNode(final HierarchicalStreamWriter writer, final FailureCause cause) {
writer.startNode(EXCEPTION);
writer.setValue(cause.getErrorType() + ":" + cause.getMessage() + System.lineSeparator() + Arrays.toString(cause.getStackTrace()));
writer.endNode();
}
private void writeErrorMessageNode(final HierarchicalStreamWriter writer,
final String errorMessage) {
writer.startNode(ERROR);
writer.setValue(errorMessage);
writer.endNode();
}
private void writeScreenshotIfPresent(final HierarchicalStreamWriter writer, final TestStep step) {
if ((step.getScreenshots() != null) && (step.getScreenshots().size() > 0)) {
writer.startNode(SCREENSHOT_LIST_FIELD);
for(ScreenshotAndHtmlSource screenshotAndHtmlSource : step.getScreenshots()) {
writer.startNode(SCREENSHOT_FIELD);
writer.addAttribute(SCREENSHOT_IMAGE, screenshotAndHtmlSource.getScreenshotFile().getName());
if (screenshotAndHtmlSource.getHtmlSource().isPresent()) {
writer.addAttribute(SCREENSHOT_SOURCE, screenshotAndHtmlSource.getHtmlSource().get().getName());
}
writer.endNode();
}
writer.endNode();
}
}
private void writeResult(final HierarchicalStreamWriter writer, final TestStep step) {
writer.addAttribute(RESULT_FIELD, step.getResult().toString());
}
private void writeDescription(final HierarchicalStreamWriter writer, final TestStep step) {
writer.startNode(DESCRIPTION);
writer.setValue(step.getDescription());
writer.endNode();
}
/**
* Convert XML to an TestOutcome object. Not needed for now.
*/
public Object unmarshal(final HierarchicalStreamReader reader,
final UnmarshallingContext context) {
String methodName = reader.getAttribute(NAME_FIELD);
TestOutcome testOutcome = new TestOutcome(methodName);
testOutcome.setTitle(unescape(reader.getAttribute(TITLE_FIELD)));
if (reader.getAttribute(DESCRIPTION_FIELD) != null) {
testOutcome.setDescription(unescape(reader.getAttribute(DESCRIPTION_FIELD)));
}
TestResult savedTestResult = TestResult.valueOf(reader.getAttribute(RESULT_FIELD));
TestResult savedAnnotatedResult = null;
if ((reader.getAttribute(ANNOTATED_RESULT_FIELD) != null)) {
savedAnnotatedResult = TestResult.valueOf(reader.getAttribute(ANNOTATED_RESULT_FIELD));
}
if (reader.getAttribute(QUALIFIER_FIELD) != null) {
testOutcome = testOutcome.withQualifier(unescape(reader.getAttribute(QUALIFIER_FIELD)));
}
Long duration = readDuration(reader);
testOutcome.setDuration(duration);
Optional<DateTime> startTime = readTimestamp(reader);
if (startTime.isPresent()) {
testOutcome.setStartTime(startTime.get());
}
boolean isManualTest = readManualTest(reader);
if (isManualTest) {
testOutcome = testOutcome.asManualTest();
}
String sessionId = readSessionId(reader);
testOutcome.setSessionId(sessionId);
readChildren(reader, testOutcome);
if (savedAnnotatedResult != null) {
testOutcome.setAnnotatedResult(savedAnnotatedResult);
}
if (savedAnnotatedResult == null && testOutcome.getStepCount().equals(0)) {
testOutcome.setAnnotatedResult(savedTestResult);
}
return testOutcome;
}
private boolean readManualTest(HierarchicalStreamReader reader) {
boolean isManualTest = false;
String manualTestAttribute = reader.getAttribute(MANUAL);
if (manualTestAttribute != null) {
isManualTest = Boolean.valueOf(manualTestAttribute);
}
return isManualTest;
}
private void readChildren(final HierarchicalStreamReader reader, final TestOutcome testOutcome) {
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(TEST_STEP)) {
readTestStep(reader, testOutcome);
} else if (childNode.equals(TEST_GROUP)) {
readTestGroup(reader, testOutcome);
} else if (childNode.equals(ISSUES)) {
readTestRunIssues(reader, testOutcome);
} else if (childNode.equals(VERSIONS)) {
readTestRunVersions(reader, testOutcome);
} else if (childNode.equals(USER_STORY)) {
readUserStory(reader, testOutcome);
} else if (childNode.equals(TAGS)) {
readTags(reader, testOutcome);
} else if (childNode.equals(EXAMPLES)) {
readExamples(reader, testOutcome);
}
reader.moveUp();
}
}
private void readUserStory(final HierarchicalStreamReader reader,
final TestOutcome testOutcome) {
String storyId = reader.getAttribute(ID_FIELD);
String storyName = reader.getAttribute(NAME_FIELD);
String storyPath = reader.getAttribute(PATH_FIELD);
ApplicationFeature feature = null;
if (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(FEATURE)) {
feature = readFeature(reader);
}
reader.moveUp();
}
Story story;
if (feature == null) {
story = Story.withIdAndPath(storyId, storyName,storyPath);
} else {
story = Story.withIdAndPathAndFeature(storyId, storyName, storyPath, feature.getId(), feature.getName());
}
testOutcome.setUserStory(story);
}
private ApplicationFeature readFeature(final HierarchicalStreamReader reader) {
String featureId = reader.getAttribute(ID_FIELD);
String featureName = reader.getAttribute(NAME_FIELD);
return new ApplicationFeature(featureId, featureName);
}
private void readTestRunIssues(final HierarchicalStreamReader reader,
final TestOutcome testOutcome) {
while (reader.hasMoreChildren()) {
reader.moveDown();
String issue = reader.getValue();
testOutcome.isRelatedToIssue(issue);
reader.moveUp();
}
}
private void readTestRunVersions(final HierarchicalStreamReader reader,
final TestOutcome testOutcome) {
while (reader.hasMoreChildren()) {
reader.moveDown();
String version = reader.getValue();
testOutcome.addVersion(version);
reader.moveUp();
}
}
private void readTags(final HierarchicalStreamReader reader,
final TestOutcome testOutcome) {
Set<TestTag> tags = new HashSet<TestTag>();
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(TAG)) {
tags.add(readTag(reader));
}
reader.moveUp();
}
testOutcome.setTags(tags);
}
private void readExamples(final HierarchicalStreamReader reader,
final TestOutcome testOutcome) {
List<String> headers = Lists.newArrayList();
List<DataTableRow> rows = Lists.newArrayList();
List<DataSetDescriptor> descriptors = Lists.newArrayList();
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(HEADERS)) {
headers = readHeaders(reader);
} else if (childNode.equals(ROWS)) {
rows = readRows(reader);
} else if (childNode.equals("datasets")) {
descriptors = readDescriptors(reader);
}
reader.moveUp();
}
DataTable table = DataTable.withHeaders(headers).andRowData(rows).andDescriptors(descriptors).build();
testOutcome.useExamplesFrom(table);
}
private List<String> readHeaders(final HierarchicalStreamReader reader) {
List<String> headers = Lists.newArrayList();
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(HEADER)) {
headers.add(reader.getValue());
}
reader.moveUp();
}
return headers;
}
private List<DataTableRow> readRows(final HierarchicalStreamReader reader) {
List<DataTableRow> rows = Lists.newArrayList();
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(ROW)) {
rows.add(readRow(reader));
}
reader.moveUp();
}
return rows;
}
private List<DataSetDescriptor> readDescriptors(final HierarchicalStreamReader reader) {
List<DataSetDescriptor> descriptors = Lists.newArrayList();
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals("dataset")) {
int startRow = Integer.parseInt(reader.getAttribute("startRow"));
int rowCount = Integer.parseInt(reader.getAttribute("rowCount"));
String name = reader.getAttribute("name");
String description = reader.getAttribute("description");
descriptors.add(new DataSetDescriptor(startRow,rowCount,name, description));
}
reader.moveUp();
}
return descriptors;
}
private DataTableRow readRow(final HierarchicalStreamReader reader) {
List<String> rowValues = Lists.newArrayList();
TestResult result = null;
String resultValue = reader.getAttribute("result");
while (reader.hasMoreChildren()) {
if (resultValue != null) {
result = TestResult.valueOf(resultValue);
} else {
result = TestResult.SUCCESS;
}
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(VALUE)) {
rowValues.add(reader.getValue());
}
reader.moveUp();
}
DataTableRow newRow = new DataTableRow(rowValues);
if (result != null) {
newRow.setResult(result);
}
return newRow;
}
private TestTag readTag(HierarchicalStreamReader reader) {
return TestTag.withName(reader.getAttribute("name")).andType(reader.getAttribute("type"));
}
/*
* <test-step result="SUCCESS"> <description>The customer navigates to the
* metro masthead site.</description>
* <screenshot>the_customer_navigates_to_the_metro_masthead_site2
* .png</screenshot> </test-step>
*/
private void readTestStep(final HierarchicalStreamReader reader, final TestOutcome testOutcome) {
TestStep step = new TestStep();
String testResultValue = reader.getAttribute(RESULT_FIELD);
TestResult result = TestResult.valueOf(testResultValue);
step.setResult(result);
Long duration = readDuration(reader);
step.setDuration(duration);
readTestStepChildren(reader, step);
testOutcome.recordStep(step);
}
private long readDuration(HierarchicalStreamReader reader) {
String durationValue = reader.getAttribute(DURATION);
if (StringUtils.isNumeric(durationValue)) {
return Long.parseLong(reader.getAttribute(DURATION));
} else {
return 0;
}
}
private static final Optional<DateTime> NO_TIMESTAMP = Optional.absent();
private Optional<DateTime> readTimestamp(HierarchicalStreamReader reader) {
String timestamp = reader.getAttribute(TIMESTAMP);
return (timestamp != null) ? Optional.of(DateTime.parse(reader.getAttribute(TIMESTAMP))) : NO_TIMESTAMP;
}
private String readSessionId(HierarchicalStreamReader reader) {
return reader.getAttribute(SESSION_ID);
}
private void readTestGroup(final HierarchicalStreamReader reader, final TestOutcome testOutcome) {
String name = reader.getAttribute(NAME_FIELD);
String testResultValue = reader.getAttribute(RESULT_FIELD);
TestResult result = TestResult.valueOf(testResultValue);
testOutcome.recordStep(new TestStep(name));
testOutcome.startGroup();
testOutcome.currentGroup().setResult(result);
readChildren(reader, testOutcome);
testOutcome.endGroup();
}
private void readTestStepChildren(final HierarchicalStreamReader reader, final TestStep step) {
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(DESCRIPTION)) {
step.setDescription(reader.getValue());
} else if (childNode.equals(SCREENSHOT_LIST_FIELD)) {
readScreenshots(reader, step);
}
reader.moveUp();
}
}
private void readScreenshots(HierarchicalStreamReader reader, TestStep step) {
if (reader.getNodeName().equals(SCREENSHOT_LIST_FIELD)) {
while (reader.hasMoreChildren()) {
reader.moveDown();
String childNode = reader.getNodeName();
if (childNode.equals(SCREENSHOT_FIELD)) {
String screenshot = reader.getAttribute(SCREENSHOT_IMAGE);
String source = reader.getAttribute(SCREENSHOT_SOURCE);
if (source != null) {
step.addScreenshot(new ScreenshotAndHtmlSource(new File(screenshot), new File(source)));
} else {
step.addScreenshot(new ScreenshotAndHtmlSource(new File(screenshot)));
}
}
reader.moveUp();
}
}
}
}