diff --git a/README.md b/README.md
index d583c99f..96b51ab3 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ Requirements
* Glossary changes - got rid of the noise when extracting substeps tag info. Enabled the migration to new qualified custom glossary tags 'org.substeps.step.example' and 'org.substeps.step.section'
* some sonar suggested fixes
* If a -Denvironment= variable is set, pass through to the forked VM process. Can be overriden from the parent process using the vmArgs parameter in config if required.
+* Multiple execution configs cause issues with the report when running in forked mode, only the second set of results are visible. #74
1.1.2
-----
diff --git a/core/src/main/scala/org/substeps/report/ExecutionResultsCollector.scala b/core/src/main/scala/org/substeps/report/ExecutionResultsCollector.scala
index e59f8bd2..fcea5b53 100644
--- a/core/src/main/scala/org/substeps/report/ExecutionResultsCollector.scala
+++ b/core/src/main/scala/org/substeps/report/ExecutionResultsCollector.scala
@@ -461,14 +461,45 @@ class ExecutionResultsCollector extends IExecutionResultsCollector {
case class FeatureSummary(nodeType: String, filename: String, result : String, id : Long,
executionDurationMillis : Option[Long], description : String, scenarios: List[ScenarioSummary], tags : List[String])
+object FeatureSummary {
+ def sequenceIds(summary: FeatureSummary, toAdd: Long): FeatureSummary = {
+ summary.copy(id = summary.id + toAdd, scenarios = ScenarioSummary.sequenceIds(summary.scenarios, toAdd))
+ }
+}
+
case class ScenarioSummary(nodeId : Long, filename : String, result: String, tags : List[String])
+object ScenarioSummary {
+ def sequenceIds(scenarios: List[ScenarioSummary], toAdd : Long): scala.List[ScenarioSummary] = {
+
+ scenarios.map(s => {
+ s.copy(nodeId = s.nodeId + toAdd)
+ })
+ }
+}
+
case class FeatureSummaryForRootNode(nodeId : Long, resultsDir: String, result : String)
+object FeatureSummaryForRootNode {
+ def sequenceIds(features: List[FeatureSummaryForRootNode], toAdd: Long): scala.List[FeatureSummaryForRootNode] = {
+
+ features.map(f => {
+ f.copy(nodeId = f.nodeId + toAdd)
+ })
+ }
+}
+
case class RootNodeSummary(nodeType: String, description: String, result : String, id : Long,
executionDurationMillis : Option[Long], features : List[FeatureSummaryForRootNode], tags : Option[String], nonFatalTags : Option[String], timestamp : Long, environment : String)
+object RootNodeSummary {
+ def sequenceIds(rootNodeSummary: RootNodeSummary, toAdd: Long): RootNodeSummary = {
+
+ rootNodeSummary.copy(id = rootNodeSummary.id + toAdd, features = FeatureSummaryForRootNode.sequenceIds(rootNodeSummary.features, toAdd))
+ }
+}
+
case class SubstepsNode(id : Long, nodeType: String, description: String, children : List[SubstepsNode])
diff --git a/core/src/main/scala/org/substeps/report/NodeDetail.scala b/core/src/main/scala/org/substeps/report/NodeDetail.scala
index a56e0646..a0313060 100644
--- a/core/src/main/scala/org/substeps/report/NodeDetail.scala
+++ b/core/src/main/scala/org/substeps/report/NodeDetail.scala
@@ -25,6 +25,15 @@ case class NodeDetail(nodeType: String, filename: String, lineNumber : Int, resu
case object NodeDetail {
+
+ def sequenceIds(nodeDetails: List[NodeDetail], toAdd: Long): scala.List[NodeDetail] = {
+
+ nodeDetails.map(n => {
+ n.copy(id = n.id + toAdd, children = sequenceIds(n.children, toAdd))
+ })
+ }
+
+
def basicScenarioNodeInError(scenarioNode: BasicScenarioNode, children: List[NodeDetail], tags: Option[List[String]]) = {
val stackTrace = scenarioNode.getResult.getFailure.getCause.getStackTrace.toList.map(elem => elem.toString)
diff --git a/core/src/main/scala/org/substeps/report/ReportBuilder.scala b/core/src/main/scala/org/substeps/report/ReportBuilder.scala
index 87c64d9e..39289f62 100644
--- a/core/src/main/scala/org/substeps/report/ReportBuilder.scala
+++ b/core/src/main/scala/org/substeps/report/ReportBuilder.scala
@@ -87,10 +87,40 @@ object ReportBuilder {
case class FeatureDetails(summary : FeatureSummary, nodeDetails : List[NodeDetail])
+object FeatureDetails {
+ def sequenceIds(featuresList: List[FeatureDetails], toAdd: Long): scala.List[_root_.org.substeps.report.FeatureDetails] = {
+
+ featuresList.map(f => {
+ f.copy(summary = FeatureSummary.sequenceIds(f.summary, toAdd), nodeDetails = NodeDetail.sequenceIds(f.nodeDetails, toAdd))
+ })
+ }
+}
+
case class SourceDataModel(rootNodeSummary : RootNodeSummary, featuresList : List[FeatureDetails], config : Config)
case object SourceDataModel{
+ def sequenceIds(srcDataList: List[SourceDataModel]) : List[SourceDataModel] = {
+
+
+
+ srcDataList match {
+ case sdm :: Nil => srcDataList
+ case head :: tail => {
+
+ var toAdd = head.rootNodeSummary.id
+ // renumber the tail list
+ val newTail =
+ tail.map(sd => {
+ val copied = sd.copy(rootNodeSummary = RootNodeSummary.sequenceIds(sd.rootNodeSummary, toAdd) , featuresList = FeatureDetails.sequenceIds(sd.featuresList, toAdd))
+ toAdd = copied.rootNodeSummary.id
+ copied
+ })
+ head :: newTail
+ }
+ }
+ }
+
}
@@ -101,7 +131,7 @@ case class UncalledStepImpl(value:String, implementedIn: String, keyword: String
/**
* Created by ian on 30/06/16.
*/
-class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTreeTemplate with GlossaryTemplate {
+class ReportBuilder extends IReportBuilder with IndexPageTemplate with UsageTreeTemplate with GlossaryTemplate {
// TODO need to leave here as legacy to ensure maven is able to inject in parameter, mojo fails otherwise
@BeanProperty
@@ -223,7 +253,7 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr
processUncalledAndUnusedDataFiles( dataDir, executionConfigs)
- val srcDataList: List[SourceDataModel] = readModels(dataDir, executionConfigs)
+ val srcDataList: List[SourceDataModel] = SourceDataModel.sequenceIds(readModels(dataDir, executionConfigs))
val detailData = createFile( "detail_data.js")
@@ -680,7 +710,7 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr
f
}
- def writeResultSummary(resultsFile: RootNodeSummary)(implicit reportDir : File): Unit = {
+ def writeResultSummaryX(resultsFile: RootNodeSummary)(implicit reportDir : File): Unit = {
val file = createFile("results-summary.js")
@@ -734,7 +764,7 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr
resultsFileOption match {
case Some(resultsFile) => {
- writeResultSummary(resultsFile)
+ //writeResultSummary(resultsFile)
resultsFile.features.flatMap(featureSummary => {
@@ -910,13 +940,12 @@ class ReportBuilder extends IReportBuilder with ReportFrameTemplate with UsageTr
val featureSummaries = srcData.featuresList.map(f => f.summary) //srcData._2.map(_._1)
- srcData.featuresList.foreach(feature =>{
+ writeRootNode(writer, srcData.rootNodeSummary, featureSummaries, srcData.config)
+ srcData.featuresList.foreach(feature =>{
val nodeDetailList = feature.nodeDetails
- writeRootNode(writer, srcData.rootNodeSummary, featureSummaries, srcData.config)
-
val dataSubdir = NewSubstepsExecutionConfig.getDataSubdir(srcData.config)
writeFeatureNode(writer, feature.summary, nodeDetailList, dataSubdir)
diff --git a/core/src/main/scala/org/substeps/report/ReportFrame.scala b/core/src/main/scala/org/substeps/report/ReportFrame.scala
index bb815a62..d0420e05 100644
--- a/core/src/main/scala/org/substeps/report/ReportFrame.scala
+++ b/core/src/main/scala/org/substeps/report/ReportFrame.scala
@@ -1,8 +1,5 @@
package org.substeps.report
-import java.time.{Instant, LocalDateTime, ZoneId}
-import java.time.format.DateTimeFormatter
-
import com.typesafe.config.Config
/**
@@ -10,7 +7,7 @@ import com.typesafe.config.Config
*/
-trait ReportFrameTemplate {
+trait IndexPageTemplate {
def buildStatsBlock(name: String, counters : Counters) = {
@@ -87,9 +84,6 @@ trait ReportFrameTemplate {
|
|
|
- |
- |
- |
|
|
|
diff --git a/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/SyntaxAwareStepValidatorTest.java b/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/SyntaxAwareStepValidatorTest.java
index 1303c28a..c8cde006 100644
--- a/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/SyntaxAwareStepValidatorTest.java
+++ b/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/SyntaxAwareStepValidatorTest.java
@@ -26,49 +26,49 @@
import com.technophobia.substeps.runner.syntax.ClassAnalyser;
import com.technophobia.substeps.runner.syntax.SubStepDefinitionParser;
import com.technophobia.substeps.runner.syntax.SyntaxBuilder;
-import com.technophobia.substeps.runner.syntax.validation.fake.FakeSyntaxErrorReporter;
-import com.technophobia.substeps.runner.syntax.validation.fake.FakeSyntaxErrorReporter.SyntaxErrorData;
+import com.technophobia.substeps.runner.syntax.SyntaxErrorReporter;
import com.technophobia.substeps.stepimplementations.MockStepImplementations;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.util.Arrays;
-import java.util.List;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+@RunWith(MockitoJUnitRunner.class)
public class SyntaxAwareStepValidatorTest {
private static final String FEATURE_PATH = "./target/test-classes/features/";
private static final String SUBSTEPS_PATH = "./target/test-classes/substeps/";
- private FakeSyntaxErrorReporter syntaxErrorReporter;
private FeatureFileParser featureFileParser;
private SubStepDefinitionParser substepsFileParser;
-
+ private SyntaxErrorReporter mock;
@Before
public void initialise() {
- this.syntaxErrorReporter = new FakeSyntaxErrorReporter();
+ this.mock = Mockito.mock(SyntaxErrorReporter.class);
this.featureFileParser = new FeatureFileParser();
- this.substepsFileParser = new SubStepDefinitionParser(this.syntaxErrorReporter);
+ this.substepsFileParser = new SubStepDefinitionParser(mock);
}
@Test
public void validatorReportsMissingStepsInScenario() {
- final FeatureFile featureFile = this.featureFileParser.loadFeatureFile(createFeatureFile("error.feature"));
+ File ff = createFeatureFile("error.feature");
+ final FeatureFile featureFile = this.featureFileParser.loadFeatureFile(ff);
- createStepValidatorWithSubsteps("simple.substeps").validateFeatureFile(featureFile, syntaxErrorReporter);
- final List errors = syntaxErrorReporter.syntaxErrors();
- assertThat(Integer.valueOf(errors.size()), is(Integer.valueOf(2)));
+ createStepValidatorWithSubsteps("simple.substeps", mock).validateFeatureFile(featureFile, mock);
- checkError(errors.get(0), 6, "Given step 1");
- checkError(errors.get(1), 7, "Given step 2");
+ verify(mock).reportFeatureError(eq(ff), eq("Given step 1"), eq(6), anyInt(), any());
+ verify(mock).reportFeatureError(eq(ff), eq("Given step 2"), eq(7), anyInt(), any());
}
@@ -76,27 +76,31 @@ public void validatorReportsMissingStepsInScenario() {
public void validatorReportsNoErrorsForFeatureWithValidSteps() {
final FeatureFile featureFile = this.featureFileParser.loadFeatureFile(createFeatureFile("error.feature"));
- createStepValidatorWithSubsteps("error.substeps").validateFeatureFile(featureFile, syntaxErrorReporter);
- final List errors = syntaxErrorReporter.syntaxErrors();
- assertTrue(errors.isEmpty());
+ createStepValidatorWithSubsteps("error.substeps",mock).validateFeatureFile(featureFile, mock);
+
+ verify(mock, never()).reportFeatureError(any(),any(),anyInt(),anyInt(),any());
+
+ verify(mock, never()).reportSubstepsError(any());
+ verify(mock, never()).reportStepImplError(any());
}
@Test
public void validatorReportsMissingSubstepsInDefinition() {
- final PatternMap substeps = substepsFileParser.loadSubSteps(createSubstepsFile("error.substeps"));
- final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps");
+ File substepsFile = createSubstepsFile("error.substeps").getAbsoluteFile();
+
+ final PatternMap substeps = substepsFileParser.loadSubSteps(substepsFile);
+
+ final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps", mock);
for (final ParentStep substep : substeps.values()) {
- stepValidator.validateSubstep(substep, syntaxErrorReporter);
+ stepValidator.validateSubstep(substep, mock);
}
- final List errors = syntaxErrorReporter.syntaxErrors();
- assertThat(Integer.valueOf(errors.size()), is(Integer.valueOf(3)));
+ verify(mock).reportFeatureError(eq(substepsFile), eq("SingleWord"), eq(5), eq(101), eq("Step \"SingleWord\" is not defined"));
+ verify(mock).reportFeatureError(eq(substepsFile), eq("Test_Then something else has happened"), eq(6), anyInt(), any());
+ verify(mock).reportFeatureError(eq(substepsFile), eq("Test_Then something has happened"), eq(9), anyInt(), any());
- checkError(errors.get(0), 5, "SingleWord");
- checkError(errors.get(1), 6, "Test_Then something else has happened");
- checkError(errors.get(2), 9, "Test_Then something has happened");
}
@@ -105,38 +109,30 @@ public void validatorReportsNoErrorsForSubstepsWithValidSteps() {
final PatternMap substeps = this.substepsFileParser
.loadSubSteps(createSubstepsFile("allFeatures.substeps"));
- final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps",
+ final StepValidator stepValidator = createStepValidatorWithSubsteps("simple.substeps", mock,
MockStepImplementations.class);
for (final ParentStep substep : substeps.values()) {
- stepValidator.validateSubstep(substep, syntaxErrorReporter);
+ stepValidator.validateSubstep(substep, mock);
}
- final List errors = syntaxErrorReporter.syntaxErrors();
- assertTrue(errors.isEmpty());
- }
-
- private void checkError(final SyntaxErrorData error, final int lineNumber, final String line) {
- assertThat(Integer.valueOf(error.getLineNumber()), is(Integer.valueOf(lineNumber)));
- assertThat(error.getLine(), is(line));
- assertThat(error.getDescription(), is("Step \"" + line + "\" is not defined"));
+ verify(mock, never()).reportFeatureError(any(),any(),anyInt(),anyInt(),any());
+ verify(mock, never()).reportSubstepsError(any());
+ verify(mock, never()).reportStepImplError(any());
}
-
private File createFeatureFile(final String name) {
return new File(FEATURE_PATH, name);
}
-
private File createSubstepsFile(final String name) {
return new File(SUBSTEPS_PATH, name);
}
-
- private StepValidator createStepValidatorWithSubsteps(final String substepsFilename,
+ private StepValidator createStepValidatorWithSubsteps(final String substepsFilename, SyntaxErrorReporter errorReporter,
final Class>... stepImplClasses) {
final Syntax syntax = SyntaxBuilder.buildSyntax(Arrays.asList(stepImplClasses),
createSubstepsFile(substepsFilename), true, new String[0], new ClassAnalyser(), true,
- this.syntaxErrorReporter);
+ errorReporter);
return new SyntaxAwareStepValidator(syntax);
}
diff --git a/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/fake/FakeSyntaxErrorReporter.java b/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/fake/FakeSyntaxErrorReporter.java
deleted file mode 100644
index 30b2a545..00000000
--- a/core/src/test/java/com/technophobia/substeps/runner/syntax/validation/fake/FakeSyntaxErrorReporter.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright Technophobia Ltd 2012
- *
- * This file is part of Substeps.
- *
- * Substeps is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Substeps is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Substeps. If not, see .
- */
-package com.technophobia.substeps.runner.syntax.validation.fake;
-
-import com.technophobia.substeps.model.exception.StepImplementationException;
-import com.technophobia.substeps.model.exception.SubstepsParsingException;
-import com.technophobia.substeps.runner.syntax.SyntaxErrorReporter;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class FakeSyntaxErrorReporter implements SyntaxErrorReporter {
-
- private final List syntaxErrors;
- private final List stepErrors;
-
-
- public FakeSyntaxErrorReporter() {
- this.syntaxErrors = new ArrayList();
- this.stepErrors = new ArrayList();
- }
-
-
- public void reportFeatureError(final File file, final String line, final int lineNumber, final int offset,
- final String description) throws RuntimeException {
- syntaxErrors.add(new SyntaxErrorData(true, file, line, lineNumber, description));
- }
-
-
- public void reportSubstepsError(final SubstepsParsingException ex) {
- syntaxErrors.add(new SyntaxErrorData(false, ex.getFile(), ex.getLine(), ex.getLineNumber(), ex.getMessage()));
- }
-
-
- public void reportStepImplError(final StepImplementationException ex) {
- stepErrors.add(new StepImplErrorData(ex.getImplementingClass(), ex.getImplementingMethod().getName(), ex
- .getMessage()));
- }
-
-
- public List syntaxErrors() {
- Collections.sort(syntaxErrors);
- return syntaxErrors;
- }
-
-
- public List stepImplErrors() {
- Collections.sort(stepImplErrors());
- return stepErrors;
- }
-
- public static class SyntaxErrorData implements Comparable {
-
- private final boolean isFeature;
- private final File file;
- private final String line;
- private final int lineNumber;
- private final String description;
-
-
- public SyntaxErrorData(final boolean isFeature, final File file, final String line, final int lineNumber,
- final String description) {
- this.isFeature = isFeature;
- this.file = file;
- this.line = line;
- this.lineNumber = lineNumber;
- this.description = description;
- }
-
-
- public boolean isFeature() {
- return isFeature;
- }
-
-
- public File getFile() {
- return file;
- }
-
-
- public String getLine() {
- return line;
- }
-
-
- public int getLineNumber() {
- return lineNumber;
- }
-
-
- public String getDescription() {
- return description;
- }
-
-
- public int compareTo(final SyntaxErrorData other) {
- return lineNumber - other.lineNumber;
- }
- }
-
- private static class StepImplErrorData implements Comparable {
-
- private final Class> clazz;
- private final String methodName;
- private final String description;
-
-
- public StepImplErrorData(final Class> clazz, final String methodName, final String description) {
- this.clazz = clazz;
- this.methodName = methodName;
- this.description = description;
- }
-
-
- public Class> getClazz() {
- return clazz;
- }
-
-
- public String getMethodName() {
- return methodName;
- }
-
-
- public String getDescription() {
- return description;
- }
-
-
- public int compareTo(final StepImplErrorData other) {
- final int result = clazz.getName().compareTo(other.clazz.getName());
-
- if (result != 0) {
- return result;
- }
- return methodName.compareTo(other.methodName);
- }
- }
-
-}
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/results.json
new file mode 100644
index 00000000..031930af
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/results.json
@@ -0,0 +1 @@
+{"nodeType":"RootNode","description":"Chrome Self Test Features","result":"PASSED","id":262,"executionDurationMillis":262414,"features":[{"nodeId":105,"resultsDir":"self-test.feature.results","result":"PASSED"},{"nodeId":132,"resultsDir":"test-action-stepimpls.feature.results","result":"PASSED"},{"nodeId":144,"resultsDir":"test-assertions-stepimpls.feature.results","result":"PASSED"},{"nodeId":174,"resultsDir":"test-css-stepimpls.feature.results","result":"PASSED"},{"nodeId":204,"resultsDir":"test-form-step-impls.feature.results","result":"PASSED"},{"nodeId":247,"resultsDir":"test-finders-stepimpls.feature.results","result":"PASSED"},{"nodeId":261,"resultsDir":"test-window-stepimpls.feature.results","result":"PASSED"}],"tags":"@non-visual","timestamp":1510576934387,"environment":"itmoore"}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/self-test.feature.results/a_scenario_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/self-test.feature.results/a_scenario_0_results.json
new file mode 100644
index 00000000..19374215
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/self-test.feature.results/a_scenario_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"self-test.feature","lineNumber":0,"result":"PASSED","id":104,"executionDurationMillis":97505,"description":"a scenario","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":2,"executionDurationMillis":722,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":1,"executionDurationMillis":717,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":4,"result":"PASSED","id":4,"executionDurationMillis":401,"description":"Then I can see 'Hello Self Test page'","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":5,"result":"PASSED","id":3,"executionDurationMillis":401,"description":"AssertPageTitle is \"Hello Self Test page\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertPageTitle(java.lang.String)","children":[],"source":"AssertPageTitle is \"\"","sourceParameterNames":["page_title"]}],"source":"Then I can see ''","sourceParameterNames":["page_title"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":7,"result":"PASSED","id":6,"executionDurationMillis":925,"description":"And if I click the 'click me' button","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":8,"result":"PASSED","id":5,"executionDurationMillis":924,"description":"ClickSubmitButton \"click me\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickInput(java.lang.String)","children":[],"source":"ClickSubmitButton \"\"","sourceParameterNames":["btn_text"]}],"source":"And if I click the '' button","sourceParameterNames":["btn_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":10,"result":"PASSED","id":8,"executionDurationMillis":305,"description":"Then I can see \"Wahoo\" message","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":11,"result":"PASSED","id":7,"executionDurationMillis":305,"description":"FindById show-text-on-button-click-text-div-id and text = \"Wahoo\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById show-text-on-button-click-text-div-id and text = \"\"","sourceParameterNames":["msg"]}],"source":"Then I can see \"\" message","sourceParameterNames":["msg"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":16,"result":"PASSED","id":10,"executionDurationMillis":1533,"description":"And I dont see \"number one option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":17,"result":"PASSED","id":9,"executionDurationMillis":1531,"description":"AssertSelect id=\"select_id\" text=\"number one option\" is not currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsNotSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is not currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I dont see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":13,"result":"PASSED","id":12,"executionDurationMillis":1848,"description":"And I see \"number two option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":14,"result":"PASSED","id":11,"executionDurationMillis":1848,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":19,"result":"PASSED","id":14,"executionDurationMillis":5121,"description":"Then I choose \"number three option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":20,"result":"PASSED","id":13,"executionDurationMillis":5120,"description":"ChooseOption \"number three option\" in id select_id","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectValueInId(java.lang.String,java.lang.String)","children":[],"source":"ChooseOption \"\" in id ","sourceParameterNames":["option_value","select_id"]}],"source":"Then I choose \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":13,"result":"PASSED","id":16,"executionDurationMillis":2028,"description":"And I see \"number three option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":14,"result":"PASSED","id":15,"executionDurationMillis":2027,"description":"AssertSelect id=\"select_id\" text=\"number three option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":22,"result":"PASSED","id":18,"executionDurationMillis":1452,"description":"And if I click the Click by id button","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":23,"result":"PASSED","id":17,"executionDurationMillis":1451,"description":"ClickById click-id","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById click-id"}],"source":"And if I click the Click by id button"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":25,"result":"PASSED","id":20,"executionDurationMillis":409,"description":"Then I can see another \"Wahoo Two\" message","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":26,"result":"PASSED","id":19,"executionDurationMillis":409,"description":"FindById show-text-on-id-click-text-div-id and text = \"Wahoo Two\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById show-text-on-id-click-text-div-id and text = \"\"","sourceParameterNames":["msg"]}],"source":"Then I can see another \"\" message","sourceParameterNames":["msg"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":7,"result":"PASSED","id":22,"executionDurationMillis":1024,"description":"And if I click the 'delayed click' button","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":8,"result":"PASSED","id":21,"executionDurationMillis":1024,"description":"ClickSubmitButton \"delayed click\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickInput(java.lang.String)","children":[],"source":"ClickSubmitButton \"\"","sourceParameterNames":["btn_text"]}],"source":"And if I click the '' button","sourceParameterNames":["btn_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":28,"result":"PASSED","id":24,"executionDurationMillis":820,"description":"Then I see \"Delayed Text\" after a pause","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":29,"result":"PASSED","id":23,"executionDurationMillis":820,"description":"FindByIdTimeout show-text-on-deplayed-id-click-text-div-id timeout = 10 secs","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String,java.lang.String)","children":[],"source":"FindByIdTimeout show-text-on-deplayed-id-click-text-div-id timeout = 10 secs"}],"source":"Then I see \"\" after a pause","sourceParameterNames":["msg"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":31,"result":"PASSED","id":27,"executionDurationMillis":1639,"description":"Given I enter \"some text\" into the text field","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":32,"result":"PASSED","id":25,"executionDurationMillis":302,"description":"FindByTagAndAttributes tag=\"?input\"? attributes=\\[name=\"named_text_field\",maxlength=\"10\",size=\"10\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributes tag=\"input\" attributes=[name=\"named_text_field\",maxlength=\"10\",size=\"10\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":33,"result":"PASSED","id":26,"executionDurationMillis":1336,"description":"ClearAndSendKeys \"some text\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.clearAndSendKeys(java.lang.String)","children":[],"source":"ClearAndSendKeys \"\"","sourceParameterNames":["txt"]}],"source":"Given I enter \"\" into the text field","sourceParameterNames":["txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":35,"result":"PASSED","id":30,"executionDurationMillis":711,"description":"Then the text field will contain \"some text\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":36,"result":"PASSED","id":28,"executionDurationMillis":409,"description":"FindByName \"?named_text_field\"?","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByName(java.lang.String)","children":[],"source":"FindByName \"named_text_field\""},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":37,"result":"PASSED","id":29,"executionDurationMillis":302,"description":"AssertCurrentElement attribute=\"value\" value=\"some text\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertAttributeInCurrentElement(java.lang.String,java.lang.String)","children":[],"source":"AssertCurrentElement attribute=\"value\" value=\"\"","sourceParameterNames":["txt"]}],"source":"Then the text field will contain \"\"","sourceParameterNames":["txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":69,"result":"PASSED","id":34,"executionDurationMillis":1433,"description":"And the field located beneath the heading has the text 'some child text'","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":70,"result":"PASSED","id":31,"executionDurationMillis":823,"description":"FindById parent_div_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById parent_div_id"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":71,"result":"PASSED","id":32,"executionDurationMillis":302,"description":"FindChild ByTagAndAttributes tag=\"?span\"? attributes=\\[class=\"some_class\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindChild ByTagAndAttributes tag=\"span\" attributes=[class=\"some_class\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":72,"result":"PASSED","id":33,"executionDurationMillis":307,"description":"AssertCurrentElement text=\"some child text\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"\"","sourceParameterNames":["txt"]}],"source":"And the field located beneath the heading has the text ''","sourceParameterNames":["txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":74,"result":"PASSED","id":38,"executionDurationMillis":1630,"description":"And I can click the link \"a text link\" and see \"clicked a link\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":75,"result":"PASSED","id":35,"executionDurationMillis":824,"description":"ClickLink \"a text link\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickLink(java.lang.String)","children":[],"source":"ClickLink \"\"","sourceParameterNames":["link_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":76,"result":"PASSED","id":36,"executionDurationMillis":409,"description":"FindById show-text-on-link-click-text-div-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById show-text-on-link-click-text-div-id"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":77,"result":"PASSED","id":37,"executionDurationMillis":396,"description":"AssertCurrentElement text=\"clicked a link\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"\"","sourceParameterNames":["txt"]}],"source":"And I can click the link \"\" and see \"\"","sourceParameterNames":["link_text","txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":40,"result":"PASSED","id":41,"executionDurationMillis":5338,"description":"Given radio button with text \"radio - option 1\" is checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":41,"result":"PASSED","id":39,"executionDurationMillis":2252,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 1\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":42,"result":"PASSED","id":40,"executionDurationMillis":3086,"description":"AssertRadioButton checked=\"?true\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertRadioButtonIsChecked(java.lang.String)","children":[],"source":"AssertRadioButton checked=true"}],"source":"Given radio button with text \"\" is checked","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":47,"result":"PASSED","id":44,"executionDurationMillis":9515,"description":"Given radio button with text \"radio - option 2\" is not checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":48,"result":"PASSED","id":42,"executionDurationMillis":6248,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 2\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":49,"result":"PASSED","id":43,"executionDurationMillis":3267,"description":"AssertRadioButton checked=\"?false\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertRadioButtonIsChecked(java.lang.String)","children":[],"source":"AssertRadioButton checked=false"}],"source":"Given radio button with text \"\" is not checked","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":51,"result":"PASSED","id":47,"executionDurationMillis":4530,"description":"Then I can check radio button with text \"radio - option 3\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":52,"result":"PASSED","id":45,"executionDurationMillis":2222,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 3\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":53,"result":"PASSED","id":46,"executionDurationMillis":2307,"description":"SetRadioButton checked=true","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.setRadioButtonChecked(java.lang.String)","children":[],"source":"SetRadioButton checked=true"}],"source":"Then I can check radio button with text \"\"","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":44,"result":"PASSED","id":51,"executionDurationMillis":3876,"description":"Then radio button with text \"radio - option 3\" is checked","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":40,"result":"PASSED","id":50,"executionDurationMillis":3876,"description":"Given radio button with text \"\" is checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":41,"result":"PASSED","id":48,"executionDurationMillis":2316,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 3\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":42,"result":"PASSED","id":49,"executionDurationMillis":1558,"description":"AssertRadioButton checked=\"?true\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertRadioButtonIsChecked(java.lang.String)","children":[],"source":"AssertRadioButton checked=true"}],"source":"Given radio button with text \"\" is checked","sourceParameterNames":["rb_text"]}],"source":"Then radio button with text \"\" is checked","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":56,"result":"PASSED","id":54,"executionDurationMillis":3671,"description":"Given checkbox with text \"a checkbox label\" is not checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":57,"result":"PASSED","id":52,"executionDurationMillis":2101,"description":"FindCheckbox inside tag=\"?label\"? with label=\"a checkbox label\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findCheckBox(java.lang.String,java.lang.String)","children":[],"source":"FindCheckbox inside tag=\"label\" with label=\"\"","sourceParameterNames":["cb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":58,"result":"PASSED","id":53,"executionDurationMillis":1570,"description":"AssertCheckBox checked=\"?false\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCheckBoxIsChecked(java.lang.String)","children":[],"source":"AssertCheckBox checked=false"}],"source":"Given checkbox with text \"\" is not checked","sourceParameterNames":["cb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":60,"result":"PASSED","id":56,"executionDurationMillis":2370,"description":"Then I can check checkbox with text \"a checkbox label\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":61,"result":"PASSED","id":55,"executionDurationMillis":2368,"description":"SetCheckedBox checked=true","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.setSetCheckedBoxChecked(java.lang.String)","children":[],"source":"SetCheckedBox checked=true"}],"source":"Then I can check checkbox with text \"\"","sourceParameterNames":["cb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":63,"result":"PASSED","id":59,"executionDurationMillis":3687,"description":"And checkbox with text \"a checkbox label\" is checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":64,"result":"PASSED","id":57,"executionDurationMillis":2140,"description":"FindCheckbox inside tag=\"?label\"? with label=\"a checkbox label\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findCheckBox(java.lang.String,java.lang.String)","children":[],"source":"FindCheckbox inside tag=\"label\" with label=\"\"","sourceParameterNames":["cb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":65,"result":"PASSED","id":58,"executionDurationMillis":1547,"description":"AssertCheckBox checked=\"?true\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCheckBoxIsChecked(java.lang.String)","children":[],"source":"AssertCheckBox checked=true"}],"source":"And checkbox with text \"\" is checked","sourceParameterNames":["cb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":79,"result":"PASSED","id":62,"executionDurationMillis":2351,"description":"And the table row 1, column 2 contains \"Mrs Evil Headtecher\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":80,"result":"PASSED","id":60,"executionDurationMillis":408,"description":"FindById table_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table_id"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":81,"result":"PASSED","id":61,"executionDurationMillis":1942,"description":"AssertTableValue column 2, row 1 contains text \"Mrs Evil Headtecher\"","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.assertTableValue(java.lang.Integer,java.lang.Integer,java.lang.String)","children":[],"source":"AssertTableValue column , row contains text \"\"","sourceParameterNames":["col","row","txt"]}],"source":"And the table row , column contains \"\"","sourceParameterNames":["row","col","txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":90,"result":"PASSED","id":68,"executionDurationMillis":2666,"description":"And find by child works","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":91,"result":"PASSED","id":63,"executionDurationMillis":303,"description":"FindByTagAndAttributes tag=\"?div\"? attributes=\\[class=\"mini-profile\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributes tag=\"div\" attributes=[class=\"mini-profile\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":92,"result":"PASSED","id":64,"executionDurationMillis":305,"description":"FindChild ByTagAndAttributes tag=\"?a\"? attributes=\\[class=\"btn edit-button dialog-modal\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindChild ByTagAndAttributes tag=\"a\" attributes=[class=\"btn edit-button dialog-modal\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":93,"result":"PASSED","id":65,"executionDurationMillis":1135,"description":"Click","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.click()","children":[],"source":"Click"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":94,"result":"PASSED","id":66,"executionDurationMillis":512,"description":"FindById mcshane-bug-div-id and text = \"mcshane\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById mcshane-bug-div-id and text = \"mcshane\""},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":95,"result":"PASSED","id":67,"executionDurationMillis":410,"description":"FindById mcshane-negative-bug-div-id and text = \"mcshane negative test\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById mcshane-negative-bug-div-id and text = \"mcshane negative test\""}],"source":"And find by child works"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":125,"result":"PASSED","id":71,"executionDurationMillis":705,"description":"And I can find the disabled text field","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":126,"result":"PASSED","id":69,"executionDurationMillis":409,"description":"FindById postcode","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById postcode"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":127,"result":"PASSED","id":70,"executionDurationMillis":295,"description":"AssertCurrentElement attribute=\"disabled\" value=\"true\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertAttributeInCurrentElement(java.lang.String,java.lang.String)","children":[],"source":"AssertCurrentElement attribute=\"disabled\" value=\"true\""}],"source":"And I can find the disabled text field"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":16,"result":"PASSED","id":73,"executionDurationMillis":1635,"description":"And I dont see \"number two option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":17,"result":"PASSED","id":72,"executionDurationMillis":1633,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is not currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsNotSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is not currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I dont see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":129,"result":"PASSED","id":76,"executionDurationMillis":3394,"description":"And I select the second option by looking at the text it contains","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":130,"result":"PASSED","id":74,"executionDurationMillis":1050,"description":"FindFirstTagElementContainingText tag=\"option\" text=\"two\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstTagElementContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindFirstTagElementContainingText tag=\"option\" text=\"two\""},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":131,"result":"PASSED","id":75,"executionDurationMillis":2343,"description":"Click","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.click()","children":[],"source":"Click"}],"source":"And I select the second option by looking at the text it contains"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":13,"result":"PASSED","id":78,"executionDurationMillis":1601,"description":"And I see \"number two option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":14,"result":"PASSED","id":77,"executionDurationMillis":1601,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":133,"result":"PASSED","id":81,"executionDurationMillis":6284,"description":"Then I find a row using column contents","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":134,"result":"PASSED","id":79,"executionDurationMillis":329,"description":"FindById table-contents-tests","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table-contents-tests"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":135,"result":"PASSED","id":80,"executionDurationMillis":5952,"description":"FindTableRowWithColumnsThatContainText \\[\"My Name\", \"Where it all began...\", \"09:31\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findRowInTableWithText(java.lang.String)","children":[],"source":"FindTableRowWithColumnsThatContainText [\"My Name\", \"Where it all began...\", \"09:31\"]"}],"source":"Then I find a row using column contents"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":137,"result":"PASSED","id":86,"executionDurationMillis":5421,"description":"And I can find and click the link \"View\" in the row","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":138,"result":"PASSED","id":82,"executionDurationMillis":3584,"description":"FindElementInRow linkText=\"View\"","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findLinkInRow(java.lang.String)","children":[],"source":"FindElementInRow linkText=\"\"","sourceParameterNames":["linkText"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":139,"result":"PASSED","id":83,"executionDurationMillis":1126,"description":"Click","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.click()","children":[],"source":"Click"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":140,"result":"PASSED","id":84,"executionDurationMillis":411,"description":"FindById show-text-on-link-click-in-row1","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById show-text-on-link-click-in-row1"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":141,"result":"PASSED","id":85,"executionDurationMillis":299,"description":"FindById show-text-on-link-click-in-row2","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById show-text-on-link-click-in-row2"}],"source":"And I can find and click the link \"\" in the row","sourceParameterNames":["linkText"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":150,"result":"PASSED","id":89,"executionDurationMillis":1437,"description":"And I can find a table with caption \"There are 2 comments awaiting moderation\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":151,"result":"PASSED","id":87,"executionDurationMillis":1128,"description":"FindParentByTagAndAttributes tag=\"?table\"? attributes=\\[class=\"comments\"\\] ThatHasChild tag=\"?caption\"? text=\"There are 2 comments awaiting moderation\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findParentByTagAndAttributesThatHasChildWithTagAndText(java.lang.String,java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindParentByTagAndAttributes tag=\"table\" attributes=[class=\"comments\"] ThatHasChild tag=\"caption\" text=\"\"","sourceParameterNames":["caption"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":152,"result":"PASSED","id":88,"executionDurationMillis":308,"description":"AssertCurrentElement has attributes=\\[border=\"1\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentElementHasAttributes(java.lang.String)","children":[],"source":"AssertCurrentElement has attributes=[border=\"1\"]"}],"source":"And I can find a table with caption \"\"","sourceParameterNames":["caption"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":154,"result":"PASSED","id":92,"executionDurationMillis":1320,"description":"And I can find a table with an error caption","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":155,"result":"PASSED","id":90,"executionDurationMillis":1026,"description":"FindParentByTagAndAttributes tag=\"?table\"? attributes=\\[class=\"comments\"\\] ThatHasChild tag=\"?caption\"? attributes=\\[class=\"captionClassError\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findParentByTagAndAttributesThatHasChildWithTagAndAttributes(java.lang.String,java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindParentByTagAndAttributes tag=\"table\" attributes=[class=\"comments\"] ThatHasChild tag=\"caption\" attributes=[class=\"captionClassError\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":156,"result":"PASSED","id":91,"executionDurationMillis":294,"description":"AssertCurrentElement has attributes=\\[border=\"2\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentElementHasAttributes(java.lang.String)","children":[],"source":"AssertCurrentElement has attributes=[border=\"2\"]"}],"source":"And I can find a table with an error caption"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":176,"result":"PASSED","id":94,"executionDurationMillis":900,"description":"And I can find an h4 tag with a css regex","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":177,"result":"PASSED","id":93,"executionDurationMillis":899,"description":"FindByTag \"h4\" with cssClassRegex \".* markerClass-d .*\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByTagAndCssWildcard(java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"h4\" with cssClassRegex \".* markerClass-\\d .*\""}],"source":"And I can find an h4 tag with a css regex"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":179,"result":"PASSED","id":96,"executionDurationMillis":1568,"description":"And I can find an h4 tag with a css regex and text","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":180,"result":"PASSED","id":95,"executionDurationMillis":1568,"description":"FindByTag \"h4\" with cssClassRegex \".* markerClass-d .*\" and containing text \"Another heading\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByTagAndCssWildcard(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"h4\" with cssClassRegex \".* markerClass-\\d .*\" and containing text \"Another heading\""}],"source":"And I can find an h4 tag with a css regex and text"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":56,"result":"PASSED","id":97,"executionDurationMillis":317,"description":"FindById table_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table_id"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":57,"result":"PASSED","id":98,"executionDurationMillis":1330,"description":"FindTableBodyRow row 2","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findTableBodyRow(java.lang.Integer)","children":[],"source":"FindTableBodyRow row 2"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":58,"result":"PASSED","id":99,"executionDurationMillis":1338,"description":"AssertColumn 2 text = \"Mr A. Person\"","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.assertColumnText(java.lang.Integer,java.lang.String)","children":[],"source":"AssertColumn 2 text = \"Mr A. Person\""},{"nodeType":"Step","filename":"self-test.feature","lineNumber":61,"result":"PASSED","id":100,"executionDurationMillis":295,"description":"FindById table_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table_id"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":62,"result":"PASSED","id":101,"executionDurationMillis":1296,"description":"FindTableBodyRow row 3","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findTableBodyRow(java.lang.Integer)","children":[],"source":"FindTableBodyRow row 3"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":63,"result":"PASSED","id":102,"executionDurationMillis":2486,"description":"FindElementInRow ByTagAndAttributes tag=\"?span\"? attributes=\\[data-reactid=\"123456\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findLinkInRowByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindElementInRow ByTagAndAttributes tag=\"span\" attributes=[data-reactid=\"123456\"]"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":64,"result":"PASSED","id":103,"executionDurationMillis":310,"description":"AssertCurrentElement text=\"Mr Z. Person\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"Mr Z. Person\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/self-test.feature.results/self-test.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/self-test.feature.results/self-test.feature.results.json
new file mode 100644
index 00000000..2b86e80d
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/self-test.feature.results/self-test.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"self-test.feature","result":"PASSED","id":105,"executionDurationMillis":99633,"description":"A feature to self test the webdriver substeps implementations","scenarios":[{"nodeId":104,"filename":"a_scenario_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/Double_click_step_impls_1_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/Double_click_step_impls_1_results.json
new file mode 100644
index 00000000..5a7c4a80
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/Double_click_step_impls_1_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-action-stepimpls.feature","lineNumber":0,"result":"PASSED","id":131,"executionDurationMillis":6860,"description":"Double click step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":127,"executionDurationMillis":616,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":126,"executionDurationMillis":615,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":55,"result":"PASSED","id":128,"executionDurationMillis":408,"description":"FindById dbl-click-trigger","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById dbl-click-trigger"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":56,"result":"PASSED","id":129,"executionDurationMillis":849,"description":"PerformDoubleClick","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.doDoubleClick()","children":[],"source":"PerformDoubleClick"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":57,"result":"PASSED","id":130,"executionDurationMillis":380,"description":"FindById dbl-click-button-div and text = \"doubled clicked\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById dbl-click-button-div and text = \"doubled clicked\""}],"tags":["fails-in-firefox","@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/Test_Action_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/Test_Action_step_impls_0_results.json
new file mode 100644
index 00000000..59d7cf48
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/Test_Action_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-action-stepimpls.feature","lineNumber":0,"result":"PASSED","id":125,"executionDurationMillis":22221,"description":"Test Action step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":107,"executionDurationMillis":615,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":106,"executionDurationMillis":615,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":8,"result":"PASSED","id":108,"executionDurationMillis":1620,"description":"ClickButton containing \"partially\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButtonContainingText(java.lang.String)","children":[],"source":"ClickButton containing \"partially\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":9,"result":"PASSED","id":109,"executionDurationMillis":2476,"description":"FindById partial-text-button-div and text = \"Partial text matched Button click successful\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById partial-text-button-div and text = \"Partial text matched Button click successful\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":11,"result":"PASSED","id":110,"executionDurationMillis":2662,"description":"ClickById other-button-enabler","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById other-button-enabler"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":12,"result":"PASSED","id":111,"executionDurationMillis":284,"description":"FindById the-other-button","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById the-other-button"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":13,"result":"PASSED","id":112,"executionDurationMillis":1866,"description":"ClickWhenClickable","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickWhenClickable()","children":[],"source":"ClickWhenClickable"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":14,"result":"PASSED","id":113,"executionDurationMillis":296,"description":"FindById the-other-button-div and text = \"Other button clicked\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById the-other-button-div and text = \"Other button clicked\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":16,"result":"PASSED","id":114,"executionDurationMillis":1446,"description":"ClickById trigger-alert","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById trigger-alert"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":17,"result":"PASSED","id":115,"executionDurationMillis":201,"description":"WaitFor 200","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.waitFor(java.lang.String)","children":[],"source":"WaitFor 200"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":18,"result":"PASSED","id":116,"executionDurationMillis":1187,"description":"DismissAlert with message \"Popup\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.dismissAlertWithMessage(java.lang.String)","children":[],"source":"DismissAlert with message \"Popup\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":21,"result":"PASSED","id":117,"executionDurationMillis":1385,"description":"ClickById trigger-page-title-change","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById trigger-page-title-change"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":22,"result":"PASSED","id":118,"executionDurationMillis":1574,"description":"WaitForPageTitle \"A new page title\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.waitForPageTitle(java.lang.String)","children":[],"source":"WaitForPageTitle \"A new page title\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":25,"result":"PASSED","id":119,"executionDurationMillis":361,"description":"FindById id-for-js-manipulation and text = \"initial\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById id-for-js-manipulation and text = \"initial\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":26,"result":"PASSED","id":120,"executionDurationMillis":512,"description":"ExecuteJavascript document.getElementById(\"id-for-js-manipulation\").innerHTML = \"js fiddled\"$","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.executeJavaScript(java.lang.String)","children":[],"source":"ExecuteJavascript document.getElementById(\"id-for-js-manipulation\").innerHTML = \"js fiddled\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":27,"result":"PASSED","id":121,"executionDurationMillis":676,"description":"FindById id-for-js-manipulation and text = \"js fiddled\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById id-for-js-manipulation and text = \"js fiddled\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":31,"result":"PASSED","id":122,"executionDurationMillis":655,"description":"NavigateTo url property \"external.content\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateToProperty(java.lang.String)","children":[],"source":"NavigateTo url property \"external.content\""},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":84,"result":"PASSED","id":124,"executionDurationMillis":333,"description":"And the raw README is loaded","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":85,"result":"PASSED","id":123,"executionDurationMillis":333,"description":"AssertPageSourceContains \"Substeps.org Release Notes\"$","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.pageSourceContains(java.lang.String)","children":[],"source":"AssertPageSourceContains \"Substeps.org Release Notes\""}],"source":"And the raw README is loaded"}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/test-action-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/test-action-stepimpls.feature.results.json
new file mode 100644
index 00000000..aea556a6
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-action-stepimpls.feature.results/test-action-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-action-stepimpls.feature","result":"PASSED","id":132,"executionDurationMillis":31926,"description":"A feature to test Action Step implementations","scenarios":[{"nodeId":125,"filename":"Test_Action_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]},{"nodeId":131,"filename":"Double_click_step_impls_1_results.json","result":"PASSED","tags":["fails-in-firefox","@non-visual"]}],"tags":[]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-assertions-stepimpls.feature.results/Test_Assertion_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-assertions-stepimpls.feature.results/Test_Assertion_step_impls_0_results.json
new file mode 100644
index 00000000..c5c39270
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-assertions-stepimpls.feature.results/Test_Assertion_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-assertions-stepimpls.feature","lineNumber":0,"result":"PASSED","id":143,"executionDurationMillis":8187,"description":"Test Assertion step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":134,"executionDurationMillis":556,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":133,"executionDurationMillis":556,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":7,"result":"PASSED","id":135,"executionDurationMillis":366,"description":"FindById span-id-with-regex","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById span-id-with-regex"},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":8,"result":"PASSED","id":136,"executionDurationMillis":305,"description":"AssertCurrentElement text contains \"xyzabc\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElementContains(java.lang.String)","children":[],"source":"AssertCurrentElement text contains \"xyzabc\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":10,"result":"PASSED","id":137,"executionDurationMillis":281,"description":"FindById input-value-assertion-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById input-value-assertion-id"},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":11,"result":"PASSED","id":138,"executionDurationMillis":281,"description":"AssertCurrentInput value=\"123456\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"123456\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":13,"result":"PASSED","id":139,"executionDurationMillis":472,"description":"AssertNotPresent text=\"discombobulation\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertNotPresent(java.lang.String)","children":[],"source":"AssertNotPresent text=\"discombobulation\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":15,"result":"PASSED","id":140,"executionDurationMillis":578,"description":"RememberForScenario textFrom \"remember-div\" as \"context.name\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.rememberForScenario(java.lang.String,java.lang.String)","children":[],"source":"RememberForScenario textFrom \"remember-div\" as \"context.name\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":16,"result":"PASSED","id":141,"executionDurationMillis":610,"description":"AssertSame rememberedValue \"context.name\" compareToElement \"to-compare-to-matching-div\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertSame(java.lang.String,java.lang.String)","children":[],"source":"AssertSame rememberedValue \"context.name\" compareToElement \"to-compare-to-matching-div\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":17,"result":"PASSED","id":142,"executionDurationMillis":647,"description":"AssertDifferent rememberedValue \"context.name\" compareToElement \"to-compare-to-not-matching-div\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertDifferent(java.lang.String,java.lang.String)","children":[],"source":"AssertDifferent rememberedValue \"context.name\" compareToElement \"to-compare-to-not-matching-div\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-assertions-stepimpls.feature.results/test-assertions-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-assertions-stepimpls.feature.results/test-assertions-stepimpls.feature.results.json
new file mode 100644
index 00000000..14e8493c
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-assertions-stepimpls.feature.results/test-assertions-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-assertions-stepimpls.feature","result":"PASSED","id":144,"executionDurationMillis":9724,"description":"A feature to test Assertion based Step implementations","scenarios":[{"nodeId":143,"filename":"Test_Assertion_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-css-stepimpls.feature.results/Test_css_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-css-stepimpls.feature.results/Test_css_step_impls_0_results.json
new file mode 100644
index 00000000..b12c2206
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-css-stepimpls.feature.results/Test_css_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-css-stepimpls.feature","lineNumber":0,"result":"PASSED","id":173,"executionDurationMillis":46378,"description":"Test css step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":146,"executionDurationMillis":613,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":145,"executionDurationMillis":613,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":7,"result":"PASSED","id":147,"executionDurationMillis":489,"description":"FindByCssClass \"markerClass-1\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findWithCssClass(java.lang.String)","children":[],"source":"FindByCssClass \"markerClass-1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":8,"result":"PASSED","id":148,"executionDurationMillis":1558,"description":"FindByCssClass \"somethingElse\" with text \"Another heading\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassWithText(java.lang.String,java.lang.String)","children":[],"source":"FindByCssClass \"somethingElse\" with text \"Another heading\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":9,"result":"PASSED","id":149,"executionDurationMillis":1814,"description":"FindByCssClass \"markerClass-2\" containing text \"Another\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindByCssClass \"markerClass-2\" containing text \"Another\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":12,"result":"PASSED","id":150,"executionDurationMillis":336,"description":"FindByCssSelector \"#parent_div_id\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \"#parent_div_id\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":13,"result":"PASSED","id":151,"executionDurationMillis":292,"description":"FindByCssSelector \".markerClass-1\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \".markerClass-1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":15,"result":"PASSED","id":152,"executionDurationMillis":295,"description":"AssertCssSelector \".not-present-button\" is not present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertCssSelectorIsNotPresent(java.lang.String)","children":[],"source":"AssertCssSelector \".not-present-button\" is not present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":17,"result":"PASSED","id":153,"executionDurationMillis":334,"description":"ClickByCssClass \"not-present-button\" if present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.clickByCssClassNoFail(java.lang.String)","children":[],"source":"ClickByCssClass \"not-present-button\" if present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":19,"result":"PASSED","id":154,"executionDurationMillis":1126,"description":"ClickByCssClass \"present-button\" if present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.clickByCssClassNoFail(java.lang.String)","children":[],"source":"ClickByCssClass \"present-button\" if present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":21,"result":"PASSED","id":155,"executionDurationMillis":717,"description":"FindByCssSelector \".present-button\" with text \"clicked\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertCssSelectorHasCorrectText(java.lang.String,java.lang.String)","children":[],"source":"FindByCssSelector \".present-button\" with text \"clicked\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":23,"result":"PASSED","id":156,"executionDurationMillis":293,"description":"AssertCssSelector \".two-divs\" count is greater than \"1\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertNumberOfElementsIsGreaterThanExpectedSize(java.lang.String,int)","children":[],"source":"AssertCssSelector \".two-divs\" count is greater than \"1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":24,"result":"PASSED","id":157,"executionDurationMillis":297,"description":"AssertCssSelector \".two-divs\" count is \"2\" or less","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertNumberOfElementsIsLessThanOrEqualToExpectedSize(java.lang.String,int)","children":[],"source":"AssertCssSelector \".two-divs\" count is \"2\" or less"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":26,"result":"PASSED","id":158,"executionDurationMillis":287,"description":"AssertCssSelector \".two-divs\" is present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertCssSelectorIsPresent(java.lang.String)","children":[],"source":"AssertCssSelector \".two-divs\" is present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":27,"result":"PASSED","id":159,"executionDurationMillis":10363,"description":"FindByCssClass \"two-divs\" using timeout \"500\" containing \"1\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassContainingTimeoutAndText(java.lang.String,long,java.lang.String)","children":[],"source":"FindByCssClass \"two-divs\" using timeout \"500\" containing \"1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":28,"result":"PASSED","id":160,"executionDurationMillis":10776,"description":"FindByCssClass \"two-divs\" using timeout \"500\" with text \"2\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassWithTimeoutAndText(java.lang.String,long,java.lang.String)","children":[],"source":"FindByCssClass \"two-divs\" using timeout \"500\" with text \"2\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":30,"result":"PASSED","id":161,"executionDurationMillis":409,"description":"FindById visible-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById visible-div"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":31,"result":"PASSED","id":162,"executionDurationMillis":289,"description":"AssertCurrentElement is visible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsVisible()","children":[],"source":"AssertCurrentElement is visible"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":32,"result":"PASSED","id":163,"executionDurationMillis":1758,"description":"ClickButton \"?hide something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"hide something visible\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":33,"result":"PASSED","id":164,"executionDurationMillis":586,"description":"WaitFor CSS Selector \"#visible-div\" to be invisibile","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.waitForCssSelectorToHide(java.lang.String)","children":[],"source":"WaitFor CSS Selector \"#visible-div\" to be invisibile"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":34,"result":"PASSED","id":165,"executionDurationMillis":4739,"description":"ClickButton \"?make something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"make something visible\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":35,"result":"PASSED","id":166,"executionDurationMillis":607,"description":"WaitFor CSS Selector \"#visible-div\" to be visibile","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.waitForCssSelector(java.lang.String)","children":[],"source":"WaitFor CSS Selector \"#visible-div\" to be visibile"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":38,"result":"PASSED","id":167,"executionDurationMillis":586,"description":"Find ByChained CSS selectors \".parent-div-class\", \".nested-div-class\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByChainedCss(java.lang.String,java.lang.String)","children":[],"source":"Find ByChained CSS selectors \".parent-div-class\", \".nested-div-class\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":39,"result":"PASSED","id":168,"executionDurationMillis":314,"description":"AssertCurrentElement text=\"expected\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"expected\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":41,"result":"PASSED","id":169,"executionDurationMillis":319,"description":"FindByCssSelector \".parent-div-class\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \".parent-div-class\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":42,"result":"PASSED","id":170,"executionDurationMillis":1245,"description":"FindChildByTag \"div\" with cssClassRegex \"nested-div-class-d{4}\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findChildByTagAndCssWildcard(java.lang.String,java.lang.String)","children":[],"source":"FindChildByTag \"div\" with cssClassRegex \"nested-div-class-\\d{4}\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":43,"result":"PASSED","id":171,"executionDurationMillis":295,"description":"AssertCurrentElement text=\"expected2\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"expected2\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":45,"result":"PASSED","id":172,"executionDurationMillis":926,"description":"FindNthByTagAndText from parent by CSSSelector \".parent-div-class\", tag name \"span\", tag number \"2\" containing text \"two\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findNthByTagContainingText(java.lang.String,java.lang.String,int,java.lang.String)","children":[],"source":"FindNthByTagAndText from parent by CSSSelector \".parent-div-class\", tag name \"span\", tag number \"2\" containing text \"two\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-css-stepimpls.feature.results/test-css-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-css-stepimpls.feature.results/test-css-stepimpls.feature.results.json
new file mode 100644
index 00000000..a89969fa
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-css-stepimpls.feature.results/test-css-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-css-stepimpls.feature","result":"PASSED","id":174,"executionDurationMillis":48629,"description":"A feature to test CSS Step implementations","scenarios":[{"nodeId":173,"filename":"Test_css_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-finders-stepimpls.feature.results/Test_finder_step_implementations_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-finders-stepimpls.feature.results/Test_finder_step_implementations_0_results.json
new file mode 100644
index 00000000..f0d4d626
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-finders-stepimpls.feature.results/Test_finder_step_implementations_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-finders-stepimpls.feature","lineNumber":0,"result":"PASSED","id":246,"executionDurationMillis":22934,"description":"Test finder step implementations","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":206,"executionDurationMillis":615,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":205,"executionDurationMillis":614,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":8,"result":"PASSED","id":207,"executionDurationMillis":409,"description":"FindByTagAndAttributesWithText tag=\"?div\"? attributes=\\[data-reactid=\"12345\"\\] with text=\"div1 with tag and attributes\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributesWithText(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributesWithText tag=\"div\" attributes=[data-reactid=\"12345\"] with text=\"div1 with tag and attributes\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":9,"result":"PASSED","id":208,"executionDurationMillis":279,"description":"FindByTagAndAttributesWithText tag=\"?div\"? attributes=\\[data-reactid=\"12345\", aria-label=\"a-label\"\\] with text=\"div2 with tag and attributes\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributesWithText(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributesWithText tag=\"div\" attributes=[data-reactid=\"12345\", aria-label=\"a-label\"] with text=\"div2 with tag and attributes\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":11,"result":"PASSED","id":209,"executionDurationMillis":283,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":12,"result":"PASSED","id":210,"executionDurationMillis":359,"description":"FindChild ByName name=\"?child1\"?","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByName(java.lang.String)","children":[],"source":"FindChild ByName name=\"child1\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":13,"result":"PASSED","id":211,"executionDurationMillis":413,"description":"AssertCurrentElement text=\"child 1\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"child 1\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":15,"result":"PASSED","id":212,"executionDurationMillis":279,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":16,"result":"PASSED","id":213,"executionDurationMillis":285,"description":"FindChild ByTagAndAttributes tag=\"?div\"? attributes=\\[data-reactid=\"12345\"\\] with text=\"child div1 with tag and attributes\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByTagAndAttributesWithText(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindChild ByTagAndAttributes tag=\"div\" attributes=[data-reactid=\"12345\"] with text=\"child div1 with tag and attributes\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":18,"result":"PASSED","id":214,"executionDurationMillis":354,"description":"FindFirstByTagAndAttributes tag=\"?div\"? attributes=\\[data-reactid=\"54321\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindFirstByTagAndAttributes tag=\"div\" attributes=[data-reactid=\"54321\"]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":19,"result":"PASSED","id":215,"executionDurationMillis":299,"description":"AssertCurrentElement text=\"first\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"first\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":21,"result":"PASSED","id":216,"executionDurationMillis":268,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":22,"result":"PASSED","id":217,"executionDurationMillis":352,"description":"FindFirstChild ByTagAndAttributes tag=\"?div\"? attributes=\\[data-reactid=\"6789\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstChildByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindFirstChild ByTagAndAttributes tag=\"div\" attributes=[data-reactid=\"6789\"]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":23,"result":"PASSED","id":218,"executionDurationMillis":293,"description":"AssertCurrentElement text=\"first child\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"first child\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":25,"result":"PASSED","id":219,"executionDurationMillis":277,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":26,"result":"PASSED","id":220,"executionDurationMillis":351,"description":"FindFirstTagElementStartingWithText tag=\"div\" text=\"beginning with\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstTagElementStartingWithText(java.lang.String,java.lang.String)","children":[],"source":"FindFirstTagElementStartingWithText tag=\"div\" text=\"beginning with\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":27,"result":"PASSED","id":221,"executionDurationMillis":286,"description":"AssertCurrentElement text=\"beginning with first\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"beginning with first\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":30,"result":"PASSED","id":222,"executionDurationMillis":328,"description":"FindNthByTagAndAttributes n=\"?2\"? tag=\"?div\"? attributes=\\[data-reactid=\"54321\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findNthByTagAndAttributes(java.lang.Integer,java.lang.String,java.lang.String)","children":[],"source":"FindNthByTagAndAttributes n=\"2\" tag=\"div\" attributes=[data-reactid=\"54321\"]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":31,"result":"PASSED","id":223,"executionDurationMillis":283,"description":"AssertCurrentElement text=\"second\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"second\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":33,"result":"PASSED","id":224,"executionDurationMillis":276,"description":"FindByXpath //li[a/i[contains(@class, \"NOT_RUN\")]]$","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByXpath(java.lang.String)","children":[],"source":"FindByXpath //li[a/i[contains(@class, \"NOT_RUN\")]]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":34,"result":"PASSED","id":225,"executionDurationMillis":363,"description":"AssertCurrentElement text=\"target\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"target\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":36,"result":"PASSED","id":226,"executionDurationMillis":283,"description":"FindById ul-parent-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById ul-parent-id"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":37,"result":"PASSED","id":227,"executionDurationMillis":2146,"description":"FindFirstChildElementContainingText xpath=\"li//a\" text=\"decoy\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildElementContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindFirstChildElementContainingText xpath=\"li//a\" text=\"decoy\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":39,"result":"PASSED","id":228,"executionDurationMillis":284,"description":"FindByTag \"div\" with text \"some text\"","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByTagAndText(java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"div\" with text \"some text\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":40,"result":"PASSED","id":229,"executionDurationMillis":360,"description":"FindByTag \"span\" containing text \"xyzabc\"","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByTagContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"span\" containing text \"xyzabc\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":42,"result":"PASSED","id":230,"executionDurationMillis":611,"description":"FindById \"span-id-with-regex\" with text matching regex w* xyzabc.*$","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByIdWithRegex(java.lang.String,java.lang.String)","children":[],"source":"FindById \"span-id-with-regex\" with text matching regex \\w* xyzabc.*"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":43,"result":"PASSED","id":231,"executionDurationMillis":287,"description":"AssertCurrentElement text=\"blah xyzabc found\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"blah xyzabc found\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":46,"result":"PASSED","id":232,"executionDurationMillis":1,"description":"NotImplemented","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.notImplementedNotFailing()","children":[],"source":"NotImplemented"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":48,"result":"PASSED","id":233,"executionDurationMillis":289,"description":"FindById visible-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById visible-div"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":49,"result":"PASSED","id":234,"executionDurationMillis":275,"description":"AssertCurrentElement is visible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsVisible()","children":[],"source":"AssertCurrentElement is visible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":50,"result":"PASSED","id":235,"executionDurationMillis":1660,"description":"ClickButton \"?hide something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"hide something visible\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":51,"result":"PASSED","id":236,"executionDurationMillis":1016,"description":"WaitFor id \"visible-div\" to hide","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.waitForElementToHide(java.lang.String)","children":[],"source":"WaitFor id \"visible-div\" to hide"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":53,"result":"PASSED","id":237,"executionDurationMillis":2031,"description":"ClickButton \"?make something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"make something visible\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":54,"result":"PASSED","id":238,"executionDurationMillis":562,"description":"WaitFor id \"visible-div\" to be visible","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.waitForElementToBeVisible(java.lang.String)","children":[],"source":"WaitFor id \"visible-div\" to be visible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":56,"result":"PASSED","id":239,"executionDurationMillis":329,"description":"FindById not-visible-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById not-visible-div"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":57,"result":"PASSED","id":240,"executionDurationMillis":273,"description":"AssertCurrentElement is invisible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsInVisible()","children":[],"source":"AssertCurrentElement is invisible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":59,"result":"PASSED","id":241,"executionDurationMillis":342,"description":"FindById not-visible-hidden-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById not-visible-hidden-div"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":60,"result":"PASSED","id":242,"executionDurationMillis":297,"description":"AssertCurrentElement is invisible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsInVisible()","children":[],"source":"AssertCurrentElement is invisible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":62,"result":"PASSED","id":243,"executionDurationMillis":284,"description":"FindBy xpath with token replacement \\$x\\//li[a/i[contains \"FAILED\"]]\"\\)(.*)$","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByXpathWithTokenReplacement(java.lang.String,java.lang.String)","children":[],"source":"FindBy xpath with token replacement $x(\"//li[a/i[contains(@class, '%s')]]\") \"FAILED\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":63,"result":"PASSED","id":244,"executionDurationMillis":290,"description":"AssertCurrentElement text=\"target2\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"target2\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":65,"result":"PASSED","id":245,"executionDurationMillis":358,"description":"FindById id-for-partial-text-match containing text=\"jarlsberg\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.assertEventuallyContains(java.lang.String,java.lang.String)","children":[],"source":"FindById id-for-partial-text-match containing text=\"jarlsberg\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-finders-stepimpls.feature.results/test-finders-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-finders-stepimpls.feature.results/test-finders-stepimpls.feature.results.json
new file mode 100644
index 00000000..2e4c2867
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-finders-stepimpls.feature.results/test-finders-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-finders-stepimpls.feature","result":"PASSED","id":247,"executionDurationMillis":24450,"description":"A feature to test finder step implementations description","scenarios":[{"nodeId":246,"filename":"Test_finder_step_implementations_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-form-step-impls.feature.results/Test_Form_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-form-step-impls.feature.results/Test_Form_step_impls_0_results.json
new file mode 100644
index 00000000..40d9fff2
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-form-step-impls.feature.results/Test_Form_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-form-step-impls.feature","lineNumber":0,"result":"PASSED","id":203,"executionDurationMillis":29295,"description":"Test Form step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":176,"executionDurationMillis":617,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":175,"executionDurationMillis":616,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":7,"result":"PASSED","id":177,"executionDurationMillis":1738,"description":"ClearAndSendKeys \"a test\" to id input-value-assertion-id","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeysById(java.lang.String,java.lang.String)","children":[],"source":"ClearAndSendKeys \"a test\" to id input-value-assertion-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":9,"result":"PASSED","id":178,"executionDurationMillis":308,"description":"FindById input-value-assertion-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById input-value-assertion-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":10,"result":"PASSED","id":179,"executionDurationMillis":278,"description":"AssertCurrentInput value=\"a test\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"a test\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":12,"result":"PASSED","id":180,"executionDurationMillis":285,"description":"FindById select_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById select_id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":13,"result":"PASSED","id":181,"executionDurationMillis":2815,"description":"ChooseOption \"number three option\" in current element","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectValueInCurrentElement(java.lang.String)","children":[],"source":"ChooseOption \"number three option\" in current element"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":14,"result":"PASSED","id":182,"executionDurationMillis":1842,"description":"AssertSelect id=\"select_id\" text=\"number three option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"select_id\" text=\"number three option\" is currently selected"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":16,"result":"PASSED","id":183,"executionDurationMillis":2814,"description":"Select \"number one option\" option in Id \"select_id\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectOptionInId(java.lang.String,java.lang.String) throws java.lang.InterruptedException","children":[],"source":"Select \"number one option\" option in Id \"select_id\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":17,"result":"PASSED","id":184,"executionDurationMillis":1323,"description":"AssertSelect id=\"select_id\" text=\"number one option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"select_id\" text=\"number one option\" is currently selected"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":19,"result":"PASSED","id":185,"executionDurationMillis":2003,"description":"Select \"number two option\" option in class \"select-marker-class\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectOptionInClass(java.lang.String,java.lang.String)","children":[],"source":"Select \"number two option\" option in class \"select-marker-class\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":20,"result":"PASSED","id":186,"executionDurationMillis":1512,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":22,"result":"PASSED","id":187,"executionDurationMillis":288,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":23,"result":"PASSED","id":188,"executionDurationMillis":462,"description":"SendKeys \"abc123\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeys(java.lang.String)","children":[],"source":"SendKeys \"abc123\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":24,"result":"PASSED","id":189,"executionDurationMillis":282,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":25,"result":"PASSED","id":190,"executionDurationMillis":278,"description":"AssertCurrentInput value=\"abc123\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"abc123\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":27,"result":"PASSED","id":191,"executionDurationMillis":455,"description":"SendKey Key\\.BACK_SPACE","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKey(java.lang.String)","children":[],"source":"SendKey Key.BACK_SPACE"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":28,"result":"PASSED","id":192,"executionDurationMillis":275,"description":"AssertCurrentInput value=\"abc12\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"abc12\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":30,"result":"PASSED","id":193,"executionDurationMillis":274,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":31,"result":"PASSED","id":194,"executionDurationMillis":1293,"description":"ClearAndSendKeys \"\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.clearAndSendKeys(java.lang.String)","children":[],"source":"ClearAndSendKeys \"\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":33,"result":"PASSED","id":195,"executionDurationMillis":614,"description":"SendKeys pathOf property \"test.filename\" to current element","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeysToCurrentElement(java.lang.String)","children":[],"source":"SendKeys pathOf property \"test.filename\" to current element"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":34,"result":"PASSED","id":196,"executionDurationMillis":285,"description":"AssertCurrentInput value contains \"one.file\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentInputContainsText(java.lang.String)","children":[],"source":"AssertCurrentInput value contains \"one.file\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":36,"result":"PASSED","id":197,"executionDurationMillis":886,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":37,"result":"PASSED","id":198,"executionDurationMillis":1286,"description":"ClearAndSendKeys \"\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.clearAndSendKeys(java.lang.String)","children":[],"source":"ClearAndSendKeys \"\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":39,"result":"PASSED","id":199,"executionDurationMillis":1706,"description":"SendKeys pathOf property \"test.filename2\" to id \"text-id\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeysToId(java.lang.String,java.lang.String)","children":[],"source":"SendKeys pathOf property \"test.filename2\" to id \"text-id\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":40,"result":"PASSED","id":200,"executionDurationMillis":279,"description":"AssertCurrentInput value contains \"two.file\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentInputContainsText(java.lang.String)","children":[],"source":"AssertCurrentInput value contains \"two.file\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":42,"result":"PASSED","id":201,"executionDurationMillis":370,"description":"FindById input-value-to-submit-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById input-value-to-submit-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":43,"result":"PASSED","id":202,"executionDurationMillis":512,"description":"Submit","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.submit()","children":[],"source":"Submit"}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-form-step-impls.feature.results/test-form-step-impls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-form-step-impls.feature.results/test-form-step-impls.feature.results.json
new file mode 100644
index 00000000..a8e0e10a
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-form-step-impls.feature.results/test-form-step-impls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-form-step-impls.feature","result":"PASSED","id":204,"executionDurationMillis":30834,"description":"A feature to test Form Step implementations","scenarios":[{"nodeId":203,"filename":"Test_Form_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-window-stepimpls.feature.results/Test_iframe_and_window_step_implementations_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-window-stepimpls.feature.results/Test_iframe_and_window_step_implementations_0_results.json
new file mode 100644
index 00000000..8d1bc775
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-window-stepimpls.feature.results/Test_iframe_and_window_step_implementations_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-window-stepimpls.feature","lineNumber":0,"result":"PASSED","id":260,"executionDurationMillis":15379,"description":"Test iframe and window step implementations","children":[{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":7,"result":"PASSED","id":248,"executionDurationMillis":2601,"description":"NavigateTo url property \"iframe.test.page\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateToProperty(java.lang.String)","children":[],"source":"NavigateTo url property \"iframe.test.page\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":9,"result":"PASSED","id":249,"executionDurationMillis":2561,"description":"FindById outer-div-id and text = \"iframe page\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById outer-div-id and text = \"iframe page\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":11,"result":"PASSED","id":250,"executionDurationMillis":1761,"description":"Switch to new frame by name \"iframe-name\"","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToNewFrameByName(java.lang.String)","children":[],"source":"Switch to new frame by name \"iframe-name\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":12,"result":"PASSED","id":251,"executionDurationMillis":321,"description":"FindById iframed-div-id and text = \"iframed div\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById iframed-div-id and text = \"iframed div\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":14,"result":"PASSED","id":252,"executionDurationMillis":374,"description":"Switch to default content","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToDefaultContent()","children":[],"source":"Switch to default content"},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":15,"result":"PASSED","id":253,"executionDurationMillis":291,"description":"FindById outer-div-id and text = \"iframe page\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById outer-div-id and text = \"iframe page\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":17,"result":"PASSED","id":254,"executionDurationMillis":591,"description":"Switch to new frame by CSS selector \".iframe-class\"","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToNewFrame(java.lang.String)","children":[],"source":"Switch to new frame by CSS selector \".iframe-class\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":18,"result":"PASSED","id":255,"executionDurationMillis":308,"description":"FindById iframed-div-id and text = \"iframed div\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById iframed-div-id and text = \"iframed div\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":20,"result":"PASSED","id":256,"executionDurationMillis":284,"description":"Switch to default content","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToDefaultContent()","children":[],"source":"Switch to default content"},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":21,"result":"PASSED","id":257,"executionDurationMillis":369,"description":"FindByCssSelector \".iframe-class\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \".iframe-class\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":22,"result":"PASSED","id":258,"executionDurationMillis":284,"description":"SwitchFrameToCurrentElement","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchFrameToCurrentElement()","children":[],"source":"SwitchFrameToCurrentElement"},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":23,"result":"PASSED","id":259,"executionDurationMillis":331,"description":"FindById iframed-div-id and text = \"iframed div\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById iframed-div-id and text = \"iframed div\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/test-window-stepimpls.feature.results/test-window-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/1/test-window-stepimpls.feature.results/test-window-stepimpls.feature.results.json
new file mode 100644
index 00000000..0a2853a0
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/test-window-stepimpls.feature.results/test-window-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-window-stepimpls.feature","result":"PASSED","id":261,"executionDurationMillis":17120,"description":"A feature to test iframe and window related step impls","scenarios":[{"nodeId":260,"filename":"Test_iframe_and_window_step_implementations_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":[]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/uncalled.stepdefs.js b/core/src/test/resources/sample-results-data-dupe-ids/1/uncalled.stepdefs.js
new file mode 100644
index 00000000..7634800a
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/uncalled.stepdefs.js
@@ -0,0 +1 @@
+[{"line":"And I click the context menu","source":"self-test.substeps","lineNumber":103},{"line":"And I double click the link","source":"self-test.substeps","lineNumber":118},{"line":"Check to see if I wait until the div I\u0027ve just made visbile in 2 secs is clickable","source":"self-test.substeps","lineNumber":171},{"line":"Given I click the Dont click me button","source":"self-test.substeps","lineNumber":144},{"line":"Given that I\u0027ve not double clicked the link","source":"self-test.substeps","lineNumber":114},{"line":"Given the context menu hasn\u0027t been clicked","source":"self-test.substeps","lineNumber":98},{"line":"Then I can see I\u0027ve double clicked","source":"self-test.substeps","lineNumber":122},{"line":"Then I click a button which makes the div I\u0027m going to click visible in 2 seconds time","source":"self-test.substeps","lineNumber":167},{"line":"Then I see \"context has been clicked\"","source":"self-test.substeps","lineNumber":108},{"line":"Then I see an Alert \"\u003cmsg\u003e\"","source":"self-test.substeps","lineNumber":147},{"line":"Then the page title is \"\u003cpage_title\u003e\"","source":"self-test.substeps","lineNumber":87}]
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/1/uncalled.stepimpls.js b/core/src/test/resources/sample-results-data-dupe-ids/1/uncalled.stepimpls.js
new file mode 100644
index 00000000..9a335e3b
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/1/uncalled.stepimpls.js
@@ -0,0 +1 @@
+[{"value":"Close new window","implementedIn":"com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations","method":"closeWebDriver","keyword":"Close"},{"value":"PerformContextClick","implementedIn":"com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations","method":"performContextClick","keyword":"PerformContextClick"},{"value":"TakeScreenshot with prefix \"([^\"]*)\"","implementedIn":"com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations","method":"takeScreenshot","keyword":"TakeScreenshot"},{"value":"Switch to new window","implementedIn":"com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations","method":"switchToNewWindow","keyword":"Switch"},{"value":"AssertScreenshotFileExists \"([^\"]*)\" something","implementedIn":"org.substeps.webdriver.TestWebdriverStepImplementations","method":"assertScreenShotFileCreated","keyword":"AssertScreenshotFileExists"}]
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/results.json
new file mode 100644
index 00000000..89240ba1
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/results.json
@@ -0,0 +1 @@
+{"nodeType":"RootNode","description":"Firefox Self Test Features","result":"PASSED","id":262,"executionDurationMillis":291535,"features":[{"nodeId":105,"resultsDir":"self-test.feature.results","result":"PASSED"},{"nodeId":132,"resultsDir":"test-action-stepimpls.feature.results","result":"PASSED"},{"nodeId":144,"resultsDir":"test-assertions-stepimpls.feature.results","result":"PASSED"},{"nodeId":174,"resultsDir":"test-css-stepimpls.feature.results","result":"PASSED"},{"nodeId":204,"resultsDir":"test-form-step-impls.feature.results","result":"PASSED"},{"nodeId":247,"resultsDir":"test-finders-stepimpls.feature.results","result":"PASSED"},{"nodeId":261,"resultsDir":"test-window-stepimpls.feature.results","result":"PASSED"}],"tags":"@non-visual","nonFatalTags":"fails-in-firefox","timestamp":1510577198671,"environment":"itmoore"}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/self-test.feature.results/a_scenario_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/self-test.feature.results/a_scenario_0_results.json
new file mode 100644
index 00000000..51d90b11
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/self-test.feature.results/a_scenario_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"self-test.feature","lineNumber":0,"result":"PASSED","id":104,"executionDurationMillis":94968,"description":"a scenario","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":2,"executionDurationMillis":726,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":1,"executionDurationMillis":718,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":4,"result":"PASSED","id":4,"executionDurationMillis":301,"description":"Then I can see 'Hello Self Test page'","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":5,"result":"PASSED","id":3,"executionDurationMillis":300,"description":"AssertPageTitle is \"Hello Self Test page\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertPageTitle(java.lang.String)","children":[],"source":"AssertPageTitle is \"\"","sourceParameterNames":["page_title"]}],"source":"Then I can see ''","sourceParameterNames":["page_title"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":7,"result":"PASSED","id":6,"executionDurationMillis":917,"description":"And if I click the 'click me' button","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":8,"result":"PASSED","id":5,"executionDurationMillis":916,"description":"ClickSubmitButton \"click me\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickInput(java.lang.String)","children":[],"source":"ClickSubmitButton \"\"","sourceParameterNames":["btn_text"]}],"source":"And if I click the '' button","sourceParameterNames":["btn_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":10,"result":"PASSED","id":8,"executionDurationMillis":296,"description":"Then I can see \"Wahoo\" message","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":11,"result":"PASSED","id":7,"executionDurationMillis":296,"description":"FindById show-text-on-button-click-text-div-id and text = \"Wahoo\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById show-text-on-button-click-text-div-id and text = \"\"","sourceParameterNames":["msg"]}],"source":"Then I can see \"\" message","sourceParameterNames":["msg"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":16,"result":"PASSED","id":10,"executionDurationMillis":1175,"description":"And I dont see \"number one option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":17,"result":"PASSED","id":9,"executionDurationMillis":1175,"description":"AssertSelect id=\"select_id\" text=\"number one option\" is not currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsNotSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is not currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I dont see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":13,"result":"PASSED","id":12,"executionDurationMillis":1552,"description":"And I see \"number two option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":14,"result":"PASSED","id":11,"executionDurationMillis":1552,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":19,"result":"PASSED","id":14,"executionDurationMillis":5174,"description":"Then I choose \"number three option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":20,"result":"PASSED","id":13,"executionDurationMillis":5173,"description":"ChooseOption \"number three option\" in id select_id","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectValueInId(java.lang.String,java.lang.String)","children":[],"source":"ChooseOption \"\" in id ","sourceParameterNames":["option_value","select_id"]}],"source":"Then I choose \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":13,"result":"PASSED","id":16,"executionDurationMillis":1779,"description":"And I see \"number three option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":14,"result":"PASSED","id":15,"executionDurationMillis":1779,"description":"AssertSelect id=\"select_id\" text=\"number three option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":22,"result":"PASSED","id":18,"executionDurationMillis":1595,"description":"And if I click the Click by id button","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":23,"result":"PASSED","id":17,"executionDurationMillis":1594,"description":"ClickById click-id","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById click-id"}],"source":"And if I click the Click by id button"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":25,"result":"PASSED","id":20,"executionDurationMillis":284,"description":"Then I can see another \"Wahoo Two\" message","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":26,"result":"PASSED","id":19,"executionDurationMillis":284,"description":"FindById show-text-on-id-click-text-div-id and text = \"Wahoo Two\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById show-text-on-id-click-text-div-id and text = \"\"","sourceParameterNames":["msg"]}],"source":"Then I can see another \"\" message","sourceParameterNames":["msg"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":7,"result":"PASSED","id":22,"executionDurationMillis":844,"description":"And if I click the 'delayed click' button","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":8,"result":"PASSED","id":21,"executionDurationMillis":843,"description":"ClickSubmitButton \"delayed click\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickInput(java.lang.String)","children":[],"source":"ClickSubmitButton \"\"","sourceParameterNames":["btn_text"]}],"source":"And if I click the '' button","sourceParameterNames":["btn_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":28,"result":"PASSED","id":24,"executionDurationMillis":282,"description":"Then I see \"Delayed Text\" after a pause","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":29,"result":"PASSED","id":23,"executionDurationMillis":281,"description":"FindByIdTimeout show-text-on-deplayed-id-click-text-div-id timeout = 10 secs","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String,java.lang.String)","children":[],"source":"FindByIdTimeout show-text-on-deplayed-id-click-text-div-id timeout = 10 secs"}],"source":"Then I see \"\" after a pause","sourceParameterNames":["msg"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":31,"result":"PASSED","id":27,"executionDurationMillis":1558,"description":"Given I enter \"some text\" into the text field","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":32,"result":"PASSED","id":25,"executionDurationMillis":300,"description":"FindByTagAndAttributes tag=\"?input\"? attributes=\\[name=\"named_text_field\",maxlength=\"10\",size=\"10\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributes tag=\"input\" attributes=[name=\"named_text_field\",maxlength=\"10\",size=\"10\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":33,"result":"PASSED","id":26,"executionDurationMillis":1257,"description":"ClearAndSendKeys \"some text\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.clearAndSendKeys(java.lang.String)","children":[],"source":"ClearAndSendKeys \"\"","sourceParameterNames":["txt"]}],"source":"Given I enter \"\" into the text field","sourceParameterNames":["txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":35,"result":"PASSED","id":30,"executionDurationMillis":554,"description":"Then the text field will contain \"some text\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":36,"result":"PASSED","id":28,"executionDurationMillis":278,"description":"FindByName \"?named_text_field\"?","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByName(java.lang.String)","children":[],"source":"FindByName \"named_text_field\""},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":37,"result":"PASSED","id":29,"executionDurationMillis":275,"description":"AssertCurrentElement attribute=\"value\" value=\"some text\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertAttributeInCurrentElement(java.lang.String,java.lang.String)","children":[],"source":"AssertCurrentElement attribute=\"value\" value=\"\"","sourceParameterNames":["txt"]}],"source":"Then the text field will contain \"\"","sourceParameterNames":["txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":69,"result":"PASSED","id":34,"executionDurationMillis":938,"description":"And the field located beneath the heading has the text 'some child text'","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":70,"result":"PASSED","id":31,"executionDurationMillis":367,"description":"FindById parent_div_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById parent_div_id"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":71,"result":"PASSED","id":32,"executionDurationMillis":285,"description":"FindChild ByTagAndAttributes tag=\"?span\"? attributes=\\[class=\"some_class\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindChild ByTagAndAttributes tag=\"span\" attributes=[class=\"some_class\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":72,"result":"PASSED","id":33,"executionDurationMillis":284,"description":"AssertCurrentElement text=\"some child text\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"\"","sourceParameterNames":["txt"]}],"source":"And the field located beneath the heading has the text ''","sourceParameterNames":["txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":74,"result":"PASSED","id":38,"executionDurationMillis":5470,"description":"And I can click the link \"a text link\" and see \"clicked a link\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":75,"result":"PASSED","id":35,"executionDurationMillis":2397,"description":"ClickLink \"a text link\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickLink(java.lang.String)","children":[],"source":"ClickLink \"\"","sourceParameterNames":["link_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":76,"result":"PASSED","id":36,"executionDurationMillis":2561,"description":"FindById show-text-on-link-click-text-div-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById show-text-on-link-click-text-div-id"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":77,"result":"PASSED","id":37,"executionDurationMillis":512,"description":"AssertCurrentElement text=\"clicked a link\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"\"","sourceParameterNames":["txt"]}],"source":"And I can click the link \"\" and see \"\"","sourceParameterNames":["link_text","txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":40,"result":"PASSED","id":41,"executionDurationMillis":4710,"description":"Given radio button with text \"radio - option 1\" is checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":41,"result":"PASSED","id":39,"executionDurationMillis":3029,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 1\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":42,"result":"PASSED","id":40,"executionDurationMillis":1680,"description":"AssertRadioButton checked=\"?true\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertRadioButtonIsChecked(java.lang.String)","children":[],"source":"AssertRadioButton checked=true"}],"source":"Given radio button with text \"\" is checked","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":47,"result":"PASSED","id":44,"executionDurationMillis":4505,"description":"Given radio button with text \"radio - option 2\" is not checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":48,"result":"PASSED","id":42,"executionDurationMillis":2969,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 2\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":49,"result":"PASSED","id":43,"executionDurationMillis":1535,"description":"AssertRadioButton checked=\"?false\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertRadioButtonIsChecked(java.lang.String)","children":[],"source":"AssertRadioButton checked=false"}],"source":"Given radio button with text \"\" is not checked","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":51,"result":"PASSED","id":47,"executionDurationMillis":5132,"description":"Then I can check radio button with text \"radio - option 3\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":52,"result":"PASSED","id":45,"executionDurationMillis":2104,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 3\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":53,"result":"PASSED","id":46,"executionDurationMillis":3026,"description":"SetRadioButton checked=true","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.setRadioButtonChecked(java.lang.String)","children":[],"source":"SetRadioButton checked=true"}],"source":"Then I can check radio button with text \"\"","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":44,"result":"PASSED","id":51,"executionDurationMillis":3719,"description":"Then radio button with text \"radio - option 3\" is checked","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":40,"result":"PASSED","id":50,"executionDurationMillis":3719,"description":"Given radio button with text \"\" is checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":41,"result":"PASSED","id":48,"executionDurationMillis":2222,"description":"FindRadioButton inside tag=\"?label\"? with label=\"radio - option 3\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findRadioButton(java.lang.String,java.lang.String)","children":[],"source":"FindRadioButton inside tag=\"label\" with label=\"\"","sourceParameterNames":["rb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":42,"result":"PASSED","id":49,"executionDurationMillis":1497,"description":"AssertRadioButton checked=\"?true\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertRadioButtonIsChecked(java.lang.String)","children":[],"source":"AssertRadioButton checked=true"}],"source":"Given radio button with text \"\" is checked","sourceParameterNames":["rb_text"]}],"source":"Then radio button with text \"\" is checked","sourceParameterNames":["rb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":56,"result":"PASSED","id":54,"executionDurationMillis":3692,"description":"Given checkbox with text \"a checkbox label\" is not checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":57,"result":"PASSED","id":52,"executionDurationMillis":2207,"description":"FindCheckbox inside tag=\"?label\"? with label=\"a checkbox label\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findCheckBox(java.lang.String,java.lang.String)","children":[],"source":"FindCheckbox inside tag=\"label\" with label=\"\"","sourceParameterNames":["cb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":58,"result":"PASSED","id":53,"executionDurationMillis":1485,"description":"AssertCheckBox checked=\"?false\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCheckBoxIsChecked(java.lang.String)","children":[],"source":"AssertCheckBox checked=false"}],"source":"Given checkbox with text \"\" is not checked","sourceParameterNames":["cb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":60,"result":"PASSED","id":56,"executionDurationMillis":2406,"description":"Then I can check checkbox with text \"a checkbox label\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":61,"result":"PASSED","id":55,"executionDurationMillis":2406,"description":"SetCheckedBox checked=true","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.setSetCheckedBoxChecked(java.lang.String)","children":[],"source":"SetCheckedBox checked=true"}],"source":"Then I can check checkbox with text \"\"","sourceParameterNames":["cb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":63,"result":"PASSED","id":59,"executionDurationMillis":3643,"description":"And checkbox with text \"a checkbox label\" is checked","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":64,"result":"PASSED","id":57,"executionDurationMillis":2149,"description":"FindCheckbox inside tag=\"?label\"? with label=\"a checkbox label\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findCheckBox(java.lang.String,java.lang.String)","children":[],"source":"FindCheckbox inside tag=\"label\" with label=\"\"","sourceParameterNames":["cb_text"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":65,"result":"PASSED","id":58,"executionDurationMillis":1493,"description":"AssertCheckBox checked=\"?true\"?","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCheckBoxIsChecked(java.lang.String)","children":[],"source":"AssertCheckBox checked=true"}],"source":"And checkbox with text \"\" is checked","sourceParameterNames":["cb_text"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":79,"result":"PASSED","id":62,"executionDurationMillis":2187,"description":"And the table row 1, column 2 contains \"Mrs Evil Headtecher\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":80,"result":"PASSED","id":60,"executionDurationMillis":281,"description":"FindById table_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table_id"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":81,"result":"PASSED","id":61,"executionDurationMillis":1904,"description":"AssertTableValue column 2, row 1 contains text \"Mrs Evil Headtecher\"","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.assertTableValue(java.lang.Integer,java.lang.Integer,java.lang.String)","children":[],"source":"AssertTableValue column , row contains text \"\"","sourceParameterNames":["col","row","txt"]}],"source":"And the table row , column contains \"\"","sourceParameterNames":["row","col","txt"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":90,"result":"PASSED","id":68,"executionDurationMillis":2464,"description":"And find by child works","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":91,"result":"PASSED","id":63,"executionDurationMillis":284,"description":"FindByTagAndAttributes tag=\"?div\"? attributes=\\[class=\"mini-profile\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributes tag=\"div\" attributes=[class=\"mini-profile\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":92,"result":"PASSED","id":64,"executionDurationMillis":285,"description":"FindChild ByTagAndAttributes tag=\"?a\"? attributes=\\[class=\"btn edit-button dialog-modal\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindChild ByTagAndAttributes tag=\"a\" attributes=[class=\"btn edit-button dialog-modal\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":93,"result":"PASSED","id":65,"executionDurationMillis":1175,"description":"Click","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.click()","children":[],"source":"Click"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":94,"result":"PASSED","id":66,"executionDurationMillis":410,"description":"FindById mcshane-bug-div-id and text = \"mcshane\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById mcshane-bug-div-id and text = \"mcshane\""},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":95,"result":"PASSED","id":67,"executionDurationMillis":306,"description":"FindById mcshane-negative-bug-div-id and text = \"mcshane negative test\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById mcshane-negative-bug-div-id and text = \"mcshane negative test\""}],"source":"And find by child works"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":125,"result":"PASSED","id":71,"executionDurationMillis":551,"description":"And I can find the disabled text field","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":126,"result":"PASSED","id":69,"executionDurationMillis":280,"description":"FindById postcode","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById postcode"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":127,"result":"PASSED","id":70,"executionDurationMillis":270,"description":"AssertCurrentElement attribute=\"disabled\" value=\"true\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertAttributeInCurrentElement(java.lang.String,java.lang.String)","children":[],"source":"AssertCurrentElement attribute=\"disabled\" value=\"true\""}],"source":"And I can find the disabled text field"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":16,"result":"PASSED","id":73,"executionDurationMillis":1598,"description":"And I dont see \"number two option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":17,"result":"PASSED","id":72,"executionDurationMillis":1598,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is not currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsNotSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is not currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I dont see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":129,"result":"PASSED","id":76,"executionDurationMillis":1450,"description":"And I select the second option by looking at the text it contains","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":130,"result":"PASSED","id":74,"executionDurationMillis":287,"description":"FindFirstTagElementContainingText tag=\"option\" text=\"two\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstTagElementContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindFirstTagElementContainingText tag=\"option\" text=\"two\""},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":131,"result":"PASSED","id":75,"executionDurationMillis":1161,"description":"Click","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.click()","children":[],"source":"Click"}],"source":"And I select the second option by looking at the text it contains"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":13,"result":"PASSED","id":78,"executionDurationMillis":5396,"description":"And I see \"number two option\" in select id select_id","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":14,"result":"PASSED","id":77,"executionDurationMillis":5396,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"\" text=\"\" is currently selected","sourceParameterNames":["select_id","option_value"]}],"source":"And I see \"\" in select id ","sourceParameterNames":["option_value","select_id"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":133,"result":"PASSED","id":81,"executionDurationMillis":5429,"description":"Then I find a row using column contents","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":134,"result":"PASSED","id":79,"executionDurationMillis":281,"description":"FindById table-contents-tests","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table-contents-tests"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":135,"result":"PASSED","id":80,"executionDurationMillis":5148,"description":"FindTableRowWithColumnsThatContainText \\[\"My Name\", \"Where it all began...\", \"09:31\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findRowInTableWithText(java.lang.String)","children":[],"source":"FindTableRowWithColumnsThatContainText [\"My Name\", \"Where it all began...\", \"09:31\"]"}],"source":"Then I find a row using column contents"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":137,"result":"PASSED","id":86,"executionDurationMillis":4763,"description":"And I can find and click the link \"View\" in the row","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":138,"result":"PASSED","id":82,"executionDurationMillis":3065,"description":"FindElementInRow linkText=\"View\"","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findLinkInRow(java.lang.String)","children":[],"source":"FindElementInRow linkText=\"\"","sourceParameterNames":["linkText"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":139,"result":"PASSED","id":83,"executionDurationMillis":1146,"description":"Click","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.click()","children":[],"source":"Click"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":140,"result":"PASSED","id":84,"executionDurationMillis":273,"description":"FindById show-text-on-link-click-in-row1","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById show-text-on-link-click-in-row1"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":141,"result":"PASSED","id":85,"executionDurationMillis":279,"description":"FindById show-text-on-link-click-in-row2","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById show-text-on-link-click-in-row2"}],"source":"And I can find and click the link \"\" in the row","sourceParameterNames":["linkText"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":150,"result":"PASSED","id":89,"executionDurationMillis":1297,"description":"And I can find a table with caption \"There are 2 comments awaiting moderation\"","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":151,"result":"PASSED","id":87,"executionDurationMillis":935,"description":"FindParentByTagAndAttributes tag=\"?table\"? attributes=\\[class=\"comments\"\\] ThatHasChild tag=\"?caption\"? text=\"There are 2 comments awaiting moderation\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findParentByTagAndAttributesThatHasChildWithTagAndText(java.lang.String,java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindParentByTagAndAttributes tag=\"table\" attributes=[class=\"comments\"] ThatHasChild tag=\"caption\" text=\"\"","sourceParameterNames":["caption"]},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":152,"result":"PASSED","id":88,"executionDurationMillis":360,"description":"AssertCurrentElement has attributes=\\[border=\"1\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentElementHasAttributes(java.lang.String)","children":[],"source":"AssertCurrentElement has attributes=[border=\"1\"]"}],"source":"And I can find a table with caption \"\"","sourceParameterNames":["caption"]},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":154,"result":"PASSED","id":92,"executionDurationMillis":1223,"description":"And I can find a table with an error caption","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":155,"result":"PASSED","id":90,"executionDurationMillis":862,"description":"FindParentByTagAndAttributes tag=\"?table\"? attributes=\\[class=\"comments\"\\] ThatHasChild tag=\"?caption\"? attributes=\\[class=\"captionClassError\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findParentByTagAndAttributesThatHasChildWithTagAndAttributes(java.lang.String,java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindParentByTagAndAttributes tag=\"table\" attributes=[class=\"comments\"] ThatHasChild tag=\"caption\" attributes=[class=\"captionClassError\"]"},{"nodeType":"Step","filename":"self-test.substeps","lineNumber":156,"result":"PASSED","id":91,"executionDurationMillis":360,"description":"AssertCurrentElement has attributes=\\[border=\"2\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentElementHasAttributes(java.lang.String)","children":[],"source":"AssertCurrentElement has attributes=[border=\"2\"]"}],"source":"And I can find a table with an error caption"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":176,"result":"PASSED","id":94,"executionDurationMillis":921,"description":"And I can find an h4 tag with a css regex","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":177,"result":"PASSED","id":93,"executionDurationMillis":921,"description":"FindByTag \"h4\" with cssClassRegex \".* markerClass-d .*\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByTagAndCssWildcard(java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"h4\" with cssClassRegex \".* markerClass-\\d .*\""}],"source":"And I can find an h4 tag with a css regex"},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":179,"result":"PASSED","id":96,"executionDurationMillis":1489,"description":"And I can find an h4 tag with a css regex and text","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":180,"result":"PASSED","id":95,"executionDurationMillis":1489,"description":"FindByTag \"h4\" with cssClassRegex \".* markerClass-d .*\" and containing text \"Another heading\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByTagAndCssWildcard(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"h4\" with cssClassRegex \".* markerClass-\\d .*\" and containing text \"Another heading\""}],"source":"And I can find an h4 tag with a css regex and text"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":56,"result":"PASSED","id":97,"executionDurationMillis":354,"description":"FindById table_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table_id"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":57,"result":"PASSED","id":98,"executionDurationMillis":1200,"description":"FindTableBodyRow row 2","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findTableBodyRow(java.lang.Integer)","children":[],"source":"FindTableBodyRow row 2"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":58,"result":"PASSED","id":99,"executionDurationMillis":1209,"description":"AssertColumn 2 text = \"Mr A. Person\"","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.assertColumnText(java.lang.Integer,java.lang.String)","children":[],"source":"AssertColumn 2 text = \"Mr A. Person\""},{"nodeType":"Step","filename":"self-test.feature","lineNumber":61,"result":"PASSED","id":100,"executionDurationMillis":354,"description":"FindById table_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById table_id"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":62,"result":"PASSED","id":101,"executionDurationMillis":1206,"description":"FindTableBodyRow row 3","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findTableBodyRow(java.lang.Integer)","children":[],"source":"FindTableBodyRow row 3"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":63,"result":"PASSED","id":102,"executionDurationMillis":2154,"description":"FindElementInRow ByTagAndAttributes tag=\"?span\"? attributes=\\[data-reactid=\"123456\"\\]","method":"public void com.technophobia.webdriver.substeps.impl.TableSubStepImplementations.findLinkInRowByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindElementInRow ByTagAndAttributes tag=\"span\" attributes=[data-reactid=\"123456\"]"},{"nodeType":"Step","filename":"self-test.feature","lineNumber":64,"result":"PASSED","id":103,"executionDurationMillis":295,"description":"AssertCurrentElement text=\"Mr Z. Person\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"Mr Z. Person\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/self-test.feature.results/self-test.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/self-test.feature.results/self-test.feature.results.json
new file mode 100644
index 00000000..7214c746
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/self-test.feature.results/self-test.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"self-test.feature","result":"PASSED","id":105,"executionDurationMillis":97152,"description":"A feature to self test the webdriver substeps implementations","scenarios":[{"nodeId":104,"filename":"a_scenario_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/Double_click_step_impls_1_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/Double_click_step_impls_1_results.json
new file mode 100644
index 00000000..e81b17d3
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/Double_click_step_impls_1_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-action-stepimpls.feature","lineNumber":0,"result":"PASSED","id":131,"executionDurationMillis":9223,"description":"Double click step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":127,"executionDurationMillis":724,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":126,"executionDurationMillis":724,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":55,"result":"PASSED","id":128,"executionDurationMillis":353,"description":"FindById dbl-click-trigger","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById dbl-click-trigger"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":56,"result":"PASSED","id":129,"executionDurationMillis":990,"description":"PerformDoubleClick","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.doDoubleClick()","children":[],"source":"PerformDoubleClick"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":57,"result":"PASSED","id":130,"executionDurationMillis":303,"description":"FindById dbl-click-button-div and text = \"doubled clicked\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById dbl-click-button-div and text = \"doubled clicked\""}],"tags":["fails-in-firefox","@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/Test_Action_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/Test_Action_step_impls_0_results.json
new file mode 100644
index 00000000..7dbfc580
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/Test_Action_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-action-stepimpls.feature","lineNumber":0,"result":"PASSED","id":125,"executionDurationMillis":38038,"description":"Test Action step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":107,"executionDurationMillis":843,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":106,"executionDurationMillis":842,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":8,"result":"PASSED","id":108,"executionDurationMillis":1841,"description":"ClickButton containing \"partially\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButtonContainingText(java.lang.String)","children":[],"source":"ClickButton containing \"partially\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":9,"result":"PASSED","id":109,"executionDurationMillis":281,"description":"FindById partial-text-button-div and text = \"Partial text matched Button click successful\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById partial-text-button-div and text = \"Partial text matched Button click successful\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":11,"result":"PASSED","id":110,"executionDurationMillis":1421,"description":"ClickById other-button-enabler","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById other-button-enabler"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":12,"result":"PASSED","id":111,"executionDurationMillis":297,"description":"FindById the-other-button","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById the-other-button"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":13,"result":"PASSED","id":112,"executionDurationMillis":1994,"description":"ClickWhenClickable","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickWhenClickable()","children":[],"source":"ClickWhenClickable"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":14,"result":"PASSED","id":113,"executionDurationMillis":282,"description":"FindById the-other-button-div and text = \"Other button clicked\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById the-other-button-div and text = \"Other button clicked\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":16,"result":"PASSED","id":114,"executionDurationMillis":17125,"description":"ClickById trigger-alert","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById trigger-alert"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":17,"result":"PASSED","id":115,"executionDurationMillis":200,"description":"WaitFor 200","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.waitFor(java.lang.String)","children":[],"source":"WaitFor 200"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":18,"result":"PASSED","id":116,"executionDurationMillis":1184,"description":"DismissAlert with message \"Popup\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.dismissAlertWithMessage(java.lang.String)","children":[],"source":"DismissAlert with message \"Popup\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":21,"result":"PASSED","id":117,"executionDurationMillis":1381,"description":"ClickById trigger-page-title-change","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickById(java.lang.String)","children":[],"source":"ClickById trigger-page-title-change"},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":22,"result":"PASSED","id":118,"executionDurationMillis":1571,"description":"WaitForPageTitle \"A new page title\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.waitForPageTitle(java.lang.String)","children":[],"source":"WaitForPageTitle \"A new page title\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":25,"result":"PASSED","id":119,"executionDurationMillis":375,"description":"FindById id-for-js-manipulation and text = \"initial\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById id-for-js-manipulation and text = \"initial\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":26,"result":"PASSED","id":120,"executionDurationMillis":410,"description":"ExecuteJavascript document.getElementById(\"id-for-js-manipulation\").innerHTML = \"js fiddled\"$","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.executeJavaScript(java.lang.String)","children":[],"source":"ExecuteJavascript document.getElementById(\"id-for-js-manipulation\").innerHTML = \"js fiddled\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":27,"result":"PASSED","id":121,"executionDurationMillis":291,"description":"FindById id-for-js-manipulation and text = \"js fiddled\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById id-for-js-manipulation and text = \"js fiddled\""},{"nodeType":"Step","filename":"test-action-stepimpls.feature","lineNumber":31,"result":"PASSED","id":122,"executionDurationMillis":630,"description":"NavigateTo url property \"external.content\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateToProperty(java.lang.String)","children":[],"source":"NavigateTo url property \"external.content\""},{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":84,"result":"PASSED","id":124,"executionDurationMillis":289,"description":"And the raw README is loaded","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":85,"result":"PASSED","id":123,"executionDurationMillis":289,"description":"AssertPageSourceContains \"Substeps.org Release Notes\"$","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.pageSourceContains(java.lang.String)","children":[],"source":"AssertPageSourceContains \"Substeps.org Release Notes\""}],"source":"And the raw README is loaded"}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/test-action-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/test-action-stepimpls.feature.results.json
new file mode 100644
index 00000000..50cff53d
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-action-stepimpls.feature.results/test-action-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-action-stepimpls.feature","result":"PASSED","id":132,"executionDurationMillis":50651,"description":"A feature to test Action Step implementations","scenarios":[{"nodeId":125,"filename":"Test_Action_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]},{"nodeId":131,"filename":"Double_click_step_impls_1_results.json","result":"PASSED","tags":["fails-in-firefox","@non-visual"]}],"tags":[]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-assertions-stepimpls.feature.results/Test_Assertion_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-assertions-stepimpls.feature.results/Test_Assertion_step_impls_0_results.json
new file mode 100644
index 00000000..079a076e
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-assertions-stepimpls.feature.results/Test_Assertion_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-assertions-stepimpls.feature","lineNumber":0,"result":"PASSED","id":143,"executionDurationMillis":11013,"description":"Test Assertion step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":134,"executionDurationMillis":616,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":133,"executionDurationMillis":616,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":7,"result":"PASSED","id":135,"executionDurationMillis":333,"description":"FindById span-id-with-regex","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById span-id-with-regex"},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":8,"result":"PASSED","id":136,"executionDurationMillis":315,"description":"AssertCurrentElement text contains \"xyzabc\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElementContains(java.lang.String)","children":[],"source":"AssertCurrentElement text contains \"xyzabc\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":10,"result":"PASSED","id":137,"executionDurationMillis":274,"description":"FindById input-value-assertion-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById input-value-assertion-id"},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":11,"result":"PASSED","id":138,"executionDurationMillis":274,"description":"AssertCurrentInput value=\"123456\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"123456\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":13,"result":"PASSED","id":139,"executionDurationMillis":481,"description":"AssertNotPresent text=\"discombobulation\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertNotPresent(java.lang.String)","children":[],"source":"AssertNotPresent text=\"discombobulation\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":15,"result":"PASSED","id":140,"executionDurationMillis":554,"description":"RememberForScenario textFrom \"remember-div\" as \"context.name\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.rememberForScenario(java.lang.String,java.lang.String)","children":[],"source":"RememberForScenario textFrom \"remember-div\" as \"context.name\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":16,"result":"PASSED","id":141,"executionDurationMillis":578,"description":"AssertSame rememberedValue \"context.name\" compareToElement \"to-compare-to-matching-div\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertSame(java.lang.String,java.lang.String)","children":[],"source":"AssertSame rememberedValue \"context.name\" compareToElement \"to-compare-to-matching-div\""},{"nodeType":"Step","filename":"test-assertions-stepimpls.feature","lineNumber":17,"result":"PASSED","id":142,"executionDurationMillis":559,"description":"AssertDifferent rememberedValue \"context.name\" compareToElement \"to-compare-to-not-matching-div\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertDifferent(java.lang.String,java.lang.String)","children":[],"source":"AssertDifferent rememberedValue \"context.name\" compareToElement \"to-compare-to-not-matching-div\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-assertions-stepimpls.feature.results/test-assertions-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-assertions-stepimpls.feature.results/test-assertions-stepimpls.feature.results.json
new file mode 100644
index 00000000..eefc07ea
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-assertions-stepimpls.feature.results/test-assertions-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-assertions-stepimpls.feature","result":"PASSED","id":144,"executionDurationMillis":12705,"description":"A feature to test Assertion based Step implementations","scenarios":[{"nodeId":143,"filename":"Test_Assertion_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-css-stepimpls.feature.results/Test_css_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-css-stepimpls.feature.results/Test_css_step_impls_0_results.json
new file mode 100644
index 00000000..072a6b62
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-css-stepimpls.feature.results/Test_css_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-css-stepimpls.feature","lineNumber":0,"result":"PASSED","id":173,"executionDurationMillis":50959,"description":"Test css step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":146,"executionDurationMillis":636,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":145,"executionDurationMillis":636,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":7,"result":"PASSED","id":147,"executionDurationMillis":338,"description":"FindByCssClass \"markerClass-1\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findWithCssClass(java.lang.String)","children":[],"source":"FindByCssClass \"markerClass-1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":8,"result":"PASSED","id":148,"executionDurationMillis":1586,"description":"FindByCssClass \"somethingElse\" with text \"Another heading\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassWithText(java.lang.String,java.lang.String)","children":[],"source":"FindByCssClass \"somethingElse\" with text \"Another heading\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":9,"result":"PASSED","id":149,"executionDurationMillis":1843,"description":"FindByCssClass \"markerClass-2\" containing text \"Another\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindByCssClass \"markerClass-2\" containing text \"Another\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":12,"result":"PASSED","id":150,"executionDurationMillis":656,"description":"FindByCssSelector \"#parent_div_id\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \"#parent_div_id\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":13,"result":"PASSED","id":151,"executionDurationMillis":368,"description":"FindByCssSelector \".markerClass-1\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \".markerClass-1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":15,"result":"PASSED","id":152,"executionDurationMillis":1844,"description":"AssertCssSelector \".not-present-button\" is not present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertCssSelectorIsNotPresent(java.lang.String)","children":[],"source":"AssertCssSelector \".not-present-button\" is not present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":17,"result":"PASSED","id":153,"executionDurationMillis":1655,"description":"ClickByCssClass \"not-present-button\" if present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.clickByCssClassNoFail(java.lang.String)","children":[],"source":"ClickByCssClass \"not-present-button\" if present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":19,"result":"PASSED","id":154,"executionDurationMillis":1211,"description":"ClickByCssClass \"present-button\" if present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.clickByCssClassNoFail(java.lang.String)","children":[],"source":"ClickByCssClass \"present-button\" if present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":21,"result":"PASSED","id":155,"executionDurationMillis":571,"description":"FindByCssSelector \".present-button\" with text \"clicked\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertCssSelectorHasCorrectText(java.lang.String,java.lang.String)","children":[],"source":"FindByCssSelector \".present-button\" with text \"clicked\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":23,"result":"PASSED","id":156,"executionDurationMillis":865,"description":"AssertCssSelector \".two-divs\" count is greater than \"1\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertNumberOfElementsIsGreaterThanExpectedSize(java.lang.String,int)","children":[],"source":"AssertCssSelector \".two-divs\" count is greater than \"1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":24,"result":"PASSED","id":157,"executionDurationMillis":275,"description":"AssertCssSelector \".two-divs\" count is \"2\" or less","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertNumberOfElementsIsLessThanOrEqualToExpectedSize(java.lang.String,int)","children":[],"source":"AssertCssSelector \".two-divs\" count is \"2\" or less"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":26,"result":"PASSED","id":158,"executionDurationMillis":271,"description":"AssertCssSelector \".two-divs\" is present","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.assertCssSelectorIsPresent(java.lang.String)","children":[],"source":"AssertCssSelector \".two-divs\" is present"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":27,"result":"PASSED","id":159,"executionDurationMillis":10473,"description":"FindByCssClass \"two-divs\" using timeout \"500\" containing \"1\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassContainingTimeoutAndText(java.lang.String,long,java.lang.String)","children":[],"source":"FindByCssClass \"two-divs\" using timeout \"500\" containing \"1\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":28,"result":"PASSED","id":160,"executionDurationMillis":11608,"description":"FindByCssClass \"two-divs\" using timeout \"500\" with text \"2\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssClassWithTimeoutAndText(java.lang.String,long,java.lang.String)","children":[],"source":"FindByCssClass \"two-divs\" using timeout \"500\" with text \"2\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":30,"result":"PASSED","id":161,"executionDurationMillis":274,"description":"FindById visible-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById visible-div"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":31,"result":"PASSED","id":162,"executionDurationMillis":279,"description":"AssertCurrentElement is visible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsVisible()","children":[],"source":"AssertCurrentElement is visible"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":32,"result":"PASSED","id":163,"executionDurationMillis":1802,"description":"ClickButton \"?hide something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"hide something visible\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":33,"result":"PASSED","id":164,"executionDurationMillis":558,"description":"WaitFor CSS Selector \"#visible-div\" to be invisibile","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.waitForCssSelectorToHide(java.lang.String)","children":[],"source":"WaitFor CSS Selector \"#visible-div\" to be invisibile"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":34,"result":"PASSED","id":165,"executionDurationMillis":1695,"description":"ClickButton \"?make something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"make something visible\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":35,"result":"PASSED","id":166,"executionDurationMillis":614,"description":"WaitFor CSS Selector \"#visible-div\" to be visibile","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.waitForCssSelector(java.lang.String)","children":[],"source":"WaitFor CSS Selector \"#visible-div\" to be visibile"},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":38,"result":"PASSED","id":167,"executionDurationMillis":563,"description":"Find ByChained CSS selectors \".parent-div-class\", \".nested-div-class\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByChainedCss(java.lang.String,java.lang.String)","children":[],"source":"Find ByChained CSS selectors \".parent-div-class\", \".nested-div-class\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":39,"result":"PASSED","id":168,"executionDurationMillis":290,"description":"AssertCurrentElement text=\"expected\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"expected\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":41,"result":"PASSED","id":169,"executionDurationMillis":475,"description":"FindByCssSelector \".parent-div-class\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \".parent-div-class\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":42,"result":"PASSED","id":170,"executionDurationMillis":1433,"description":"FindChildByTag \"div\" with cssClassRegex \"nested-div-class-d{4}\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findChildByTagAndCssWildcard(java.lang.String,java.lang.String)","children":[],"source":"FindChildByTag \"div\" with cssClassRegex \"nested-div-class-\\d{4}\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":43,"result":"PASSED","id":171,"executionDurationMillis":275,"description":"AssertCurrentElement text=\"expected2\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"expected2\""},{"nodeType":"Step","filename":"test-css-stepimpls.feature","lineNumber":45,"result":"PASSED","id":172,"executionDurationMillis":928,"description":"FindNthByTagAndText from parent by CSSSelector \".parent-div-class\", tag name \"span\", tag number \"2\" containing text \"two\"","method":"public void com.technophobia.webdriver.substeps.impl.CssStepImplementations.findNthByTagContainingText(java.lang.String,java.lang.String,int,java.lang.String)","children":[],"source":"FindNthByTagAndText from parent by CSSSelector \".parent-div-class\", tag name \"span\", tag number \"2\" containing text \"two\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-css-stepimpls.feature.results/test-css-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-css-stepimpls.feature.results/test-css-stepimpls.feature.results.json
new file mode 100644
index 00000000..8732afe4
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-css-stepimpls.feature.results/test-css-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-css-stepimpls.feature","result":"PASSED","id":174,"executionDurationMillis":52521,"description":"A feature to test CSS Step implementations","scenarios":[{"nodeId":173,"filename":"Test_css_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-finders-stepimpls.feature.results/Test_finder_step_implementations_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-finders-stepimpls.feature.results/Test_finder_step_implementations_0_results.json
new file mode 100644
index 00000000..1661e1d8
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-finders-stepimpls.feature.results/Test_finder_step_implementations_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-finders-stepimpls.feature","lineNumber":0,"result":"PASSED","id":246,"executionDurationMillis":25395,"description":"Test finder step implementations","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":206,"executionDurationMillis":618,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":205,"executionDurationMillis":618,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":8,"result":"PASSED","id":207,"executionDurationMillis":412,"description":"FindByTagAndAttributesWithText tag=\"?div\"? attributes=\\[data-reactid=\"12345\"\\] with text=\"div1 with tag and attributes\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributesWithText(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributesWithText tag=\"div\" attributes=[data-reactid=\"12345\"] with text=\"div1 with tag and attributes\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":9,"result":"PASSED","id":208,"executionDurationMillis":407,"description":"FindByTagAndAttributesWithText tag=\"?div\"? attributes=\\[data-reactid=\"12345\", aria-label=\"a-label\"\\] with text=\"div2 with tag and attributes\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByTagAndAttributesWithText(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindByTagAndAttributesWithText tag=\"div\" attributes=[data-reactid=\"12345\", aria-label=\"a-label\"] with text=\"div2 with tag and attributes\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":11,"result":"PASSED","id":209,"executionDurationMillis":293,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":12,"result":"PASSED","id":210,"executionDurationMillis":289,"description":"FindChild ByName name=\"?child1\"?","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByName(java.lang.String)","children":[],"source":"FindChild ByName name=\"child1\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":13,"result":"PASSED","id":211,"executionDurationMillis":300,"description":"AssertCurrentElement text=\"child 1\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"child 1\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":15,"result":"PASSED","id":212,"executionDurationMillis":347,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":16,"result":"PASSED","id":213,"executionDurationMillis":410,"description":"FindChild ByTagAndAttributes tag=\"?div\"? attributes=\\[data-reactid=\"12345\"\\] with text=\"child div1 with tag and attributes\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildByTagAndAttributesWithText(java.lang.String,java.lang.String,java.lang.String)","children":[],"source":"FindChild ByTagAndAttributes tag=\"div\" attributes=[data-reactid=\"12345\"] with text=\"child div1 with tag and attributes\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":18,"result":"PASSED","id":214,"executionDurationMillis":293,"description":"FindFirstByTagAndAttributes tag=\"?div\"? attributes=\\[data-reactid=\"54321\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindFirstByTagAndAttributes tag=\"div\" attributes=[data-reactid=\"54321\"]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":19,"result":"PASSED","id":215,"executionDurationMillis":291,"description":"AssertCurrentElement text=\"first\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"first\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":21,"result":"PASSED","id":216,"executionDurationMillis":337,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":22,"result":"PASSED","id":217,"executionDurationMillis":308,"description":"FindFirstChild ByTagAndAttributes tag=\"?div\"? attributes=\\[data-reactid=\"6789\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstChildByTagAndAttributes(java.lang.String,java.lang.String)","children":[],"source":"FindFirstChild ByTagAndAttributes tag=\"div\" attributes=[data-reactid=\"6789\"]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":23,"result":"PASSED","id":218,"executionDurationMillis":291,"description":"AssertCurrentElement text=\"first child\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"first child\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":25,"result":"PASSED","id":219,"executionDurationMillis":286,"description":"FindById find-child-by-name-parent","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById find-child-by-name-parent"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":26,"result":"PASSED","id":220,"executionDurationMillis":291,"description":"FindFirstTagElementStartingWithText tag=\"div\" text=\"beginning with\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findFirstTagElementStartingWithText(java.lang.String,java.lang.String)","children":[],"source":"FindFirstTagElementStartingWithText tag=\"div\" text=\"beginning with\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":27,"result":"PASSED","id":221,"executionDurationMillis":359,"description":"AssertCurrentElement text=\"beginning with first\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"beginning with first\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":30,"result":"PASSED","id":222,"executionDurationMillis":288,"description":"FindNthByTagAndAttributes n=\"?2\"? tag=\"?div\"? attributes=\\[data-reactid=\"54321\"\\]","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findNthByTagAndAttributes(java.lang.Integer,java.lang.String,java.lang.String)","children":[],"source":"FindNthByTagAndAttributes n=\"2\" tag=\"div\" attributes=[data-reactid=\"54321\"]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":31,"result":"PASSED","id":223,"executionDurationMillis":289,"description":"AssertCurrentElement text=\"second\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"second\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":33,"result":"PASSED","id":224,"executionDurationMillis":298,"description":"FindByXpath //li[a/i[contains(@class, \"NOT_RUN\")]]$","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByXpath(java.lang.String)","children":[],"source":"FindByXpath //li[a/i[contains(@class, \"NOT_RUN\")]]"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":34,"result":"PASSED","id":225,"executionDurationMillis":438,"description":"AssertCurrentElement text=\"target\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"target\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":36,"result":"PASSED","id":226,"executionDurationMillis":325,"description":"FindById ul-parent-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById ul-parent-id"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":37,"result":"PASSED","id":227,"executionDurationMillis":2118,"description":"FindFirstChildElementContainingText xpath=\"li//a\" text=\"decoy\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findChildElementContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindFirstChildElementContainingText xpath=\"li//a\" text=\"decoy\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":39,"result":"PASSED","id":228,"executionDurationMillis":294,"description":"FindByTag \"div\" with text \"some text\"","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByTagAndText(java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"div\" with text \"some text\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":40,"result":"PASSED","id":229,"executionDurationMillis":352,"description":"FindByTag \"span\" containing text \"xyzabc\"","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByTagContainingText(java.lang.String,java.lang.String)","children":[],"source":"FindByTag \"span\" containing text \"xyzabc\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":42,"result":"PASSED","id":230,"executionDurationMillis":577,"description":"FindById \"span-id-with-regex\" with text matching regex w* xyzabc.*$","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByIdWithRegex(java.lang.String,java.lang.String)","children":[],"source":"FindById \"span-id-with-regex\" with text matching regex \\w* xyzabc.*"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":43,"result":"PASSED","id":231,"executionDurationMillis":291,"description":"AssertCurrentElement text=\"blah xyzabc found\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"blah xyzabc found\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":46,"result":"PASSED","id":232,"executionDurationMillis":0,"description":"NotImplemented","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.notImplementedNotFailing()","children":[],"source":"NotImplemented"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":48,"result":"PASSED","id":233,"executionDurationMillis":360,"description":"FindById visible-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById visible-div"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":49,"result":"PASSED","id":234,"executionDurationMillis":291,"description":"AssertCurrentElement is visible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsVisible()","children":[],"source":"AssertCurrentElement is visible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":50,"result":"PASSED","id":235,"executionDurationMillis":1860,"description":"ClickButton \"?hide something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"hide something visible\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":51,"result":"PASSED","id":236,"executionDurationMillis":566,"description":"WaitFor id \"visible-div\" to hide","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.waitForElementToHide(java.lang.String)","children":[],"source":"WaitFor id \"visible-div\" to hide"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":53,"result":"PASSED","id":237,"executionDurationMillis":1790,"description":"ClickButton \"?make something visible\"?","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.clickButton(java.lang.String)","children":[],"source":"ClickButton \"make something visible\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":54,"result":"PASSED","id":238,"executionDurationMillis":567,"description":"WaitFor id \"visible-div\" to be visible","method":"public void com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.waitForElementToBeVisible(java.lang.String)","children":[],"source":"WaitFor id \"visible-div\" to be visible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":56,"result":"PASSED","id":239,"executionDurationMillis":284,"description":"FindById not-visible-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById not-visible-div"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":57,"result":"PASSED","id":240,"executionDurationMillis":375,"description":"AssertCurrentElement is invisible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsInVisible()","children":[],"source":"AssertCurrentElement is invisible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":59,"result":"PASSED","id":241,"executionDurationMillis":294,"description":"FindById not-visible-hidden-div","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById not-visible-hidden-div"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":60,"result":"PASSED","id":242,"executionDurationMillis":294,"description":"AssertCurrentElement is invisible","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.assertCurrentElementIsInVisible()","children":[],"source":"AssertCurrentElement is invisible"},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":62,"result":"PASSED","id":243,"executionDurationMillis":300,"description":"FindBy xpath with token replacement \\$x\\//li[a/i[contains \"FAILED\"]]\"\\)(.*)$","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.NewFinderStepImplementations.findByXpathWithTokenReplacement(java.lang.String,java.lang.String)","children":[],"source":"FindBy xpath with token replacement $x(\"//li[a/i[contains(@class, '%s')]]\") \"FAILED\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":63,"result":"PASSED","id":244,"executionDurationMillis":298,"description":"AssertCurrentElement text=\"target2\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertTextInCurrentElement(java.lang.String)","children":[],"source":"AssertCurrentElement text=\"target2\""},{"nodeType":"Step","filename":"test-finders-stepimpls.feature","lineNumber":65,"result":"PASSED","id":245,"executionDurationMillis":453,"description":"FindById id-for-partial-text-match containing text=\"jarlsberg\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.assertEventuallyContains(java.lang.String,java.lang.String)","children":[],"source":"FindById id-for-partial-text-match containing text=\"jarlsberg\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-finders-stepimpls.feature.results/test-finders-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-finders-stepimpls.feature.results/test-finders-stepimpls.feature.results.json
new file mode 100644
index 00000000..8cdef7ba
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-finders-stepimpls.feature.results/test-finders-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-finders-stepimpls.feature","result":"PASSED","id":247,"executionDurationMillis":27238,"description":"A feature to test finder step implementations description","scenarios":[{"nodeId":246,"filename":"Test_finder_step_implementations_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-form-step-impls.feature.results/Test_Form_step_impls_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-form-step-impls.feature.results/Test_Form_step_impls_0_results.json
new file mode 100644
index 00000000..9667b075
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-form-step-impls.feature.results/Test_Form_step_impls_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-form-step-impls.feature","lineNumber":0,"result":"PASSED","id":203,"executionDurationMillis":33791,"description":"Test Form step impls","children":[{"nodeType":"SubstepNode","filename":"self-test.substeps","lineNumber":1,"result":"PASSED","id":176,"executionDurationMillis":639,"description":"Given I go to the self test page","children":[{"nodeType":"Step","filename":"self-test.substeps","lineNumber":2,"result":"PASSED","id":175,"executionDurationMillis":639,"description":"NavigateTo /self-test.html","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateTo(java.lang.String)","children":[],"source":"NavigateTo /self-test.html"}],"source":"Given I go to the self test page"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":7,"result":"PASSED","id":177,"executionDurationMillis":1741,"description":"ClearAndSendKeys \"a test\" to id input-value-assertion-id","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeysById(java.lang.String,java.lang.String)","children":[],"source":"ClearAndSendKeys \"a test\" to id input-value-assertion-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":9,"result":"PASSED","id":178,"executionDurationMillis":307,"description":"FindById input-value-assertion-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById input-value-assertion-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":10,"result":"PASSED","id":179,"executionDurationMillis":275,"description":"AssertCurrentInput value=\"a test\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"a test\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":12,"result":"PASSED","id":180,"executionDurationMillis":285,"description":"FindById select_id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById select_id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":13,"result":"PASSED","id":181,"executionDurationMillis":2921,"description":"ChooseOption \"number three option\" in current element","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectValueInCurrentElement(java.lang.String)","children":[],"source":"ChooseOption \"number three option\" in current element"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":14,"result":"PASSED","id":182,"executionDurationMillis":1930,"description":"AssertSelect id=\"select_id\" text=\"number three option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"select_id\" text=\"number three option\" is currently selected"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":16,"result":"PASSED","id":183,"executionDurationMillis":2985,"description":"Select \"number one option\" option in Id \"select_id\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectOptionInId(java.lang.String,java.lang.String) throws java.lang.InterruptedException","children":[],"source":"Select \"number one option\" option in Id \"select_id\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":17,"result":"PASSED","id":184,"executionDurationMillis":1220,"description":"AssertSelect id=\"select_id\" text=\"number one option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"select_id\" text=\"number one option\" is currently selected"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":19,"result":"PASSED","id":185,"executionDurationMillis":2056,"description":"Select \"number two option\" option in class \"select-marker-class\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.selectOptionInClass(java.lang.String,java.lang.String)","children":[],"source":"Select \"number two option\" option in class \"select-marker-class\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":20,"result":"PASSED","id":186,"executionDurationMillis":1405,"description":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.assertOptionIsSelected(java.lang.String,java.lang.String)","children":[],"source":"AssertSelect id=\"select_id\" text=\"number two option\" is currently selected"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":22,"result":"PASSED","id":187,"executionDurationMillis":280,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":23,"result":"PASSED","id":188,"executionDurationMillis":567,"description":"SendKeys \"abc123\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeys(java.lang.String)","children":[],"source":"SendKeys \"abc123\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":24,"result":"PASSED","id":189,"executionDurationMillis":278,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":25,"result":"PASSED","id":190,"executionDurationMillis":272,"description":"AssertCurrentInput value=\"abc123\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"abc123\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":27,"result":"PASSED","id":191,"executionDurationMillis":473,"description":"SendKey Key\\.BACK_SPACE","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKey(java.lang.String)","children":[],"source":"SendKey Key.BACK_SPACE"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":28,"result":"PASSED","id":192,"executionDurationMillis":280,"description":"AssertCurrentInput value=\"abc12\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertValueInCurrentInput(java.lang.String)","children":[],"source":"AssertCurrentInput value=\"abc12\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":30,"result":"PASSED","id":193,"executionDurationMillis":321,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":31,"result":"PASSED","id":194,"executionDurationMillis":1240,"description":"ClearAndSendKeys \"\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.clearAndSendKeys(java.lang.String)","children":[],"source":"ClearAndSendKeys \"\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":33,"result":"PASSED","id":195,"executionDurationMillis":615,"description":"SendKeys pathOf property \"test.filename\" to current element","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeysToCurrentElement(java.lang.String)","children":[],"source":"SendKeys pathOf property \"test.filename\" to current element"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":34,"result":"PASSED","id":196,"executionDurationMillis":283,"description":"AssertCurrentInput value contains \"one.file\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentInputContainsText(java.lang.String)","children":[],"source":"AssertCurrentInput value contains \"one.file\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":36,"result":"PASSED","id":197,"executionDurationMillis":281,"description":"FindById text-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById text-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":37,"result":"PASSED","id":198,"executionDurationMillis":4352,"description":"ClearAndSendKeys \"\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.clearAndSendKeys(java.lang.String)","children":[],"source":"ClearAndSendKeys \"\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":39,"result":"PASSED","id":199,"executionDurationMillis":788,"description":"SendKeys pathOf property \"test.filename2\" to id \"text-id\"","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.sendKeysToId(java.lang.String,java.lang.String)","children":[],"source":"SendKeys pathOf property \"test.filename2\" to id \"text-id\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":40,"result":"PASSED","id":200,"executionDurationMillis":338,"description":"AssertCurrentInput value contains \"two.file\"","method":"public void com.technophobia.webdriver.substeps.impl.AssertionWebDriverSubStepImplementations.assertCurrentInputContainsText(java.lang.String)","children":[],"source":"AssertCurrentInput value contains \"two.file\""},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":42,"result":"PASSED","id":201,"executionDurationMillis":282,"description":"FindById input-value-to-submit-id","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findById(java.lang.String)","children":[],"source":"FindById input-value-to-submit-id"},{"nodeType":"Step","filename":"test-form-step-impls.feature","lineNumber":43,"result":"PASSED","id":202,"executionDurationMillis":435,"description":"Submit","method":"public void com.technophobia.webdriver.substeps.impl.FormWebDriverSubStepImplementations.submit()","children":[],"source":"Submit"}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-form-step-impls.feature.results/test-form-step-impls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-form-step-impls.feature.results/test-form-step-impls.feature.results.json
new file mode 100644
index 00000000..4d88d9ba
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-form-step-impls.feature.results/test-form-step-impls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-form-step-impls.feature","result":"PASSED","id":204,"executionDurationMillis":35327,"description":"A feature to test Form Step implementations","scenarios":[{"nodeId":203,"filename":"Test_Form_step_impls_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-window-stepimpls.feature.results/Test_iframe_and_window_step_implementations_0_results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-window-stepimpls.feature.results/Test_iframe_and_window_step_implementations_0_results.json
new file mode 100644
index 00000000..12900ec4
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-window-stepimpls.feature.results/Test_iframe_and_window_step_implementations_0_results.json
@@ -0,0 +1 @@
+{"nodeType":"BasicScenarioNode","filename":"test-window-stepimpls.feature","lineNumber":0,"result":"PASSED","id":260,"executionDurationMillis":14028,"description":"Test iframe and window step implementations","children":[{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":7,"result":"PASSED","id":248,"executionDurationMillis":791,"description":"NavigateTo url property \"iframe.test.page\"","method":"public void com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations.navigateToProperty(java.lang.String)","children":[],"source":"NavigateTo url property \"iframe.test.page\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":9,"result":"PASSED","id":249,"executionDurationMillis":554,"description":"FindById outer-div-id and text = \"iframe page\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById outer-div-id and text = \"iframe page\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":11,"result":"PASSED","id":250,"executionDurationMillis":2751,"description":"Switch to new frame by name \"iframe-name\"","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToNewFrameByName(java.lang.String)","children":[],"source":"Switch to new frame by name \"iframe-name\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":12,"result":"PASSED","id":251,"executionDurationMillis":290,"description":"FindById iframed-div-id and text = \"iframed div\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById iframed-div-id and text = \"iframed div\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":14,"result":"PASSED","id":252,"executionDurationMillis":280,"description":"Switch to default content","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToDefaultContent()","children":[],"source":"Switch to default content"},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":15,"result":"PASSED","id":253,"executionDurationMillis":351,"description":"FindById outer-div-id and text = \"iframe page\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById outer-div-id and text = \"iframe page\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":17,"result":"PASSED","id":254,"executionDurationMillis":566,"description":"Switch to new frame by CSS selector \".iframe-class\"","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToNewFrame(java.lang.String)","children":[],"source":"Switch to new frame by CSS selector \".iframe-class\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":18,"result":"PASSED","id":255,"executionDurationMillis":284,"description":"FindById iframed-div-id and text = \"iframed div\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById iframed-div-id and text = \"iframed div\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":20,"result":"PASSED","id":256,"executionDurationMillis":379,"description":"Switch to default content","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchToDefaultContent()","children":[],"source":"Switch to default content"},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":21,"result":"PASSED","id":257,"executionDurationMillis":287,"description":"FindByCssSelector \".iframe-class\"","method":"public org.openqa.selenium.WebElement com.technophobia.webdriver.substeps.impl.CssStepImplementations.findByCssSelector(java.lang.String)","children":[],"source":"FindByCssSelector \".iframe-class\""},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":22,"result":"PASSED","id":258,"executionDurationMillis":281,"description":"SwitchFrameToCurrentElement","method":"public void com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations.switchFrameToCurrentElement()","children":[],"source":"SwitchFrameToCurrentElement"},{"nodeType":"Step","filename":"test-window-stepimpls.feature","lineNumber":23,"result":"PASSED","id":259,"executionDurationMillis":354,"description":"FindById iframed-div-id and text = \"iframed div\"","method":"public void com.technophobia.webdriver.substeps.impl.FinderWebDriverSubStepImplementations.findByIdAndText(java.lang.String,java.lang.String)","children":[],"source":"FindById iframed-div-id and text = \"iframed div\""}],"tags":["@non-visual"]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/test-window-stepimpls.feature.results/test-window-stepimpls.feature.results.json b/core/src/test/resources/sample-results-data-dupe-ids/2/test-window-stepimpls.feature.results/test-window-stepimpls.feature.results.json
new file mode 100644
index 00000000..42b7a5f9
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/test-window-stepimpls.feature.results/test-window-stepimpls.feature.results.json
@@ -0,0 +1 @@
+{"nodeType":"FeatureNode","filename":"test-window-stepimpls.feature","result":"PASSED","id":261,"executionDurationMillis":15870,"description":"A feature to test iframe and window related step impls","scenarios":[{"nodeId":260,"filename":"Test_iframe_and_window_step_implementations_0_results.json","result":"PASSED","tags":["@non-visual"]}],"tags":[]}
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/uncalled.stepdefs.js b/core/src/test/resources/sample-results-data-dupe-ids/2/uncalled.stepdefs.js
new file mode 100644
index 00000000..7634800a
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/uncalled.stepdefs.js
@@ -0,0 +1 @@
+[{"line":"And I click the context menu","source":"self-test.substeps","lineNumber":103},{"line":"And I double click the link","source":"self-test.substeps","lineNumber":118},{"line":"Check to see if I wait until the div I\u0027ve just made visbile in 2 secs is clickable","source":"self-test.substeps","lineNumber":171},{"line":"Given I click the Dont click me button","source":"self-test.substeps","lineNumber":144},{"line":"Given that I\u0027ve not double clicked the link","source":"self-test.substeps","lineNumber":114},{"line":"Given the context menu hasn\u0027t been clicked","source":"self-test.substeps","lineNumber":98},{"line":"Then I can see I\u0027ve double clicked","source":"self-test.substeps","lineNumber":122},{"line":"Then I click a button which makes the div I\u0027m going to click visible in 2 seconds time","source":"self-test.substeps","lineNumber":167},{"line":"Then I see \"context has been clicked\"","source":"self-test.substeps","lineNumber":108},{"line":"Then I see an Alert \"\u003cmsg\u003e\"","source":"self-test.substeps","lineNumber":147},{"line":"Then the page title is \"\u003cpage_title\u003e\"","source":"self-test.substeps","lineNumber":87}]
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/2/uncalled.stepimpls.js b/core/src/test/resources/sample-results-data-dupe-ids/2/uncalled.stepimpls.js
new file mode 100644
index 00000000..9a335e3b
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/2/uncalled.stepimpls.js
@@ -0,0 +1 @@
+[{"value":"Close new window","implementedIn":"com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations","method":"closeWebDriver","keyword":"Close"},{"value":"PerformContextClick","implementedIn":"com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations","method":"performContextClick","keyword":"PerformContextClick"},{"value":"TakeScreenshot with prefix \"([^\"]*)\"","implementedIn":"com.technophobia.webdriver.substeps.impl.ActionWebDriverSubStepImplementations","method":"takeScreenshot","keyword":"TakeScreenshot"},{"value":"Switch to new window","implementedIn":"com.technophobia.webdriver.substeps.impl.IFrameAndWindowSubstepImplementations","method":"switchToNewWindow","keyword":"Switch"},{"value":"AssertScreenshotFileExists \"([^\"]*)\" something","implementedIn":"org.substeps.webdriver.TestWebdriverStepImplementations","method":"assertScreenShotFileCreated","keyword":"AssertScreenshotFileExists"}]
\ No newline at end of file
diff --git a/core/src/test/resources/sample-results-data-dupe-ids/masterConfig.conf b/core/src/test/resources/sample-results-data-dupe-ids/masterConfig.conf
new file mode 100644
index 00000000..a446e629
--- /dev/null
+++ b/core/src/test/resources/sample-results-data-dupe-ids/masterConfig.conf
@@ -0,0 +1,201 @@
+org {
+ substeps {
+ baseExecutionConfig {
+ executionListeners=[
+ "com.technophobia.substeps.runner.logger.StepExecutionLogger"
+ ]
+ fastFailParseErrors=true
+ featureFile="/home/ian/projects/github/substeps-webdriver/target/test-classes/features"
+ stepImplementationClassNames=[
+ "com.technophobia.webdriver.substeps.impl.BaseWebdriverSubStepImplementations",
+ "org.substeps.webdriver.TestWebdriverStepImplementations"
+ ]
+ substepsFile="/home/ian/projects/github/substeps-webdriver/target/test-classes/substeps"
+ tags="@non-visual"
+ webdriver {
+ driver {
+ type=REMOTE
+ }
+ remote {
+ driver {
+ platform=Linux
+ url="https://iantmoore:70841465-3d97-4e39-9a22-d5dfbba70e64@ondemand.saucelabs.com:443/wd/hub"
+ }
+ }
+ }
+ }
+ config {
+ checkForUncalledAndUnused=true
+ current {
+ branchname=moved-t-new-custom-java-doc-tags
+ projectversion="1.1.3-SNAPSHOT"
+ }
+ description="Self Test Features"
+ executionResultsCollector="org.substeps.report.ExecutionResultsCollector"
+ glossary {
+ excludeStepImplementationClassNames=[
+ "org.substeps.webdriver.TestWebdriverStepImplementations"
+ ]
+ }
+ jmxPort=9999
+ log {
+ unused {
+ uncalled=false
+ }
+ }
+ parameter {
+ substitution {
+ enabled=true
+ end="}"
+ normalize {
+ from=ISO-8859-1
+ to=UTF-8
+ }
+ normalizeValue=false
+ start="${"
+ }
+ }
+ report {
+ data {
+ pretty {
+ print=false
+ }
+ }
+ rootNodeDescriptionProvider="org.substeps.report.DefaultDescriptionProvider"
+ }
+ reportBuilder="org.substeps.report.ReportBuilder"
+ reportDir="target/substeps_report"
+ rootDataDir="target/substeps_data"
+ runTestsInForkedVM=true
+ step {
+ depth {
+ description=6
+ }
+ }
+ }
+ executionConfigs=[
+ {
+ dataOutputDir="1"
+ description="Chrome Self Test Features"
+ executionListeners=[
+ "com.technophobia.substeps.runner.logger.StepExecutionLogger"
+ ]
+ fastFailParseErrors=true
+ featureFile="/home/ian/projects/github/substeps-webdriver/target/test-classes/features"
+ stepImplementationClassNames=[
+ "com.technophobia.webdriver.substeps.impl.BaseWebdriverSubStepImplementations",
+ "org.substeps.webdriver.TestWebdriverStepImplementations"
+ ]
+ substepsFile="/home/ian/projects/github/substeps-webdriver/target/test-classes/substeps"
+ tags="@non-visual"
+ webdriver {
+ driver {
+ type=REMOTE
+ }
+ remote {
+ driver {
+ base {
+ capability=chrome
+ }
+ platform=Linux
+ url="https://iantmoore:70841465-3d97-4e39-9a22-d5dfbba70e64@ondemand.saucelabs.com:443/wd/hub"
+ version=48
+ }
+ }
+ }
+ },
+ {
+ dataOutputDir="2"
+ description="Firefox Self Test Features"
+ executionListeners=[
+ "com.technophobia.substeps.runner.logger.StepExecutionLogger"
+ ]
+ fastFailParseErrors=true
+ featureFile="/home/ian/projects/github/substeps-webdriver/target/test-classes/features"
+ nonFatalTags=fails-in-firefox
+ stepImplementationClassNames=[
+ "com.technophobia.webdriver.substeps.impl.BaseWebdriverSubStepImplementations",
+ "org.substeps.webdriver.TestWebdriverStepImplementations"
+ ]
+ substepsFile="/home/ian/projects/github/substeps-webdriver/target/test-classes/substeps"
+ tags="@non-visual"
+ webdriver {
+ driver {
+ type=REMOTE
+ }
+ remote {
+ driver {
+ base {
+ capability=firefox
+ }
+ platform=Linux
+ url="https://iantmoore:70841465-3d97-4e39-9a22-d5dfbba70e64@ondemand.saucelabs.com:443/wd/hub"
+ version=45
+ }
+ }
+ }
+ }
+ ]
+ webdriver {
+ base {
+ url="http://substeps.github.io/substeps-webdriver/"
+ }
+ default {
+ driverFactories=[
+ {
+ class="org.substeps.webdriver.ChromeDriverFactory"
+ key=CHROME
+ },
+ {
+ class="org.substeps.webdriver.FirefoxDriverFactory"
+ key=FIREFOX
+ },
+ {
+ class="org.substeps.webdriver.HTMLUnitDriverFactory"
+ key=HTMLUNIT
+ },
+ {
+ class="org.substeps.webdriver.IEDriverFactory"
+ key=IE
+ },
+ {
+ class="org.substeps.webdriver.RemoteDriverFactory"
+ key=REMOTE
+ }
+ ]
+ timeout {
+ secs=5
+ }
+ }
+ htmlunit {
+ disable {
+ javascript=false
+ }
+ }
+ locale=en-gb
+ log {
+ pagesource {
+ onerror=false
+ }
+ }
+ network {
+ proxy {
+ host=""
+ port=8080
+ }
+ }
+ reuse-strategy="shutdown_and_create_new"
+ wait {
+ seconds=25
+ }
+ webdriver {
+ manager {
+ properties="substeps-webdrivermanager.properties"
+ }
+ }
+ window {
+ maximise=true
+ }
+ }
+ }
+}
diff --git a/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala b/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala
index ff34a24d..f543f56f 100644
--- a/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala
+++ b/core/src/test/scala/org/substeps/report/ReportBuilderTest.scala
@@ -17,6 +17,8 @@ import org.hamcrest.text.IsEqualIgnoringWhiteSpace.equalToIgnoringWhiteSpace
import org.json4s.native.JsonMethods.parse
import org.substeps.config.SubstepsConfigLoader
+import scala.collection.mutable
+
/**
* Created by ian on 30/06/16.
*/
@@ -155,4 +157,45 @@ class ReportBuilderTest extends FlatSpec with Matchers{
}
+ "report builder " should "build a report when there are overlapping ids in the source data" in {
+
+ val now: LocalDateTime = LocalDateTime.now
+
+ val outputDir = getOutputDir
+
+ val reportBuilder = new ReportBuilder
+
+ val uri = this.getClass.getClassLoader.getResource("sample-results-data-dupe-ids")
+
+ reportBuilder.buildFromDirectory(new File(uri.getFile), outputDir)
+
+ outputDir.exists() should be (true)
+
+ val reportFiles = outputDir.listFiles().toList
+
+ val detail_data: Option[File] = reportFiles.find(f => f.getName == "detail_data.js")
+
+ detail_data shouldBe defined
+
+ val detailDataLines = Files.asCharSource(detail_data.get, Charset.defaultCharset()).readLines()
+
+ val ids =
+ detailDataLines.flatMap(line => {
+ if (line.contains("detail[")){
+ Some(Integer.parseInt(line.substring(7, line.indexOf("]", 7))))
+ }
+ else {
+ None
+ }
+ })
+
+
+ val grouped : List[(Int, Int)] = ids.groupBy(i => i).mapValues(_.size).toList
+
+ val dupes = grouped.filter(g => g._2 > 1)
+
+ dupes.foreach(d => println("dupe id: " + d))
+
+ dupes shouldBe empty
+ }
}
diff --git a/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsRunnerMojo.java b/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsRunnerMojo.java
index 78526e71..0ba5d346 100644
--- a/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsRunnerMojo.java
+++ b/runner/Maven/src/main/java/com/technophobia/substeps/runner/SubstepsRunnerMojo.java
@@ -237,6 +237,8 @@ private void processBuildData() throws MojoFailureException {
if (this.buildFailureManager.testSuiteFailed()) {
+ this.getLog().info("buildFailureManager.testSuiteFailed");
+
MojoFailureException e = new MojoFailureException("Substep Execution failed:\n"
+ this.buildFailureManager.getBuildFailureInfo());
@@ -251,13 +253,19 @@ private void processBuildData() throws MojoFailureException {
this.session.getResult().addException(e);
}
else {
+ this.getLog().info("throwing ex..");
throw e;
}
} else if (!this.buildFailureManager.testSuiteCompletelyPassed()) {
+ this.getLog().info("buildFailureManager.testSuiteCompletelyPassed");
+
// print out the failure string (but won't include any failures)
getLog().info(this.buildFailureManager.getBuildFailureInfo());
}
+ else {
+ this.getLog().info("other...");
+ }
}