cukedep 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +15 -0
  2. data/.rspec +1 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.simplecov +7 -0
  6. data/.travis.yml +15 -0
  7. data/.yardopts +6 -0
  8. data/CHANGELOG.md +3 -0
  9. data/Gemfile +12 -0
  10. data/LICENSE.txt +19 -0
  11. data/README.md +64 -0
  12. data/Rakefile +32 -0
  13. data/bin/cukedep +14 -0
  14. data/cucumber.yml +10 -0
  15. data/lib/cukedep.rb +9 -0
  16. data/lib/cukedep/application.rb +112 -0
  17. data/lib/cukedep/cli/application.rb +0 -0
  18. data/lib/cukedep/cli/cmd-line.rb +115 -0
  19. data/lib/cukedep/config.rb +31 -0
  20. data/lib/cukedep/constants.rb +29 -0
  21. data/lib/cukedep/feature-model.rb +285 -0
  22. data/lib/cukedep/feature-rep.rb +49 -0
  23. data/lib/cukedep/gherkin-listener.rb +98 -0
  24. data/lib/macros4cuke.rb +8 -0
  25. data/sample/features/step_definitions/steps.rb +105 -0
  26. data/sample/features/support/env.rb +12 -0
  27. data/sample/model/model.rb +216 -0
  28. data/spec/cukedep/application_spec.rb +81 -0
  29. data/spec/cukedep/cli/cmd-line_spec.rb +91 -0
  30. data/spec/cukedep/feature-model_spec.rb +103 -0
  31. data/spec/cukedep/feature-rep_spec.rb +53 -0
  32. data/spec/cukedep/file-parsing.rb +40 -0
  33. data/spec/cukedep/gherkin-listener_spec.rb +59 -0
  34. data/spec/cukedep/sample_features/a_few_tests.feature +24 -0
  35. data/spec/cukedep/sample_features/more_tests.feature +24 -0
  36. data/spec/cukedep/sample_features/other_tests.feature +15 -0
  37. data/spec/cukedep/sample_features/some_tests.feature +13 -0
  38. data/spec/cukedep/sample_features/standalone.feature +19 -0
  39. data/spec/cukedep/sample_features/still_other_tests.feature +24 -0
  40. data/spec/cukedep/sample_features/yet_other_tests.feature +19 -0
  41. data/spec/spec_helper.rb +18 -0
  42. data/templates/rake.erb +163 -0
  43. metadata +165 -0
