cucumber.runtime.UndefinedStepsTracker Maven / Gradle / Ivy
package cucumber.runtime;
import cucumber.api.event.EventHandler;
import cucumber.api.event.EventListener;
import cucumber.api.event.EventPublisher;
import cucumber.api.event.SnippetsSuggestedEvent;
import cucumber.api.event.TestSourceRead;
import gherkin.AstBuilder;
import gherkin.GherkinDialect;
import gherkin.GherkinDialectProvider;
import gherkin.IGherkinDialectProvider;
import gherkin.Parser;
import gherkin.ParserException;
import gherkin.TokenMatcher;
import gherkin.ast.Background;
import gherkin.ast.GherkinDocument;
import gherkin.ast.ScenarioDefinition;
import gherkin.ast.Step;
import gherkin.pickles.PickleLocation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UndefinedStepsTracker implements EventListener {
private final List snippets = new ArrayList();
private final IGherkinDialectProvider dialectProvider = new GherkinDialectProvider();
private final Map pathToSourceMap = new HashMap();
private final Map pathToStepMap = new HashMap();
private boolean hasUndefinedSteps = false;
private EventHandler testSourceReadHandler = new EventHandler() {
@Override
public void receive(TestSourceRead event) {
pathToSourceMap.put(event.uri, event.source);
}
};
private EventHandler snippetsSuggestedHandler = new EventHandler() {
@Override
public void receive(SnippetsSuggestedEvent event) {
handleSnippetsSuggested(event.uri, event.stepLocations, event.snippets);
}
};
@Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestSourceRead.class, testSourceReadHandler);
publisher.registerHandlerFor(SnippetsSuggestedEvent.class, snippetsSuggestedHandler);
}
public boolean hasUndefinedSteps() {
return hasUndefinedSteps;
}
public List getSnippets() {
return snippets;
}
void handleSnippetsSuggested(String uri, List stepLocations, List snippets) {
hasUndefinedSteps = true;
String keyword = givenWhenThenKeyword(uri, stepLocations);
for (String rawSnippet : snippets) {
String snippet = rawSnippet.replace("**KEYWORD**", keyword);
if (!this.snippets.contains(snippet)) {
this.snippets.add(snippet);
}
}
}
private String givenWhenThenKeyword(String uri, List stepLocations) {
String keyword = null;
if (!stepLocations.isEmpty()) {
if (pathToSourceMap.containsKey(uri)) {
keyword = getKeywordFromSource(uri, stepLocations);
}
}
return keyword != null ? keyword : getFirstGivenKeyword(dialectProvider.getDefaultDialect());
}
private String getKeywordFromSource(String path, List stepLocations) {
if (!pathToStepMap.containsKey(path)) {
createFeatureStepMap(path);
}
if (!pathToStepMap.containsKey(path)) {
return null;
}
GherkinDialect featureDialect = pathToStepMap.get(path).dialect;
List givenThenWhenKeywords = getGivenWhenThenKeywords(featureDialect);
Map stepMap = pathToStepMap.get(path).stepMap;
for (PickleLocation stepLocation : stepLocations) {
if (!stepMap.containsKey(stepLocation.getLine())) {
continue;
}
for (StepNode stepNode = stepMap.get(stepLocation.getLine()); stepNode != null; stepNode = stepNode.previous) {
for (String keyword : givenThenWhenKeywords) {
if (!keyword.equals("* ") && keyword == stepNode.step.getKeyword()) {
return convertToCodeKeyword(keyword);
}
}
}
}
return getFirstGivenKeyword(featureDialect);
}
private void createFeatureStepMap(String path) {
if (!pathToSourceMap.containsKey(path)) {
return;
}
Parser parser = new Parser(new AstBuilder());
TokenMatcher matcher = new TokenMatcher();
try {
GherkinDocument gherkinDocument = parser.parse(pathToSourceMap.get(path), matcher);
Map stepMap = new HashMap();
StepNode initialPreviousNode = null;
for (ScenarioDefinition child : gherkinDocument.getFeature().getChildren()) {
StepNode lastStepNode = processScenarioDefinition(stepMap, initialPreviousNode, child);
if (child instanceof Background) {
initialPreviousNode = lastStepNode;
}
}
pathToStepMap.put(path, new FeatureStepMap(new GherkinDialectProvider(gherkinDocument.getFeature().getLanguage()).getDefaultDialect(), stepMap));
} catch (ParserException e) {
// Ignore exceptions
}
}
private StepNode processScenarioDefinition(Map stepMap, StepNode initialPreviousNode, ScenarioDefinition child) {
StepNode previousNode = initialPreviousNode;
for (Step step : child.getSteps()) {
StepNode stepNode = new StepNode(step, previousNode);
stepMap.put(step.getLocation().getLine(), stepNode);
previousNode = stepNode;
}
return previousNode;
}
private List getGivenWhenThenKeywords(GherkinDialect dialect) {
List keywords = new ArrayList();
keywords.addAll(dialect.getGivenKeywords());
keywords.addAll(dialect.getWhenKeywords());
keywords.addAll(dialect.getThenKeywords());
return keywords;
}
private String getFirstGivenKeyword(GherkinDialect i18n) {
for (String keyword : i18n.getGivenKeywords()) {
if (!keyword.equals("* ")) {
return convertToCodeKeyword(keyword);
}
}
return null;
}
private String convertToCodeKeyword(String keyword) {
return keyword.replaceAll("[\\s',!]", "");
}
private static final class FeatureStepMap {
final GherkinDialect dialect;
final Map stepMap;
FeatureStepMap(GherkinDialect dialect, Map stepMap) {
this.dialect = dialect;
this.stepMap = stepMap;
}
}
private static final class StepNode {
final Step step;
final StepNode previous;
StepNode(Step step, StepNode previous) {
this.step = step;
this.previous = previous;
}
}
}