Permalink
Browse files

Exclude unmatched features from AST (close #80)

This improves the way features are selected for inclusion in the AST. This fixes the issue where unmatched feature headers were output by the formatters.
  • Loading branch information...
1 parent 4ae4bec commit f8e4845947f006bb058e22292cc0a30760f725e3 @jbpros jbpros committed Oct 7, 2012
@@ -1,5 +1,5 @@
var Assembler = function(features, filter) {
- var currentFeature, currentScenarioOrBackground, currentStep;
+ var currentFeature, currentScenarioOrBackground, currentStep, suggestedFeature;
var stashedTags = [];
var self = {
@@ -67,16 +67,17 @@ var Assembler = function(features, filter) {
},
insertFeature: function insertFeature(feature) {
+ self.tryEnrollingSuggestedFeature();
self.applyStashedTagsToElement(feature);
self.setCurrentFeature(feature);
- features.addFeature(feature);
+ self.suggestFeature(feature);
},
insertScenario: function insertScenario(scenario) {
self.applyCurrentFeatureTagsToElement(scenario);
self.applyStashedTagsToElement(scenario);
self.setCurrentScenarioOrBackground(scenario);
- if (filter.isScenarioEnrolled(scenario)) {
+ if (filter.isElementEnrolled(scenario)) {
var currentFeature = self.getCurrentFeature();
currentFeature.addScenario(scenario);
}
@@ -90,6 +91,29 @@ var Assembler = function(features, filter) {
insertTag: function insertTag(tag) {
self.stashTag(tag);
+ },
+
+ finish: function finish() {
+ self.tryEnrollingSuggestedFeature();
+ },
+
+ suggestFeature: function suggestFeature(feature) {
+ suggestedFeature = feature;
+ },
+
+ isSuggestedFeatureEnrollable: function isSuggestedFeatureEnrollable() {
+ var enrollable = suggestedFeature && (suggestedFeature.hasScenarios() || filter.isElementEnrolled(suggestedFeature));
+ return enrollable;
+ },
+
+ tryEnrollingSuggestedFeature: function tryEnrollingSuggestedFeature() {
+ if (self.isSuggestedFeatureEnrollable())
+ self.enrolSuggestedFeature();
+ },
+
+ enrolSuggestedFeature: function enrolSuggestedFeature() {
+ features.addFeature(suggestedFeature);
+ suggestedFeature = null;
}
};
return self;
@@ -48,6 +48,10 @@ var Feature = function(keyword, name, description, uri, line) {
return scenarios.getLast();
},
+ hasScenarios: function hasScenarios() {
+ return scenarios.length() > 0;
+ },
+
addTags: function setTags(newTags) {
tags = tags.concat(newTags);
},
@@ -2,9 +2,9 @@ var _ = require('underscore');
var Filter = function(rules) {
var self = {
- isScenarioEnrolled: function isScenarioEnrolled(scenario) {
+ isElementEnrolled: function isElementEnrolled(element) {
var enrolled = _.all(rules, function(rule) {
- return rule.isSatisfiedByElement(scenario);
+ return rule.isSatisfiedByElement(element);
});
return enrolled;
}
@@ -13,4 +13,4 @@ var Filter = function(rules) {
};
Filter.AnyOfTagsRule = require('./filter/any_of_tags_rule');
Filter.ElementMatchingTagSpec = require('./filter/element_matching_tag_spec');
-module.exports = Filter;
+module.exports = Filter;
View
@@ -55,7 +55,9 @@ var Parser = function(featureSources, astFilter) {
astAssembler.insertDocString(docString);
},
- handleEof: function handleEof() {},
+ handleEof: function handleEof() {
+ astAssembler.finish();
+ },
handleFeature: function handleFeature(keyword, name, description, line) {
var uri = self.getCurrentSourceUri();
@@ -15,7 +15,7 @@ var Hook = function(code, options) {
appliesToScenario: function appliesToScenario(scenario) {
var astFilter = self.getAstFilter();
- return astFilter.isScenarioEnrolled(scenario);
+ return astFilter.isElementEnrolled(scenario);
},
getAstFilter: function getAstFilter() {
@@ -176,9 +176,15 @@ describe("Cucumber.Ast.Assembler", function() {
beforeEach(function() {
feature = createSpy("feature");
- spyOnStub(features, 'addFeature');
+ spyOn(assembler, 'tryEnrollingSuggestedFeature');
spyOn(assembler, 'applyStashedTagsToElement');
spyOn(assembler, 'setCurrentFeature');
+ spyOn(assembler, 'suggestFeature');
+ });
+
+ it("tries to enroll the suggested feature, if any", function () {
+ assembler.insertFeature(feature);
+ expect(assembler.tryEnrollingSuggestedFeature).toHaveBeenCalled();
});
it("applies the stashed tags to the feature", function() {
@@ -191,9 +197,9 @@ describe("Cucumber.Ast.Assembler", function() {
expect(assembler.setCurrentFeature).toHaveBeenCalledWith(feature);
});
- it("adds the feature to the root features", function() {
+ it("suggests the feature to be added to root features", function() {
assembler.insertFeature(feature);
- expect(features.addFeature).toHaveBeenCalledWith(feature);
+ expect(assembler.suggestFeature).toHaveBeenCalledWith(feature);
});
});
@@ -243,7 +249,7 @@ describe("Cucumber.Ast.Assembler", function() {
beforeEach(function() {
scenario = createSpy("scenario");
currentFeature = createSpyWithStubs("current feature", {addScenario: null});
- spyOnStub(filter, 'isScenarioEnrolled');
+ spyOnStub(filter, 'isElementEnrolled');
spyOn(assembler, 'applyStashedTagsToElement');
spyOn(assembler, 'applyCurrentFeatureTagsToElement');
spyOn(assembler, 'getCurrentFeature').andReturn(currentFeature);
@@ -267,12 +273,12 @@ describe("Cucumber.Ast.Assembler", function() {
it("asks the filter if the scenario is enrolled", function() {
assembler.insertScenario(scenario);
- expect(filter.isScenarioEnrolled).toHaveBeenCalledWith(scenario);
+ expect(filter.isElementEnrolled).toHaveBeenCalledWith(scenario);
});
describe("when the scenario is enrolled", function() {
beforeEach(function() {
- filter.isScenarioEnrolled.andReturn(true);
+ filter.isElementEnrolled.andReturn(true);
});
it("gets the current feature", function() {
@@ -288,7 +294,7 @@ describe("Cucumber.Ast.Assembler", function() {
describe("when the scenario is not enrolled", function() {
beforeEach(function() {
- filter.isScenarioEnrolled.andReturn(false);
+ filter.isElementEnrolled.andReturn(false);
});
it("does not get the current feature", function() {
@@ -342,4 +348,126 @@ describe("Cucumber.Ast.Assembler", function() {
expect(assembler.stashTag).toHaveBeenCalledWith(tag);
});
});
+
+ describe("finish()", function () {
+ beforeEach(function() {
+ spyOn(assembler, 'tryEnrollingSuggestedFeature');
+ });
+
+ it("tries to enroll the suggested feature, if any", function () {
+ assembler.finish();
+ expect(assembler.tryEnrollingSuggestedFeature).toHaveBeenCalled();
+ });
+ });
+
+ describe("isSuggestedFeatureEnrollable() [suggestFeature()]", function () {
+ it("is falsy when no feature is suggested for enrolment", function () {
+ var enrollable = assembler.isSuggestedFeatureEnrollable();
+ expect(enrollable).toBeFalsy();
+ });
+
+ describe("when a feature is suggested", function () {
+ var feature;
+
+ beforeEach(function () {
+ feature = createSpyWithStubs("suggested feature", {hasScenarios: null});
+ assembler.suggestFeature(feature);
+ spyOnStub(filter, 'isElementEnrolled');
+ });
+
+ it("checks whether the feature has scenarios or not", function () {
+ assembler.isSuggestedFeatureEnrollable();
+ expect(feature.hasScenarios).toHaveBeenCalled();
+ });
+
+ describe("when the feature has scenarios", function () {
+ beforeEach(function () {
+ feature.hasScenarios.andReturn(true);
+ });
+
+ it("is truthy", function () {
+ var enrollable = assembler.isSuggestedFeatureEnrollable();
+ expect(enrollable).toBeTruthy();
+ });
+ });
+
+ describe("when the feature has got no scenarios", function () {
+ beforeEach(function () {
+ feature.hasScenarios.andReturn(false);
+ });
+
+ it("asks the filter is the feature should be enrolled", function () {
+ assembler.isSuggestedFeatureEnrollable();
+ expect(filter.isElementEnrolled).toHaveBeenCalledWith(feature);
+ });
+
+ it("is truthy when the filter tells the feature should be enrolled", function () {
+ filter.isElementEnrolled.andReturn(true);
+ var enrollable = assembler.isSuggestedFeatureEnrollable();
+ expect(enrollable).toBeTruthy();
+ });
+
+ it("is falsy when the filter tells the feature should be enrolled", function () {
+ filter.isElementEnrolled.andReturn(false);
+ var enrollable = assembler.isSuggestedFeatureEnrollable();
+ expect(enrollable).toBeFalsy();
+ });
+ });
+ });
+ });
+
+ describe("tryEnrollingSuggestedFeature()", function () {
+ beforeEach(function () {
+ spyOn(assembler, 'isSuggestedFeatureEnrollable');
+ spyOn(assembler, 'enrolSuggestedFeature');
+ });
+
+ it("checks whether the possible suggested feature is enrollable", function () {
+ assembler.tryEnrollingSuggestedFeature();
+ expect(assembler.isSuggestedFeatureEnrollable).toHaveBeenCalled();
+ });
+
+ describe("when the suggested feature is enrollable", function () {
+ beforeEach(function () {
+ assembler.isSuggestedFeatureEnrollable.andReturn(true);
+ });
+
+ it("enrols the suggested feature", function () {
+ assembler.tryEnrollingSuggestedFeature();
+ expect(assembler.enrolSuggestedFeature).toHaveBeenCalled();
+ });
+ });
+
+ describe("when the suggested feature is not enrollable (or there is no suggested feature yet)", function () {
+ beforeEach(function () {
+ assembler.isSuggestedFeatureEnrollable.andReturn(false);
+ });
+
+ it("enrols the suggested feature", function () {
+ assembler.tryEnrollingSuggestedFeature();
+ expect(assembler.enrolSuggestedFeature).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe("enrolSuggestedFeature", function () {
+ var feature;
+
+ beforeEach(function () {
+ feature = createSpyWithStubs("suggested feature", {hasScenarios: true});
+ spyOnStub(features, 'addFeature');
+ assembler.suggestFeature(feature);
+ expect(assembler.isSuggestedFeatureEnrollable()).toBeTruthy();
+ });
+
+ it("adds the feature to the root features", function () {
+ assembler.enrolSuggestedFeature();
+ expect(features.addFeature).toHaveBeenCalledWith(feature);
+ });
+
+ it("removes the suggested feature", function () {
+ assembler.enrolSuggestedFeature();
+ expect(assembler.isSuggestedFeatureEnrollable()).toBeFalsy();
+ });
+ });
});
Oops, something went wrong.

0 comments on commit f8e4845

Please sign in to comment.