@@ -0,0 +1,103 @@
1
+ # File: feature-model_spec.rb
2
+
3
+ require_relative '../spec_helper'
4
+
5
+
6
+ require_relative 'file-parsing'
7
+ require_relative '../../lib/cukedep/gherkin-listener'
8
+
9
+ # Load the class under testing
10
+ require_relative '../../lib/cukedep/feature-model'
11
+
12
+ module Cukedep # Open module to get rid of long qualified names
13
+
14
+ describe FeatureModel do
15
+ # An array of FeatureFile objects created after parsing sample files.
16
+ FeatureFiles = begin
17
+ listener = GherkinListener.new
18
+ self.extend(FileParsing)
19
+ parse_for(listener) # Use mixin module to parse of sample feature files
20
+ listener.feature_files
21
+ end
22
+
23
+ # Default instantiation rule
24
+ subject { FeatureModel.new(FeatureFiles) }
25
+
26
+ context 'Creation and initialization:' do
27
+ it 'should be created with a list of feature files' do
28
+ expect { FeatureModel.new(FeatureFiles) }.not_to raise_error
29
+ end
30
+
31
+ it 'should know its feature file objects' do
32
+ expect(subject.feature_files).to eq(FeatureFiles)
33
+ end
34
+
35
+ end # context
36
+
37
+ context 'Provided services:' do
38
+ it 'should list all features without identifiers' do
39
+ unidentified = subject.anonymous_features
40
+ expect(unidentified).to have(1).items
41
+ expect(unidentified[0].filepath).to match(/standalone\.feature$/)
42
+ end
43
+
44
+ it 'should retrieve a feature file given its identifier' do
45
+ # Case of a single id argument
46
+ one_id = 'foo'
47
+ found = subject.select_by_ids(one_id)
48
+ expect(found).to have(1).items
49
+ expect(found[0].feature.identifier).to eq(one_id)
50
+
51
+ # Case of multiple id arguments
52
+ second_id = 'qux'
53
+ found = subject.select_by_ids(one_id, second_id)
54
+ expect(found).to have(2).items
55
+ actual_ids = found.map {|ff| ff.feature.identifier}
56
+ expected_ids = [one_id, second_id]
57
+ expect(actual_ids.sort).to eq(expected_ids.sort)
58
+
59
+ # Case of unknown id
60
+ wrong_id = 'does_not_exist'
61
+ err_type = StandardError
62
+ err_msg = "No feature file with identifier 'does_not_exist'."
63
+ expect { subject.select_by_ids(wrong_id) }.to raise_error(err_type, err_msg)
64
+ end
65
+
66
+
67
+ it 'should resolve dependency links' do
68
+ mapping = subject.send(:id2features)
69
+ deps = subject.dependency_links()
70
+ expect(deps).not_to be_empty
71
+
72
+ # Case of an identified feature without dependencies
73
+ case1 = deps.find {|a_dep| a_dep.dependee == mapping['baz']}
74
+ expect(case1.dependents).to be_empty
75
+
76
+ # Case of a feature having dependencies
77
+ case2 = deps.find {|a_dep| a_dep.dependee == mapping['foo']}
78
+ expectations = subject.select_by_ids('bar', 'qux')
79
+ expect(case2.dependents).to eq(expectations)
80
+ end
81
+
82
+ it 'should sort the feature files' do
83
+ sorted_files = subject.sort_features_by_dep()
84
+ actual_order = sorted_files.map {|f| f.feature.identifier}
85
+ expected_order = %w[qux baz bar foo]
86
+ end
87
+
88
+ it 'should generate mapping reports' do
89
+ subject.mapping_reports('feature2id.csv', 'id2feature.csv')
90
+ end
91
+
92
+ it 'should generate dependency graph' do
93
+ subject.dependency_links()
94
+ subject.draw_dependency_graph('dependencies.dot')
95
+ end
96
+
97
+ end # context
98
+
99
+ end # describe
100
+
101
+ end # module
102
+
103
+ # End of file
@@ -0,0 +1,53 @@
1
+ # File: feature-rep_spec.rb
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ # Load the class under testing
6
+ require_relative '../../lib/cukedep/feature-rep'
7
+
8
+ module Cukedep # Open module to get rid of long qualified names
9
+
10
+ describe FeatureRep do
11
+ # Tag names as provided by Gherkin parser
12
+ RawTagNames = %w[@some @feature:CO801_foo @depends_on:CO201_bar @depends_on:CO001_qux]
13
+
14
+ SampleTagNames = RawTagNames.map { |t| t[1..-1] }
15
+
16
+ subject { FeatureRep.new(RawTagNames) }
17
+
18
+
19
+ context 'Creation and initialization:' do
20
+ it 'could be created with an empty list of tags' do
21
+ expect {FeatureRep.new([]) }.not_to raise_error
22
+ end
23
+
24
+ it 'could be created with a list of tag names' do
25
+ expect { FeatureRep.new(RawTagNames) }.not_to raise_error
26
+ end
27
+
28
+ it 'should know its tags' do
29
+ expect(subject.tags).to eq(SampleTagNames)
30
+ end
31
+
32
+ it 'should know its identifier' do
33
+ expect(subject.identifier).to eq('CO801_foo')
34
+ end
35
+
36
+ it 'should know whether is has an indentifier' do
37
+ expect(subject).not_to be_anonymous
38
+ end
39
+ end # context
40
+
41
+ context 'Provided services:' do
42
+ it 'should know the feature files it depends on' do
43
+ expected_values = %w[CO201_bar CO001_qux]
44
+
45
+ expect(subject.dependency_tags.sort).to eq(expected_values.sort)
46
+ end
47
+ end # context
48
+
49
+ end # describe
50
+
51
+ end # module
52
+
53
+ # End of file
@@ -0,0 +1,40 @@
1
+ # File: file-parsing.rb
2
+
3
+ require 'gherkin/parser/parser'
4
+
5
+ module Cukedep # This module is used as a namespace
6
+
7
+ # Mixin module used in tests.
8
+ # Purpose: to parse a sample of feature files.
9
+ module FileParsing
10
+ # The list of sample feature file names.
11
+ SampleFileNames = [ "a_few_tests.feature",
12
+ "some_tests.feature",
13
+ "still_other_tests.feature",
14
+ "yet_other_tests.feature",
15
+ "more_tests.feature",
16
+ "standalone.feature"
17
+ ]
18
+
19
+ # Helper method. It parses sample feature files and
20
+ # notifies the provided listener of its progress.
21
+ def parse_for(aListener)
22
+ # Determine the folder where the sample files reside
23
+ my_dir = File.dirname(__FILE__)
24
+ sample_dir = File.expand_path(my_dir + '/sample_features')
25
+
26
+ # Create a Gherkin parser
27
+ parser = Gherkin::Parser::Parser.new(aListener)
28
+
29
+ # Let it parse the requested files
30
+ SampleFileNames.each do |sample|
31
+ path = sample_dir + '/' + sample
32
+ File::open(path, 'r') { |f| parser.parse(f.read, path, 0) }
33
+ end
34
+ end
35
+
36
+ end # module
37
+
38
+ end # module
39
+
40
+ # End of file
@@ -0,0 +1,59 @@
1
+ # File: gherkin-listener_spec.rb
2
+
3
+ require_relative '../spec_helper'
4
+
5
+
6
+ require_relative 'file-parsing'
7
+
8
+ # Load the class under testing
9
+ require_relative '../../lib/cukedep/gherkin-listener'
10
+
11
+ module Cukedep # Open module to get rid of long qualified names
12
+
13
+ describe GherkinListener do
14
+ include FileParsing # Use mixin module to parse of sample feature files
15
+
16
+ subject { GherkinListener.new }
17
+
18
+ context 'Creation and initialization:' do
19
+ it 'should be created without argument' do
20
+ expect {GherkinListener.new }.not_to raise_error
21
+ end
22
+
23
+ it 'should have no feature file at start' do
24
+ expect(subject).to have(0).feature_files
25
+ end
26
+ end # context
27
+
28
+ context 'Provided services:' do
29
+ it 'should build a FeatureFileRep per parsed file' do
30
+ parse_for(subject)
31
+ expect(subject).to have(FileParsing::SampleFileNames.size).feature_files
32
+ end
33
+
34
+ it 'should know the tags of each feature' do
35
+ parse_for(subject)
36
+
37
+ expectations = [
38
+ %w[a_few feature:qux],
39
+ %w[some feature:foo depends_on:bar depends_on:qux],
40
+ %w[still_other feature:baz],
41
+ %w[yet_other feature:bar depends_on:baz depends_on:qux depends_on:quux],
42
+ %w[feature:quux more],
43
+ []
44
+ ]
45
+
46
+ # Sort the expectations to ease further comparison
47
+ expectations.map! { |tags| tags.sort }
48
+
49
+ subject.feature_files.each_with_index do |file, i|
50
+ expect(file.feature.tags.sort).to eq(expectations[i])
51
+ end
52
+ end
53
+ end # context
54
+
55
+ end # describe
56
+
57
+ end # module
58
+
59
+ # End of file
@@ -0,0 +1,24 @@
1
+
2
+ @a_few @feature:qux
3
+ Feature: Registering user credentials
4
+ As a video rental employee
5
+ I want to enter my credentials
6
+ So I can identify myself when use the rental application
7
+
8
+ Scenario: Start without credentials
9
+ Given there is no registered user
10
+ When I enter the credentials "it's me"
11
+ Then I should not be authorized
12
+
13
+
14
+ Scenario Outline: Registering some credentials
15
+ When I register my credentials "<credentials>"
16
+ When I enter the credentials "<credentials>"
17
+ Then I should see a welcome message
18
+
19
+ Examples:
20
+ |credentials|
21
+ |it's me|
22
+ |himself|
23
+ |nemo|
24
+
@@ -0,0 +1,24 @@
1
+ # Notice how we specify the identifier of this feature file (quux)
2
+ @more @feature:quux
3
+ Feature: Subscribing member
4
+ As a video rental owner
5
+ I want to register future customers
6
+ So I can rent videos to authorized people
7
+
8
+ Scenario: Start with an empty membership
9
+ Given there is no member yet
10
+ Then I should see member "John Doe" as unknown
11
+
12
+
13
+ Scenario Outline: Adding videos to the catalogue
14
+ Given I subscribe "<name>"
15
+ Then I should see member "<name>" as registered
16
+
17
+ Examples:
18
+ |name|
19
+ |John Doe|
20
+ |Jed Eye|
21
+ |Bill Bo|
22
+ |Jack Orjones|
23
+ |Mary Gherkin|
24
+ |Waldo Cuke|
@@ -0,0 +1,15 @@
1
+ # Notice how dependencies between feature files are specified
2
+ @other @feature:corge @depends_on:foo
3
+ Feature: Deleting videos
4
+ As a video rental employee
5
+ I want to remove old/damaged videos
6
+ So I keep the quality of videos in the shelves
7
+
8
+ Scenario Outline: Renting videos to a member
9
+ When I remove the video "<title>"
10
+ Then I should see the video "<title>" as unknown
11
+
12
+ Examples:
13
+ |title|
14
+ |The world of Cucumber|
15
+ |Cucumber vs. Gherkin|
@@ -0,0 +1,13 @@
1
+
2
+ @some @feature:foo @depends_on:bar @depends_on:qux
3
+ Feature: Check-in
4
+ As a video rental employee
5
+ I want to register return of rented videos
6
+ So that other members can them too
7
+
8
+
9
+ Scenario: Registering return of rented video
10
+ When I enter the credentials "it's me"
11
+ When I register the return of "The world of Cucumber" from "John Doe"
12
+ Then I should see the return confirmed
13
+
@@ -0,0 +1,19 @@
1
+ # This feature has no identifier and no explicit dependencies
2
+ Feature: Registering new videos
3
+ As a video rental owner
4
+ I want to add more videos to my catalogue
5
+ So I can offer newer videos for rental
6
+
7
+ Background:
8
+ Given the catalogue is empty
9
+
10
+ Scenario: Start with an empty catalogue
11
+ Then I should see the video "Othello" as unknown
12
+
13
+ # Let's do something
14
+ When I add the video "Othello" to the catalogue
15
+ Then I should see the video "Othello" as available
16
+
17
+ # Undo what we just did
18
+ When I remove the video "Othello"
19
+ Then I should see the video "Othello" as unknown
@@ -0,0 +1,24 @@
1
+ # Notice how we specify the identifier of this feature file (baz)
2
+ @still_other @feature:baz
3
+ Feature: Registering new videos
4
+ As a video rental owner
5
+ I want to add more videos to my catalogue
6
+ So I can offer newer videos for rental
7
+
8
+ Scenario: Start with an empty catalogue
9
+ Given the catalogue is empty
10
+ Then I should see the video "The world of Cucumber" as unknown
11
+
12
+
13
+ Scenario Outline: Adding videos to the catalogue
14
+ Given I add the video "<title>" to the catalogue
15
+ Then I should see the video "<title>" as available
16
+
17
+ Examples:
18
+ |title|
19
+ | The world of Cucumber |
20
+ | The empire of Cucumber |
21
+ | The revenge of the Cucumber |
22
+ | The peeled Cucumber |
23
+ | The masked Cucumber|
24
+ | Cucumber vs. Gherkin|
@@ -0,0 +1,19 @@
1
+
2
+ # Notice how dependencies between feature files are specified
3
+ @yet_other @feature:bar @depends_on:baz @depends_on:qux @depends_on:quux
4
+ Feature: Renting videos
5
+ As a video rental employee
6
+ I want to register rentals made by a member
7
+ So I can run my business
8
+
9
+ Scenario Outline: Renting videos to a member
10
+ When I enter the credentials "it's me"
11
+ When I register the rental of "<title>" for "John Doe"
12
+ Then I should see the rental confirmed
13
+
14
+ Examples:
15
+ |title|
16
+ |The world of Cucumber|
17
+ |The masked Cucumber|
18
+
19
+
@@ -0,0 +1,18 @@
1
+ # File: spec_helper.rb
2
+ # Purpose: utility file that is loaded by all our RSpec files
3
+
4
+ require 'simplecov' # Use SimpleCov for test coverage measurement
5
+
6
+
7
+ require 'rspec' # Use the RSpec framework
8
+ require 'pp' # Use pretty-print for debugging purposes
9
+
10
+ RSpec.configure do |config|
11
+ config.expect_with :rspec do |c|
12
+ # Disable the `should` syntax...
13
+ c.syntax = :expect
14
+ end
15
+ end
16
+
17
+
18
+ # End of file
@@ -0,0 +1,163 @@
1
+ # File: <%=rakefile%>
2
+ # Generated by Cukedep <%=Cukedep::Version%>
3
+
4
+ require 'rake'
5
+ require 'pp'
6
+
7
+ # Run Cucumber via specialized Rake task
8
+ require 'cucumber/rake/task'
9
+ # UGLY workaround for bug in Cucumber's rake task
10
+ if Gem::VERSION[0].to_i >= 2 && Cucumber::VERSION <= '1.3.2'
11
+ # Monkey-patch a buggy method
12
+ class Cucumber::Rake::Task::ForkedCucumberRunner
13
+ def gem_available?(gemname)
14
+ if Gem::VERSION[0].to_i >= 2
15
+ gem_available_new_rubygems?(gemname)
16
+ else
17
+ gem_available_old_rubygems?(gemname)
18
+ end
19
+ end
20
+ end # class
21
+ end
22
+
23
+ # Create a task called cucumber
24
+ Cucumber::Rake::Task.new do |t|
25
+ end
26
+
27
+
28
+ # Constant holding the location of the original feature files
29
+ SOURCE_DIR = '<%=source_dir%>'
30
+
31
+ # Constant holding the location of the project
32
+ CUKE_PROJECT = '<%=proj_dir%>'
33
+
34
+ # Helper method. Calculate the filepath of a file with given name
35
+ # if it is located in the source dir.
36
+ def source_path_of(aFilename)
37
+ SOURCE_DIR + '/' + aFilename
38
+ end
39
+
40
+ # Helper method. Calculate the filepath of a file with given name
41
+ # if it is located in the project featurers dir.
42
+ def project_path_of(aFilename)
43
+ CUKE_PROJECT + '/features/' + aFilename
44
+ end
45
+
46
+ # Helper method. Given the name of files in the source dir, copy them
47
+ # into the project dir.
48
+ def copy_to_project(filenames)
49
+ filenames.each do |fname|
50
+ source_path = source_path_of(fname)
51
+ dest_path = project_path_of(fname)
52
+ FileUtils.copy_file(source_path, dest_path)
53
+ end
54
+ end
55
+
56
+
57
+ # Helper method. Given the name of files in the features dir of the project,
58
+ # copy them into the source dir.
59
+ def copy_from_project(*filenames)
60
+ filenames.each do |fname|
61
+ source_path = project_path_of(fname)
62
+ dest_path = source_path_of(fname)
63
+ FileUtils.copy_file(source_path, dest_path)
64
+ end
65
+ end
66
+
67
+ def del_from_project(filenames)
68
+ filenames.each do |fname|
69
+ dest_path = project_path_of(fname)
70
+ FileUtils.remove_file(dest_path)
71
+ end
72
+ end
73
+
74
+ # Helper method. Delete all the files in the
75
+ # the project's features dir that match one of
76
+ # the file name pattern
77
+ def clean_project_dir(fname_patterns)
78
+ curr_dir = Dir.getwd()
79
+ begin
80
+ Dir.chdir(CUKE_PROJECT + '/features')
81
+ fname_patterns.each do |patt|
82
+ filenames = Dir.glob(patt)
83
+ pp filenames
84
+ filenames.each { |fn| FileUtils.remove_file(fn) }
85
+ end
86
+ ensure #Always restore previous working dir.
87
+ Dir.chdir(curr_dir)
88
+ end
89
+ end
90
+
91
+
92
+ def process_files(filenames)
93
+ copy_to_project(filenames)
94
+ curr_dir = Dir.getwd()
95
+ Dir.chdir(CUKE_PROJECT)
96
+ begin
97
+ do_cuke = Rake::Task[:cucumber]
98
+ do_cuke.reenable
99
+ do_cuke.invoke
100
+ ensure
101
+ Dir.chdir(curr_dir)
102
+ end
103
+ del_from_project(filenames)
104
+ end
105
+
106
+ def process_a_feature(filename)
107
+ process_files([filename])
108
+ end
109
+
110
+
111
+ AllUnidentifiedFeatures = [<%anonymous_list = anonymous.map { |name| "'#{name}'"}%>
112
+ <%=anonymous_list.join(",\n ")%>
113
+ ]
114
+
115
+
116
+ AllFeatureIdentifiers = [ <%identifier_list = feature_ids.map {|f_id| ":#{f_id}"}%>
117
+ <%=identifier_list.join(",\n ")%>
118
+ ]
119
+
120
+ # Default rake task is also top-level task
121
+ task :default => :run_all_features
122
+
123
+
124
+ desc 'Run all features'
125
+ task :run_all_features => [:before_all, :all_features, :after_all] do
126
+ ; # Do nothing
127
+ end
128
+
129
+
130
+ # Remove feature files from a cuke project
131
+ task :dir_cleanup do
132
+ clean_project_dir(['*.feature'])
133
+ end
134
+
135
+
136
+
137
+ desc 'Before all'
138
+ task :before_all => [:dir_cleanup] do
139
+ ; # Do nothing
140
+ end
141
+
142
+ desc 'After_all'
143
+ task :after_all do
144
+ ; # Do nothing
145
+ end
146
+
147
+
148
+ task :unidentified do
149
+ process_files(AllUnidentifiedFeatures)
150
+ end
151
+
152
+ # Tasks for feature files involved in dependencies
153
+ <%deps.each do |dep|%>
154
+ <%dep_list = dep.dependents.map { |ff| ":#{ff.feature.identifier}" } %>
155
+ task :<%=dep.dependee.feature.identifier%> <%= dep_list.empty? ? '' : %Q|=> [#{dep_list.join(', ') }]| %> do
156
+ process_a_feature('<%=dep.dependee.basename%>')
157
+ end
158
+ <%end%>
159
+
160
+ task :all_features => ([:unidentified] + AllFeatureIdentifiers) do
161
+ end
162
+
163
+ # End of file