featuremap 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/bin/featuremap +4 -3
- data/features/give_feedback.feature +75 -0
- data/features/show features and subdirs/show-features.feature +42 -0
- data/features/show features and subdirs/show-scenarios.feature +30 -0
- data/features/show features and subdirs/show-subdirs.feature +58 -0
- data/features/step_definitions/common_steps.rb +44 -0
- data/features/step_definitions/give_feedback_steps.rb +69 -0
- data/features/step_definitions/show_features_steps.rb +57 -0
- data/features/step_definitions/show_scenarios_steps.rb +32 -0
- data/features/step_definitions/show_subdirs_steps.rb +61 -0
- data/features/support/includes.rb +6 -0
- data/features/support/testdata_gen.rb +68 -0
- data/features/support/validate_xml.rb +13 -0
- data/lib/featuremap.rb +68 -43
- data/lib/version.rb +3 -0
- metadata +28 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3e9b97e4894658a5d3d8f8dea733013c28c2a74e
|
|
4
|
+
data.tar.gz: 453f5df89b88b09fd3b6e67f50781a6044f12210
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4b2b50674749818acee3d3f6ad6d3774f9afd6872ca005584a67f2941a030d8631c08c19c4d9a78cfadd53890fb06be525541b72de996cac30c265ca8cc643c
|
|
7
|
+
data.tar.gz: 14436d3a64bbf81b951c374fb9baf82b88282f6b2c04ad17747f05be34e53b3f4c3a8a2f21bc07724548d2eb4f475f0043f9cebc55a21b4e5e91d5ceb9a4689f
|
data/bin/featuremap
CHANGED
|
@@ -28,8 +28,9 @@ else
|
|
|
28
28
|
else
|
|
29
29
|
output_file = "STDOUT"
|
|
30
30
|
end
|
|
31
|
-
featuremap = Featuremap.new(feature_dir,options[:verbose])
|
|
32
|
-
featuremap.
|
|
33
|
-
|
|
31
|
+
featuremap = Featuremap.new(feature_dir, output_file, options[:verbose])
|
|
32
|
+
if featuremap.exit_status == 0
|
|
33
|
+
featuremap.create_featuremap()
|
|
34
|
+
end
|
|
34
35
|
exit featuremap.exit_status
|
|
35
36
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
|
|
2
|
+
Ability: give feedback
|
|
3
|
+
To run featuremap the user have to provide certain parameters (e.g. the location
|
|
4
|
+
of the Gherkn feature files). If those parameters are missing or faulty,
|
|
5
|
+
featuremap will provide helpful feedback.
|
|
6
|
+
|
|
7
|
+
# rule: show an error message if the feature dir is not accessible
|
|
8
|
+
|
|
9
|
+
Scenario: feature dir does not exist
|
|
10
|
+
Given "invalid-path" as a non existing location for the feature dir
|
|
11
|
+
When the user runs featuremap
|
|
12
|
+
Then featuremap exits with 66
|
|
13
|
+
And featuremap shows the message "can't find >>invalid-path<< as feature dir"
|
|
14
|
+
|
|
15
|
+
Scenario: access rights are not sufficient to read the feature dir
|
|
16
|
+
Given a feature dir "secret_features"
|
|
17
|
+
And the user rights for the feature dir don't allow access
|
|
18
|
+
When the user runs featuremap
|
|
19
|
+
Then featuremap exits with 66
|
|
20
|
+
And featuremap shows the message "can't access >>secret_features/<< as feature dir"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# rule: show an error message if the featuremap can't write the mindmap
|
|
24
|
+
|
|
25
|
+
Scenario: mindmap name contains an non-existing path
|
|
26
|
+
Given a feature dir "features"
|
|
27
|
+
And "invalid-path/featuremap.mm" as a non existing location for the mindmap
|
|
28
|
+
When the user runs featuremap
|
|
29
|
+
Then featuremap exits with 74
|
|
30
|
+
And featuremap shows the message "can't write to invalid-path/featuremap.mm"
|
|
31
|
+
|
|
32
|
+
Scenario: access rights are not sufficient for the mindmap
|
|
33
|
+
Given a feature dir "features"
|
|
34
|
+
And "readonly-path/" as a read-only location for the mindmap
|
|
35
|
+
And the user rights for the mindmaps don't have access for writing
|
|
36
|
+
When the user runs featuremap
|
|
37
|
+
Then featuremap exits with 74
|
|
38
|
+
And featuremap shows the message "can't write to readonly-path/featuremap.mm"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# rule: add a number to the mindmaps name if the file already exists
|
|
42
|
+
|
|
43
|
+
Scenario: a file with the same name as the mindmap already exists
|
|
44
|
+
Given a mindmap file "featuremap.mm" already exists
|
|
45
|
+
And a feature dir "existing_name_features"
|
|
46
|
+
And it contains a feature
|
|
47
|
+
And "featuremap.mm" is used as an argument for the featuremap script
|
|
48
|
+
When the user runs featuremap
|
|
49
|
+
Then featuremap shows the message "given mindmap name is already in use, created featuremap-1.mm"
|
|
50
|
+
And a new mindmap with name "featuremap-1.mm" was created
|
|
51
|
+
|
|
52
|
+
Scenario: multiple files with the same name as the mindmap already exists
|
|
53
|
+
Given 6 multiple mindmap file with the name "featuremap" distinguished only by number exist
|
|
54
|
+
And a feature dir "existing_name_features"
|
|
55
|
+
And it contains a feature
|
|
56
|
+
And "featuremap.mm" is used as an argument for the featuremap script
|
|
57
|
+
When the user runs featuremap
|
|
58
|
+
Then featuremap shows the message "given mindmap name is already in use, created featuremap-6.mm"
|
|
59
|
+
And a new mindmap with name "featuremap-6.mm" was created
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# rule: if the result is written on stdout messages should appear on stderr
|
|
63
|
+
|
|
64
|
+
Scenario: user does omit the name for the feature file
|
|
65
|
+
Given a feature dir "features"
|
|
66
|
+
And it contains a feature
|
|
67
|
+
And the argument for the mindmap file name is missing
|
|
68
|
+
When the user runs featuremap
|
|
69
|
+
Then the content of the mindmap file is redirected to stdout
|
|
70
|
+
|
|
71
|
+
Scenario: user does omit the name for the feature file and the feature dir does not exists
|
|
72
|
+
Given "invalid-path" as a non existing location for the feature dir
|
|
73
|
+
And the argument for the mindmap file name is missing
|
|
74
|
+
When the user runs featuremap
|
|
75
|
+
Then featuremap shows the message "can't find >>invalid-path<< as feature dir" on stderr
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Ability: show features
|
|
2
|
+
Calling the mapper will result in a new freemind mindmap. The mindmap will
|
|
3
|
+
show all feature files as separate nodes.
|
|
4
|
+
|
|
5
|
+
# rule: every feature file is shown as a node in the mindmap^
|
|
6
|
+
|
|
7
|
+
Scenario Outline: show features
|
|
8
|
+
Given a feature dir is <feature_dir_type>
|
|
9
|
+
And it contains <nr_of_features>
|
|
10
|
+
When the mapper is called
|
|
11
|
+
Then a mindmap file without any validation error is created
|
|
12
|
+
And the mindmap contains a root node named "featuremap"
|
|
13
|
+
And the mindmap contains <nr_of_features> nodes
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
|nr_of_features|feature_dir_type|
|
|
17
|
+
|0 |empty |
|
|
18
|
+
|1 |single |
|
|
19
|
+
|3 |multiple |
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# rule: other files than .feature files are ignored
|
|
23
|
+
|
|
24
|
+
Scenario: ignore other file types
|
|
25
|
+
Given a feature dir "mixed_files"
|
|
26
|
+
And it contains <nr_of_files> files of <file_type>
|
|
27
|
+
|nr_of_files|file_type|
|
|
28
|
+
|3 |feature |
|
|
29
|
+
|1 |txt |
|
|
30
|
+
|1 |csv |
|
|
31
|
+
When the mapper is called
|
|
32
|
+
Then a mindmap file without any validation error is created
|
|
33
|
+
And the mindmap contains only 3 feature nodes
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# rule: feature names are using a bold font
|
|
37
|
+
|
|
38
|
+
Scenario: format feature names in bold
|
|
39
|
+
Given a feature dir containing a feature file
|
|
40
|
+
When the mapper is called
|
|
41
|
+
Then a mindmap file without any validation error is created
|
|
42
|
+
And the feature node in this mindmap is using a bold font
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Ability: show scenarios
|
|
2
|
+
Calling the mapper will result in a new freemind mindmap. The mindmap will
|
|
3
|
+
show all feature files as separate nodes. Every feature node will contain
|
|
4
|
+
sub-nodes for all scenarios.
|
|
5
|
+
|
|
6
|
+
# rule: if a feature has scenario, scenario name will be shown as subnodes to
|
|
7
|
+
|
|
8
|
+
Scenario Outline: show plain scenarios
|
|
9
|
+
Given a feature dir with a feature containing <nr_of_scenarios> scenarios
|
|
10
|
+
When the mapper is called
|
|
11
|
+
Then a mindmap file without any validation error is created
|
|
12
|
+
And the mindmap contains a feature node
|
|
13
|
+
And the feature node contains <nr_of_scenarios> scenario subnodes
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
|nr_of_scenarios|
|
|
17
|
+
|0 |
|
|
18
|
+
|1 |
|
|
19
|
+
|3 |
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# rule: outline scenarios are marked by an list icon
|
|
23
|
+
|
|
24
|
+
Scenario: show outline scenario
|
|
25
|
+
Given a feature dir with a feature containing an outline scenario
|
|
26
|
+
When the mapper is called
|
|
27
|
+
Then a mindmap file without any validation error is created
|
|
28
|
+
And the mindmap contains a feature node
|
|
29
|
+
And the feature node contains a scenario subnode
|
|
30
|
+
And the scenario subnode is marked with an list icon
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
Ability: show subdirs
|
|
3
|
+
Calling the mapper will result in a new freemind mindmap. The mindmap will
|
|
4
|
+
show every subdir from the feature dir as a separate node and attach all
|
|
5
|
+
features from the subdir as children nodes.
|
|
6
|
+
|
|
7
|
+
# rule: turn subdirs into mindmap nodes
|
|
8
|
+
# - show subdirs as mindmap nodes
|
|
9
|
+
# - add a folder icon to mark them as subdirs
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Scenario: feature dir without subdirs
|
|
13
|
+
Given a feature dir "subdirs_none"
|
|
14
|
+
And it contains a feature file
|
|
15
|
+
When the mapper is called
|
|
16
|
+
Then a mindmap file without any validation error is created
|
|
17
|
+
And the mindmap contains a node with the feature name
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Scenario: feature dir with one level of subdirs
|
|
21
|
+
Given a feature dir "subdirs_one_level"
|
|
22
|
+
And it contains at least one subdir
|
|
23
|
+
And the subdir contains a feature file
|
|
24
|
+
When the mapper is called
|
|
25
|
+
Then a mindmap file without any validation error is created
|
|
26
|
+
And the mindmap contains a node with the subdir
|
|
27
|
+
And the subdir node contains a node with the feature
|
|
28
|
+
And the subdir node is marked by a folder icon
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Scenario: feature dir with multiple levels of subdirs
|
|
32
|
+
Given a feature dir "subdirs_multiple_levels"
|
|
33
|
+
And the feature dir contains subdirs with a different amount of features
|
|
34
|
+
|subdirs |nr_of_features|
|
|
35
|
+
|sub1/sub1_1 |1 |
|
|
36
|
+
|sub2 |2 |
|
|
37
|
+
|sub3/sub3_1/sub3_2 |2 |
|
|
38
|
+
|sub1/sub1_2 |0 |
|
|
39
|
+
When the mapper is called
|
|
40
|
+
Then a mindmap file without any validation error is created
|
|
41
|
+
And the mindmap shows nodes with a folder icon for every subdir
|
|
42
|
+
And the node of every subdir contains the corresponding number of feature nodes
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# rule: don't show subdirs used for code and configuration
|
|
46
|
+
|
|
47
|
+
Scenario: ignore step_definitions and support folders
|
|
48
|
+
Given a feature dir "subdirs_code_config"
|
|
49
|
+
And the feature dir contains subdirs with a different amount of features
|
|
50
|
+
|subdirs |nr_of_features|
|
|
51
|
+
|sub1/sub1_1 |1 |
|
|
52
|
+
|sub2 |2 |
|
|
53
|
+
|step_definitions |0 |
|
|
54
|
+
|support |0 |
|
|
55
|
+
When the mapper is called
|
|
56
|
+
Then a mindmap file without any validation error is created
|
|
57
|
+
And the minmap does not contain a folder node "step_definitions"
|
|
58
|
+
And the minmap does not contain a folder node "support"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Before do
|
|
2
|
+
@log = Logger.new(STDOUT)
|
|
3
|
+
@log.datetime_format = "%H:%M:%S"
|
|
4
|
+
if ENV['LOG_LEVEL'] == 'debug'
|
|
5
|
+
@log.level = Logger::DEBUG
|
|
6
|
+
elsif ENV['LOG_LEVEL'] == 'info'
|
|
7
|
+
@log.level = Logger::INFO
|
|
8
|
+
else
|
|
9
|
+
# default log level
|
|
10
|
+
@log.level = Logger::ERROR
|
|
11
|
+
end
|
|
12
|
+
@featuremap_file = nil
|
|
13
|
+
delete_path("test_data")
|
|
14
|
+
@path_to_results = "test_data/out"
|
|
15
|
+
@path_to_testdata = "test_data/in"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Given("a feature dir {string}") do |feature_dir|
|
|
19
|
+
@feature_dir = feature_dir
|
|
20
|
+
create_path("#{@path_to_testdata}/#{@feature_dir}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
When("the mapper is called") do
|
|
24
|
+
create_path(@path_to_results)
|
|
25
|
+
@featuremap_file = "#{@path_to_results}/featuremap.mm"
|
|
26
|
+
@mapper = Featuremap.new("#{@path_to_testdata}/#{@feature_dir}", @featuremap_file)
|
|
27
|
+
@mapper.create_featuremap()
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
When("the user runs featuremap") do
|
|
31
|
+
@log.debug "run script: bin/featuremap #{@path_to_testdata}/#{@feature_dir} #{@featuremap_file}"
|
|
32
|
+
if @featuremap_file
|
|
33
|
+
@stdout, @stderr, @exit_status = Open3.capture3("bin/featuremap", "#{@path_to_testdata}/#{@feature_dir}", @featuremap_file)
|
|
34
|
+
else
|
|
35
|
+
@stdout, @stderr, @exit_status = Open3.capture3("bin/featuremap", "#{@path_to_testdata}/#{@feature_dir}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Then("a mindmap file without any validation error is created") do
|
|
40
|
+
#validate generated mm file with freemind.xsd
|
|
41
|
+
#validate_mm returns array containing validation errors
|
|
42
|
+
expect(validate_mm(@featuremap_file).count).to eq(0)
|
|
43
|
+
@mindmap = Nokogiri::XML(File.read(@featuremap_file))
|
|
44
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Given("{string} as a non existing location for the feature dir") do |feature_dir|
|
|
2
|
+
@feature_dir = feature_dir
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
Given("the user rights for the feature dir don't allow access") do
|
|
6
|
+
FileUtils.chmod("a=xw", "#{@path_to_testdata}/#{@feature_dir}")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
Given("{string} as a non existing location for the mindmap") do |mindmap_file|
|
|
10
|
+
@featuremap_file = mindmap_file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Given("{string} as a read-only location for the mindmap") do |path_to_mindmap|
|
|
14
|
+
@featuremap_file = "#{path_to_mindmap}featuremap.mm"
|
|
15
|
+
@path_to_mindmap = path_to_mindmap
|
|
16
|
+
create_path("#{@path_to_results}/#{@path_to_mindmap}")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Given("the user rights for the mindmaps don't have access for writing") do
|
|
20
|
+
FileUtils.chmod("a=r", "#{@path_to_results}/#{@path_to_mindmap}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Given("a mindmap file {string} already exists") do |mindmap_file_name|
|
|
24
|
+
create_file(@path_to_results, mindmap_file_name, "")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Given("it contains a feature") do
|
|
28
|
+
create_feature("#{@path_to_testdata}/#{@feature_dir}", "dummy feature.feature")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Given("{string} is used as an argument for the featuremap script") do |mindmap_file_name|
|
|
32
|
+
@featuremap_file = "#{@path_to_results}/#{mindmap_file_name}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Given("{int} multiple mindmap file with the name {string} distinguished only by number exist") do |duplicate_count, mindmap_file_name|
|
|
36
|
+
create_file(@path_to_results, "#{mindmap_file_name}.mm", "")
|
|
37
|
+
(1..duplicate_count-1).each do |file_nr|
|
|
38
|
+
create_file(@path_to_results, "#{mindmap_file_name}-#{file_nr}.mm", "")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Given("the argument for the mindmap file name is missing") do
|
|
43
|
+
@featuremap_file = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Then("featuremap exits with {int}") do |exit_status|
|
|
47
|
+
expect(@exit_status.exitstatus).to eq(exit_status)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Then("featuremap shows the message {string}") do |err_msg|
|
|
51
|
+
script_output = @stdout + @stderr
|
|
52
|
+
script_output = script_output.sub("#{@path_to_testdata}/", "")
|
|
53
|
+
script_output = script_output.sub("#{@path_to_results}/", "")
|
|
54
|
+
expect(script_output).to include(err_msg)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Then("a new mindmap with name {string} was created") do |mindmap_file_name|
|
|
58
|
+
expect(File.exists?("#{@path_to_results}/#{mindmap_file_name}")).to be_truthy
|
|
59
|
+
expect(validate_mm("#{@path_to_results}/#{mindmap_file_name}").count).to eq(0)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Then("the content of the mindmap file is redirected to stdout") do
|
|
63
|
+
expect(validate_content(@stdout).count).to eq(0)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Then("featuremap shows the message {string} on stderr") do |warning_msg|
|
|
67
|
+
@stderr = @stderr.sub("#{@path_to_testdata}/", "")
|
|
68
|
+
expect(@stderr).to include(warning_msg)
|
|
69
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Given("a feature dir is empty") do
|
|
2
|
+
@path_to_testdata = "#{@path_to_testdata}/feature_dir_empty"
|
|
3
|
+
create_path(@path_to_testdata)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Given("a feature dir is single") do
|
|
7
|
+
@path_to_testdata = "#{@path_to_testdata}/feature_dir_single"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Given("a feature dir is multiple") do
|
|
11
|
+
@path_to_testdata = "#{@path_to_testdata}/feature_dir_multiple"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Given("a feature dir containing a feature file") do
|
|
15
|
+
@path_to_testdata = "#{@path_to_testdata}/feature_dir_simple"
|
|
16
|
+
create_feature(@path_to_testdata, "dummy feature.feature")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Given("it contains {int}") do |int|
|
|
20
|
+
for file_nr in 1..int
|
|
21
|
+
create_feature(@path_to_testdata, "dummy feature #{file_nr}.feature")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Given("it contains <nr_of_files> files of <file_type>") do |table|
|
|
26
|
+
@featuredir_setup = table.hashes
|
|
27
|
+
table.hashes.each do |table_row|
|
|
28
|
+
if table_row["file_type"] == "feature"
|
|
29
|
+
for file_nr in 1..table_row["nr_of_files"].to_i
|
|
30
|
+
create_feature("#{@path_to_testdata}/#{@feature_dir}", "dummy feature #{file_nr}.feature")
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
for file_nr in 1..table_row["nr_of_files"].to_i
|
|
34
|
+
create_other_file("#{@path_to_testdata}/#{@feature_dir}", "dummy no_feature #{file_nr}.#{table_row["file_type"]}")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Then("the mindmap contains a root node named {string}") do |string|
|
|
41
|
+
expect(@mindmap.xpath("/map/node/@TEXT").first.to_s).to match("featuremap")
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Then("the mindmap contains {int} nodes") do |int|
|
|
46
|
+
for file_nr in 1..int
|
|
47
|
+
expect(@mindmap.xpath("/map/node/node").count).to eq(int)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
Then("the mindmap contains only {int} feature nodes") do |int|
|
|
52
|
+
expect(@mindmap.xpath("//node[starts-with(@ID, 'feature_')]").count).to eq(int)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Then("the feature node in this mindmap is using a bold font") do
|
|
56
|
+
expect(@mindmap.xpath("//node[starts-with(@ID, 'feature_')]/font[@BOLD = 'true']").count).to eq(1)
|
|
57
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Given("a feature dir with a feature containing {int} scenarios") do |nr_of_scenarios|
|
|
2
|
+
@path_to_testdata = "#{@path_to_testdata}/scenarios_basic"
|
|
3
|
+
create_path(@path_to_testdata)
|
|
4
|
+
scenarios = []
|
|
5
|
+
for scenario_index in 1..nr_of_scenarios
|
|
6
|
+
scenarios.unshift(["scenario nr #{scenario_index}", "Scenario"])
|
|
7
|
+
end
|
|
8
|
+
create_feature(@path_to_testdata, "dummy feature with scenarios.feature", scenarios)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Given("a feature dir with a feature containing an outline scenario") do
|
|
12
|
+
@path_to_testdata = "#{@path_to_testdata}/scenarios_outline"
|
|
13
|
+
create_path(@path_to_testdata)
|
|
14
|
+
scenarios = [['sample outline', 'Scenario Outline']]
|
|
15
|
+
create_feature(@path_to_testdata, "dummy feature with scenarios.feature", scenarios)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Then("the mindmap contains a feature node") do
|
|
19
|
+
expect(@mindmap.xpath("/map/node/node[starts-with(@ID,'feature_')]").count).to eq(1)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Then("the feature node contains {int} scenario subnodes") do |nr_of_subnodes|
|
|
23
|
+
expect(@mindmap.xpath("/map/node/node[starts-with(@ID,'feature_')]/node[starts-with(@ID,'scenario_')]").count).to eq(nr_of_subnodes)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Then("the feature node contains a scenario subnode") do
|
|
27
|
+
expect(@mindmap.xpath("/map/node/node[starts-with(@ID,'feature_')]/node[starts-with(@ID,'scenario_')]").count).to eq(1)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Then("the scenario subnode is marked with an list icon") do
|
|
31
|
+
expect(@mindmap.xpath("//node[starts-with(@ID,'scenario_')]/icon[@BUILTIN = 'list']").count).to eq(1)
|
|
32
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Given("it contains at least one subdir") do
|
|
2
|
+
create_path("#{@path_to_testdata}/#{@feature_dir}/subdir")
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
Given("it contains a feature file") do
|
|
6
|
+
create_feature("#{@path_to_testdata}/#{@feature_dir}", "dummy.feature")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
Given("the subdir contains a feature file") do
|
|
10
|
+
create_feature("#{@path_to_testdata}/#{@feature_dir}/subdir", "subdir.feature")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Given("the feature dir contains subdirs with a different amount of features") do |table|
|
|
14
|
+
@subdir_setup = table.hashes
|
|
15
|
+
table.hashes.each do |table_row|
|
|
16
|
+
subdir_path = "#{@path_to_testdata}/#{@feature_dir}/#{table_row["subdirs"]}"
|
|
17
|
+
create_path(subdir_path)
|
|
18
|
+
for feature_count in 1..table_row["nr_of_features"].to_i
|
|
19
|
+
create_feature(subdir_path, "dummy_#{feature_count}.feature")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Then("the mindmap contains a node with the feature name") do
|
|
25
|
+
expect(@mindmap.xpath("/map/node/node/@TEXT").first.to_s).to match("dummy feature for testing")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Then("the mindmap contains a node with the subdir") do
|
|
29
|
+
expect(@mindmap.xpath("/map/node/node/@TEXT").first.to_s).to match("subdir")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Then("the subdir node contains a node with the feature") do
|
|
33
|
+
expect(@mindmap.xpath("/map/node/node[starts-with(@ID,'subdir_')]/node[starts-with(@ID,'feature_')]").count).to eq(1)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Then("the subdir node is marked by a folder icon") do
|
|
37
|
+
expect(@mindmap.xpath("/map/node/node/icon").count).to eq(1)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Then("the mindmap shows nodes with a folder icon for every subdir") do
|
|
41
|
+
@subdir_setup.each do |table_row|
|
|
42
|
+
subdir_path = table_row["subdirs"]
|
|
43
|
+
subdir_path.split("/").each do |subdir|
|
|
44
|
+
expect(@mindmap.xpath("//node[@TEXT = '#{subdir}']").count).to eq(1)
|
|
45
|
+
expect(@mindmap.xpath("//node[@TEXT = '#{subdir}']/icon").first.to_s).to match("<icon BUILTIN=\"folder\"/>")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Then("the node of every subdir contains the corresponding number of feature nodes") do
|
|
51
|
+
@subdir_setup.each do |table_row|
|
|
52
|
+
subdir_path = table_row["subdirs"]
|
|
53
|
+
subdir = subdir_path.split("/").pop
|
|
54
|
+
@log.debug "xpath: //node[@TEXT = '#{subdir}']/node[starts-with(@ID, 'feature_')]"
|
|
55
|
+
expect(@mindmap.xpath("//node[@TEXT = '#{subdir}']/node[starts-with(@ID, 'feature_')]").count).to eq(table_row["nr_of_features"].to_i)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Then("the minmap does not contain a folder node {string}") do |node_name|
|
|
60
|
+
expect(@mindmap.xpath("//node[starts-with(@ID, 'subdir_') and @TEXT = '#{node_name}']").count).to eq(0)
|
|
61
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'english' # this is for speaking built-in variable names e.g. $CHILD_STATUS instead of $?
|
|
3
|
+
require 'featuremap' # include the featuremap main class
|
|
4
|
+
require 'nokogiri' # include xml creation and validation lib
|
|
5
|
+
require 'fileutils' # for setting access rights to test_data files and dirs
|
|
6
|
+
require 'open3' # to read from stderr
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# generate feature files as test data
|
|
2
|
+
# path: location of tthe feature file
|
|
3
|
+
# name: name of the feature
|
|
4
|
+
def create_feature(path, name, scenarios = [])
|
|
5
|
+
|
|
6
|
+
feature = <<DUMMY_FEATURE
|
|
7
|
+
Feature: dummy feature for testing
|
|
8
|
+
This is a dummy just for testing purposes
|
|
9
|
+
DUMMY_FEATURE
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
scenarios.each do |scenario_name, scenario_type|
|
|
13
|
+
feature += <<DUMMY_SCENARIO
|
|
14
|
+
|
|
15
|
+
#{scenario_type}: #{scenario_name}
|
|
16
|
+
Given something
|
|
17
|
+
When action
|
|
18
|
+
Then result
|
|
19
|
+
|
|
20
|
+
DUMMY_SCENARIO
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
create_file(path, name, feature)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_other_file(path, name)
|
|
28
|
+
|
|
29
|
+
content = <<DUMMY_TEXT
|
|
30
|
+
1,dummy,22
|
|
31
|
+
2,dummy,23
|
|
32
|
+
DUMMY_TEXT
|
|
33
|
+
|
|
34
|
+
create_file(path, name, content)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_file(path, name, content)
|
|
39
|
+
create_path(path)
|
|
40
|
+
# file anlegen
|
|
41
|
+
file = File.open("#{path}/#{name}", "w")
|
|
42
|
+
file.print(content)
|
|
43
|
+
file.close
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def create_path(path)
|
|
48
|
+
currentDir = Dir.getwd
|
|
49
|
+
|
|
50
|
+
#Schleife for each item in path
|
|
51
|
+
path.split('/').each do |dirname|
|
|
52
|
+
currentDir = "#{currentDir}/#{dirname}"
|
|
53
|
+
# prüfen ob Verzeichnis existiert
|
|
54
|
+
if not Dir.exists?(currentDir)
|
|
55
|
+
# wenn nicht anlegen
|
|
56
|
+
Dir.mkdir(currentDir)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def delete_path(path)
|
|
63
|
+
currentDir = Dir.getwd
|
|
64
|
+
|
|
65
|
+
if Dir.exists?("#{currentDir}/#{path}")
|
|
66
|
+
FileUtils.rm_rf(path)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def validate_mm(p_featuremap_path)
|
|
2
|
+
mindmap_content = File.read(p_featuremap_path)
|
|
3
|
+
validation_messages = validate_content(mindmap_content)
|
|
4
|
+
return validation_messages
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def validate_content(p_mindmap_content)
|
|
8
|
+
schema = Nokogiri::XML::Schema(File.read("doc/definitions/freemind.xsd"))
|
|
9
|
+
document = Nokogiri::XML(p_mindmap_content)
|
|
10
|
+
validation_messages = schema.validate(document)
|
|
11
|
+
validation_messages.each { |error_msg| @log.info "schema err: #{error_msg}"}
|
|
12
|
+
return validation_messages
|
|
13
|
+
end
|
data/lib/featuremap.rb
CHANGED
|
@@ -6,106 +6,131 @@ require_relative 'mindmap'
|
|
|
6
6
|
|
|
7
7
|
class Featuremap
|
|
8
8
|
|
|
9
|
-
attr_reader :nodes, :exit_status, :
|
|
9
|
+
attr_reader :nodes, :exit_status, :mindmap_path, :features_path
|
|
10
10
|
|
|
11
|
-
def initialize(p_features_path, p_verbose = false)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
def initialize(p_features_path, p_mindmap_path, p_verbose = false)
|
|
12
|
+
if p_mindmap_path == "STDOUT"
|
|
13
|
+
@log = Logger.new(STDERR)
|
|
14
|
+
else
|
|
15
|
+
@log = Logger.new(STDOUT)
|
|
16
|
+
end
|
|
17
|
+
@log.formatter = proc do |severity, datetime, progname, msg|
|
|
18
|
+
date_format = datetime.strftime("%Y-%m-%d %H:%M:%S")
|
|
19
|
+
"[#{date_format}] #{severity.ljust(5,' ')}: #{msg}\n"
|
|
20
|
+
end
|
|
15
21
|
@log.datetime_format = "%H:%M:%S"
|
|
16
22
|
if ENV['LOG_LEVEL'] == 'debug'
|
|
17
23
|
@log.level = Logger::DEBUG
|
|
18
24
|
elsif ENV['LOG_LEVEL'] == 'info'
|
|
19
25
|
@log.level = Logger::INFO
|
|
26
|
+
elsif ENV['LOG_LEVEL'] == 'warn'
|
|
27
|
+
@log.level = Logger::WARN
|
|
20
28
|
else
|
|
21
29
|
# default log level
|
|
22
30
|
@log.level = Logger::ERROR
|
|
23
31
|
end
|
|
24
|
-
if p_verbose
|
|
32
|
+
if p_verbose && @log.level != Logger::DEBUG && p_mindmap_path != "STDOUT"
|
|
25
33
|
@log.level = Logger::INFO
|
|
34
|
+
@log.info "set log level to verbose"
|
|
26
35
|
end
|
|
36
|
+
@exit_status = 0
|
|
37
|
+
@mindmap_path = p_mindmap_path
|
|
27
38
|
if Dir.exists?(p_features_path)
|
|
28
39
|
@features_path = p_features_path
|
|
40
|
+
@log.info("create a new featuremap")
|
|
41
|
+
@mindmap = Mindmap.new(@log)
|
|
29
42
|
else
|
|
30
43
|
@exit_status = 66 # see https://www.freebsd.org/cgi/man.cgi?query=sysexits&sektion=3 for more info
|
|
31
|
-
@
|
|
44
|
+
@log.error("can't find >>#{p_features_path}<< as feature dir")
|
|
45
|
+
return
|
|
32
46
|
end
|
|
33
|
-
@log.info("create a new featuremap")
|
|
34
|
-
@mindmap = Mindmap.new(@log)
|
|
35
47
|
end
|
|
36
48
|
|
|
37
49
|
# class entry point - create a mindmap for a given path
|
|
38
|
-
def create_featuremap(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
def create_featuremap()
|
|
51
|
+
mindmap_path = @mindmap_path
|
|
52
|
+
if mindmap_path != "STDOUT"
|
|
53
|
+
while File.exists?(mindmap_path)
|
|
54
|
+
filename_parts = mindmap_path.split(".")
|
|
55
|
+
if filename_parts[0] =~ /-\d+$/
|
|
56
|
+
filename_parts = filename_parts[0].split("-")
|
|
57
|
+
mindmap_path = "#{filename_parts[0]}-#{filename_parts[1].to_i + 1}.mm"
|
|
58
|
+
else
|
|
59
|
+
mindmap_path = "#{filename_parts[0]}-1.mm"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
if mindmap_path != @mindmap_path
|
|
63
|
+
@log.warn("given mindmap name is already in use, created #{mindmap_path}")
|
|
64
|
+
end
|
|
65
|
+
begin
|
|
66
|
+
IO.write("#{mindmap_path}","")
|
|
67
|
+
rescue Exception
|
|
68
|
+
@log.error("can't write to #{mindmap_path}")
|
|
69
|
+
@exit_status = 74
|
|
70
|
+
return
|
|
71
|
+
end
|
|
43
72
|
end
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
read_features(@features_path)
|
|
74
|
+
if @exit_status == 0
|
|
75
|
+
if mindmap_path != "STDOUT"
|
|
76
|
+
mindmap_file = File.open(mindmap_path,"w")
|
|
77
|
+
mindmap_file.write(@mindmap.to_s)
|
|
78
|
+
mindmap_file.close
|
|
49
79
|
else
|
|
50
|
-
|
|
80
|
+
puts @mindmap.to_s
|
|
51
81
|
end
|
|
52
82
|
end
|
|
53
|
-
if featuremap_path != p_featuremap_path
|
|
54
|
-
@err_msg.push("given mindmap name is already in use, created #{featuremap_path}")
|
|
55
|
-
end
|
|
56
|
-
begin
|
|
57
|
-
IO.write("#{featuremap_path}","")
|
|
58
|
-
rescue Exception
|
|
59
|
-
@err_msg.push("can't write to #{featuremap_path}")
|
|
60
|
-
@log.warn @err_msg
|
|
61
|
-
@exit_status = 74
|
|
62
|
-
return
|
|
63
|
-
end
|
|
64
|
-
read_features(@features_path)
|
|
65
|
-
mindmap_file = File.open(featuremap_path,"w")
|
|
66
|
-
mindmap_file.write(@mindmap.to_s)
|
|
67
|
-
mindmap_file.close
|
|
68
83
|
end
|
|
69
84
|
|
|
70
85
|
# scan feature folder for feature files and subdirs
|
|
71
|
-
def read_features(p_features_path, p_parent_node = nil)
|
|
86
|
+
def read_features(p_features_path = @features_path, p_parent_node = nil)
|
|
72
87
|
# don't read features if some error happened before
|
|
73
88
|
if @exit_status == 0
|
|
74
89
|
feature_node = nil
|
|
75
90
|
begin
|
|
76
|
-
|
|
91
|
+
if p_features_path.end_with?("/")
|
|
92
|
+
features_path = p_features_path
|
|
93
|
+
else
|
|
94
|
+
features_path = p_features_path + "/"
|
|
95
|
+
end
|
|
96
|
+
features = Dir.entries(features_path)
|
|
77
97
|
rescue Exception
|
|
78
|
-
@
|
|
79
|
-
@log.warn @err_msg
|
|
98
|
+
@log.error("can't access >>#{features_path}<< as feature dir")
|
|
80
99
|
@exit_status = 66
|
|
81
100
|
return
|
|
82
101
|
end
|
|
102
|
+
@log.info "start reading features from dir #{features_path}"
|
|
103
|
+
feature_count = 0
|
|
104
|
+
scenario_count = 0
|
|
83
105
|
features.each do |feature_file|
|
|
84
106
|
#ignore files starting with .
|
|
85
107
|
if feature_file =~ /^[^\.]/
|
|
86
108
|
#look for features in only in .feature files
|
|
87
109
|
if feature_file =~ /\.feature$/
|
|
88
|
-
feature = File.read("#{
|
|
110
|
+
feature = File.read("#{features_path}#{feature_file}")
|
|
89
111
|
feature.scan(/^\s*(Feature|Ability|Business Need):\s*(\S.*)$/) do |feature_type, feature_name|
|
|
90
112
|
feature_node = @mindmap.add_node(feature_name, "feature", p_parent_node)
|
|
113
|
+
feature_count += 1
|
|
91
114
|
end
|
|
92
115
|
feature.scan(/^\s*(Scenario|Scenario Outline):\s*(\S.*)$/) do |scenario_type, scenario_name|
|
|
93
116
|
case scenario_type
|
|
94
|
-
|
|
117
|
+
when "Scenario Outline" then @mindmap.add_node(scenario_name, "scenario_outline", feature_node)
|
|
95
118
|
when "Scenario" then @mindmap.add_node(scenario_name, "scenario", feature_node)
|
|
96
119
|
end
|
|
120
|
+
scenario_count += 1
|
|
97
121
|
end
|
|
98
122
|
end
|
|
99
123
|
# look for subdirs
|
|
100
|
-
if File.directory?("#{
|
|
124
|
+
if File.directory?("#{features_path}#{feature_file}")
|
|
101
125
|
# ignore step_definitions and support folders because those are used for code
|
|
102
126
|
if feature_file != "step_definitions" && feature_file != "support"
|
|
103
127
|
subdir_node = @mindmap.add_node(feature_file, "subdir", p_parent_node)
|
|
104
|
-
read_features("#{
|
|
128
|
+
read_features("#{features_path}#{feature_file}", subdir_node)
|
|
105
129
|
end
|
|
106
130
|
end
|
|
107
131
|
end
|
|
108
132
|
end
|
|
133
|
+
@log.info "found #{feature_count} feature(s) and #{scenario_count} scenarios in dir #{features_path}"
|
|
109
134
|
end
|
|
110
135
|
end
|
|
111
136
|
|
data/lib/version.rb
ADDED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: featuremap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthias Carell
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-06-
|
|
11
|
+
date: 2019-06-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: |-
|
|
14
14
|
Featurmaps helps you to visualize the functionality of your \
|
|
@@ -21,8 +21,21 @@ extensions: []
|
|
|
21
21
|
extra_rdoc_files: []
|
|
22
22
|
files:
|
|
23
23
|
- bin/featuremap
|
|
24
|
+
- features/give_feedback.feature
|
|
25
|
+
- features/show features and subdirs/show-features.feature
|
|
26
|
+
- features/show features and subdirs/show-scenarios.feature
|
|
27
|
+
- features/show features and subdirs/show-subdirs.feature
|
|
28
|
+
- features/step_definitions/common_steps.rb
|
|
29
|
+
- features/step_definitions/give_feedback_steps.rb
|
|
30
|
+
- features/step_definitions/show_features_steps.rb
|
|
31
|
+
- features/step_definitions/show_scenarios_steps.rb
|
|
32
|
+
- features/step_definitions/show_subdirs_steps.rb
|
|
33
|
+
- features/support/includes.rb
|
|
34
|
+
- features/support/testdata_gen.rb
|
|
35
|
+
- features/support/validate_xml.rb
|
|
24
36
|
- lib/featuremap.rb
|
|
25
37
|
- lib/mindmap.rb
|
|
38
|
+
- lib/version.rb
|
|
26
39
|
homepage: https://github.com/mckryton/featuremap
|
|
27
40
|
licenses: []
|
|
28
41
|
metadata: {}
|
|
@@ -46,4 +59,16 @@ rubygems_version: 2.6.14
|
|
|
46
59
|
signing_key:
|
|
47
60
|
specification_version: 4
|
|
48
61
|
summary: A script to convert Gherkin features into a mindmap
|
|
49
|
-
test_files:
|
|
62
|
+
test_files:
|
|
63
|
+
- features/give_feedback.feature
|
|
64
|
+
- features/show features and subdirs/show-features.feature
|
|
65
|
+
- features/show features and subdirs/show-scenarios.feature
|
|
66
|
+
- features/show features and subdirs/show-subdirs.feature
|
|
67
|
+
- features/step_definitions/common_steps.rb
|
|
68
|
+
- features/step_definitions/give_feedback_steps.rb
|
|
69
|
+
- features/step_definitions/show_features_steps.rb
|
|
70
|
+
- features/step_definitions/show_scenarios_steps.rb
|
|
71
|
+
- features/step_definitions/show_subdirs_steps.rb
|
|
72
|
+
- features/support/includes.rb
|
|
73
|
+
- features/support/testdata_gen.rb
|
|
74
|
+
- features/support/validate_xml.rb
|