cukehead 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ require 'rexml/document'
2
+ require 'cukehead/feature_node'
3
+
4
+ module Cukehead
5
+
6
+ class FreemindReader
7
+
8
+ def initialize(filename = nil)
9
+ @mmdoc = nil
10
+ read_file filename unless filename.nil?
11
+ end
12
+
13
+ # Loads the text from the specified file into a new REXML::Document
14
+ #
15
+ def read_file(filename)
16
+ File.open(filename, "r") {|f| load_xml(f.read)}
17
+ end
18
+
19
+ # Loads the given XML string into a new REXML::Document.
20
+ #
21
+ def load_xml(xml)
22
+ @mmdoc = REXML::Document.new(xml)
23
+ end
24
+
25
+ # Returns the first <i>REXML::Element</i> containing a TEXT attribute
26
+ # that matches 'Cucumber features:'.
27
+ # Returns nil if no match is found.
28
+ #
29
+ def cucumber_features_node
30
+ # XPath is case-sensitive so I'm using the translate function as
31
+ # described in a blog post titled "Performing a Case In-sensitive
32
+ # search in an XML Document" by Harish Ranganathan
33
+ # http://geekswithblogs.net/ranganh/archive/2005/09/12/53520.aspx
34
+ #
35
+ # There may be functions in XPath version 2 that provide a better way
36
+ # to do case-insensitive search but as of this writing REXML only
37
+ # implements XPath 1.0.
38
+ #
39
+ # Because the search is for a specific string, only those characters
40
+ # need translated to lower case.
41
+ REXML::XPath.first(@mmdoc, '//node[translate(attribute::TEXT, "CUMBERFATS", "cumberfats")="cucumber features:"]')
42
+ end
43
+
44
+
45
+ def get_feature_nodes
46
+ node = cucumber_features_node
47
+ feature_nodes = []
48
+ node.each {|e|
49
+ if e.is_a? REXML::Element
50
+ text = e.attributes["TEXT"]
51
+ feature_nodes << FeatureNode.new(e) if text =~ /^feature:.*/i
52
+ end
53
+ } unless node.nil?
54
+ feature_nodes
55
+ end
56
+
57
+
58
+ def get_features
59
+ result = Hash.new
60
+ feature_nodes = get_feature_nodes
61
+ feature_nodes.each {|feature|
62
+ result[feature.filename] = feature.to_text
63
+ }
64
+ result
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,21 @@
1
+ module Cukehead
2
+
3
+ class FreemindWriter
4
+
5
+ def add_newline_after_tags(xml)
6
+ t = ""
7
+ xml.each_char {|c| c == ">" ? t << c + "\n" : t << c}
8
+ return t
9
+ end
10
+
11
+
12
+ def write_mm(filename, mmxml)
13
+ s = add_newline_after_tags mmxml
14
+ File.open(filename, 'w') do |f|
15
+ f.write(s)
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,275 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'cukehead/app'
3
+
4
+ describe "Cukehead application" do
5
+
6
+ before do
7
+ @testdata_dir = File.dirname(__FILE__) + '/../testdata'
8
+ @features_dir = @testdata_dir + '/project1/features'
9
+ File.directory?(@features_dir).should be_true
10
+ File.directory?($testing_tmp).should be_true
11
+ @test_mm_filename = File.join $testing_tmp, 'app_spec_test.mm'
12
+ File.delete @test_mm_filename if File.exists? @test_mm_filename
13
+ File.exists?(@test_mm_filename).should be_false
14
+ @app = Cukehead::App.new
15
+ end
16
+
17
+
18
+ it "reads a set of Cucumber features and create a FreeMind mind map" do
19
+ @app.features_path = @features_dir
20
+ target = @test_mm_filename
21
+ @app.mindmap_filename = target
22
+ @app.send :read_features
23
+ result = @app.send :write_mindmap
24
+ result.should be_true
25
+ File.exists?(target).should be_true
26
+ end
27
+
28
+
29
+ it "looks for feature files in a 'features' sub-directory of the current directory by default" do
30
+ @app.features_path.should eql Dir.getwd + '/features'
31
+ end
32
+
33
+
34
+ it "accepts a features path to specify where to look for feature files" do
35
+ dir = File.join $testing_tmp, 'features'
36
+ @app.features_path = dir
37
+ @app.features_path.should eql dir
38
+ end
39
+
40
+
41
+ it "creates 'cukehead-output.mm' in a 'mm' sub-directory of the current directory by default" do
42
+ @app.mindmap_filename.should eql Dir.getwd + '/mm/cukehead-output.mm'
43
+ end
44
+
45
+
46
+ it "accepts a mind map file name to override the default" do
47
+ @app.mindmap_filename = @test_mm_filename
48
+ @app.mindmap_filename.should eql @test_mm_filename
49
+ end
50
+
51
+
52
+ it "creates the output directory if it does not exist" do
53
+ outdir = File.join $testing_tmp, 'app_test_dir_create'
54
+ FileUtils.remove_dir(outdir) if File.directory? outdir
55
+ File.directory?(outdir).should be_false
56
+ fn = File.join outdir, 'test.mm'
57
+ @app.features_path = @features_dir
58
+ @app.mindmap_filename = fn
59
+ @app.send :read_features
60
+ @app.send :write_mindmap
61
+ File.directory?(outdir).should be_true
62
+ end
63
+
64
+
65
+ it "does not overwrite an existing mind map file" do
66
+ @app.features_path = @features_dir
67
+ target = @test_mm_filename
68
+ @app.mindmap_filename = target
69
+ File.open(target, 'w') {|f| f.write("###")}
70
+ File.exists?(target).should be_true
71
+
72
+ @app.send :read_features
73
+ @app.send :write_mindmap
74
+
75
+ File.open(target, 'r') {|f|
76
+ s = f.readline
77
+ s.should eql "###"
78
+ }
79
+ end
80
+
81
+
82
+ it "accepts an overwrite option that allows it to replace an exiting mind map file" do
83
+ @app.features_path = @features_dir
84
+ target = @test_mm_filename
85
+ @app.mindmap_filename = target
86
+ File.open(target, 'w') {|f| f.write("###")}
87
+ File.exists?(target).should be_true
88
+
89
+ @app.send :read_features
90
+
91
+ @app.do_overwrite = true
92
+ @app.send :write_mindmap
93
+ File.open(target, 'r') {|f|
94
+ s = f.readline
95
+ s.should_not eql "###"
96
+ }
97
+ end
98
+
99
+
100
+ it "accepts an optional file name of an existing mind map" do
101
+ @app.mindmap_template_filename = 'test.mm'
102
+ end
103
+
104
+
105
+ it "adds feature nodes under an existing 'Cucumber features:' node" do
106
+ @app.features_path = @features_dir
107
+ target = File.join $testing_tmp, 'app_spec_insert_test.mm'
108
+ @app.mindmap_filename = target
109
+ source_mm = File.join(@testdata_dir, 'insert_test.mm')
110
+ @app.mindmap_template_filename = source_mm
111
+
112
+ @app.send :read_features
113
+
114
+ @app.do_overwrite = true
115
+ @app.send :write_mindmap
116
+ mm = ''
117
+ File.open(target, 'r') {|f|
118
+ mm = f.readlines
119
+ }
120
+ # *!* Wow. This spec is ugly. There must be a better way.
121
+ doc = REXML::Document.new(mm.join)
122
+ doc.should_not be_nil
123
+ node = REXML::XPath.first(doc, '//node[attribute::TEXT="Here"]')
124
+ node.should_not be_nil
125
+ child = node.elements.first
126
+ child.to_s.should match /.*Cucumber features:.*/
127
+ grandchild = child.elements[2]
128
+ grandchild.to_s.should match /.*Test feature.*/
129
+ end
130
+
131
+
132
+ it "reads files in the features directory in order by name" do
133
+ fdir = File.join $testing_tmp, 'features'
134
+ FileUtils.remove_dir(fdir) if File.directory? fdir
135
+ File.directory?(fdir).should be_false
136
+ FileUtils.mkdir(fdir)
137
+ # This may not be a good test because it is possible Dir[] will return the
138
+ # files in sorted order even though they are written out of order here.
139
+ File.open(File.join(fdir, 'carrots.feature'), 'w') {|f|f.write("Feature: Carrots\n")}
140
+ File.open(File.join(fdir, 'apples.feature'), 'w') {|f|f.write("Feature: Apples\n")}
141
+ File.open(File.join(fdir, 'bananas.feature'), 'w') {|f|f.write("Feature: Bananas\n")}
142
+ @app.features_path = fdir
143
+ @app.send :read_features
144
+ xml = @app.feature_reader.freemind_xml
145
+ xml.should match /.*Apples.*Bananas.*Carrots.*/m
146
+ end
147
+
148
+ end
149
+
150
+
151
+ describe "Cukehead application (generating features from mind map)" do
152
+
153
+ before do
154
+ # Define temporary mind map file and make sure the file does not exist.
155
+ @test_mm_filename = File.join $testing_tmp, 'app_spec_test.mm'
156
+ File.delete @test_mm_filename if File.exists? @test_mm_filename
157
+ File.exists?(@test_mm_filename).should be_false
158
+
159
+ # Define temporary input directory and create test mind map files.
160
+ @in_dir = File.join $testing_tmp, 'app_cuke_test_input'
161
+ FileUtils.mkdir(@in_dir) unless File.directory? @in_dir
162
+ @in_filename_1 = File.join $testing_tmp, 'test1.mm'
163
+ File.open(@in_filename_1, 'w') {|f| f.write($testing_freemind_data)}
164
+ File.exists?(@in_filename_1).should be_true
165
+ @in_filename_2 = File.join $testing_tmp, 'test2.mm'
166
+ File.open(@in_filename_2, 'w') {|f| f.write($testing_freemind_data_2)}
167
+ File.exists?(@in_filename_2).should be_true
168
+
169
+ # Define temporary output directory and make sure it does not exist.
170
+ @out_dir = File.join $testing_tmp, 'app_cuke_test_output'
171
+ FileUtils.remove_dir(@out_dir) if File.directory? @out_dir
172
+ File.directory?(@out_dir).should be_false
173
+
174
+ # Create an app instance to test.
175
+ @app = Cukehead::App.new
176
+ end
177
+
178
+
179
+ it "looks for a single file matching *.mm in a 'mm' subdirectory of the current directory by default" do
180
+ result = @app.send :default_mm_search_path
181
+ result.should eql Dir.getwd + '/mm/*.mm'
182
+ end
183
+
184
+
185
+ it "accepts the name of the mind map file to read overriding the default" do
186
+ # Uses mindmap_filename attribute as source for optional mind map template
187
+ # file in 'map' mode and as destination in 'cuke' mode.
188
+ @app.mindmap_filename = @test_mm_filename
189
+ @app.mindmap_filename.should eql @test_mm_filename
190
+ end
191
+
192
+
193
+ it "creates features in a 'features' sub-directory of the current directory by default" do
194
+ @app.features_path.should eql Dir.getwd + '/features'
195
+ end
196
+
197
+
198
+ it "accepts a features path to specify where to write feature files" do
199
+ # Uses features_path attribute as source directory in 'map' mode and as
200
+ # destination in 'cuke' mode.
201
+ dir = File.join $testing_tmp, 'output', 'features'
202
+ @app.features_path = dir
203
+ @app.features_path.should eql dir
204
+ end
205
+
206
+
207
+ it "creates the output directory if it does not exist" do
208
+ @app.mindmap_filename = @in_filename_1
209
+ @app.features_path = @out_dir
210
+ @app.send :read_mindmap
211
+ @app.send :write_features
212
+
213
+ File.directory?(@out_dir).should be_true
214
+ end
215
+
216
+
217
+ it "writes a file for each feature in the mind map" do
218
+ @app.mindmap_filename = @in_filename_2
219
+ @app.features_path = @out_dir
220
+ @app.send :read_mindmap
221
+ @app.send :write_features
222
+ a = []
223
+ Dir[File.join(@out_dir, '*')].each {|fn| a << File.basename(fn)}
224
+ a.should have(2).files
225
+ a.should include('first_feature.feature', 'second_feature.feature')
226
+ end
227
+
228
+
229
+ it "does not write any files if any one of the files to be written already exists" do
230
+ FileUtils.mkdir(@out_dir)
231
+ fn = File.join @out_dir, 'second_feature.feature'
232
+ File.open(fn, 'w') {|f| f.write('###')}
233
+ File.exists?(fn).should be_true
234
+ @app.mindmap_filename = @in_filename_2
235
+ @app.features_path = @out_dir
236
+ @app.send :read_mindmap
237
+ @app.send :write_features
238
+ @app.errors.should have(1).error
239
+ @app.errors.first.to_s.should match /exists/i
240
+ a = Dir[File.join(@out_dir, '*')]
241
+ a.should have(1).file
242
+ File.open(fn, 'r') {|t| t.readline.should eql '###' }
243
+ end
244
+
245
+
246
+ it "accepts an overwrite option that allows it to replace exiting files" do
247
+ FileUtils.mkdir(@out_dir)
248
+ fn = File.join @out_dir, 'second_feature.feature'
249
+ File.open(fn, 'w') {|f| f.write('###')}
250
+ File.exists?(fn).should be_true
251
+ @app.mindmap_filename = @in_filename_2
252
+ @app.features_path = @out_dir
253
+ @app.do_overwrite = true
254
+ @app.send :read_mindmap
255
+ @app.send :write_features
256
+ @app.errors.should have(0).errors
257
+ a = Dir[File.join(@out_dir, '*')]
258
+ a.should have(2).files
259
+ File.open(fn, 'r') {|t| t.readline.should_not eql '###' }
260
+ end
261
+
262
+ it "returns an error message if no features were found in the mind map" do
263
+ File.open(@test_mm_filename, 'w') {|f| f.write($testing_freemind_data_nocukes)}
264
+ File.exists?(@test_mm_filename).should be_true
265
+ @app.mindmap_filename = @test_mm_filename
266
+ @app.features_path = @out_dir
267
+ @app.do_overwrite = true
268
+ @app.send :read_mindmap
269
+ @app.send :write_features
270
+ @app.errors.should have(2).errors
271
+ @app.errors[0].to_s.should match /No Cucumber features found/
272
+ @app.errors[1].to_s.should match /may be missing a "Cucumber features:" node/
273
+ end
274
+
275
+ end
@@ -0,0 +1,104 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ def run_cukehead(params = '')
4
+ cmdline = params.empty? ? $cukehead_bin : $cukehead_bin + ' ' + params
5
+ puts "DEBUG: run$ #{cmdline}"
6
+ result = `#{cmdline}`
7
+ puts "DEBUG: result='#{result}'"
8
+ result
9
+ end
10
+
11
+
12
+ describe "cukehead" do
13
+
14
+ before do
15
+ @testdata_dir = File.dirname(__FILE__) + '/../testdata'
16
+ @features_dir = @testdata_dir + '/project1/features'
17
+ @source_mm = @testdata_dir + '/project2/mm/testdata.mm'
18
+ File.directory?(@features_dir).should be_true
19
+ File.directory?($testing_tmp).should be_true
20
+ end
21
+
22
+
23
+ it "should run" do
24
+ result = system("#{$cukehead_bin}")
25
+ result.should be_true
26
+ end
27
+
28
+
29
+ it "should display a help message if no command line arguments are given" do
30
+ result = run_cukehead
31
+ result.should match /.*Usage:.*/
32
+ end
33
+
34
+
35
+ it "should include available commands and options in the help message" do
36
+ result = run_cukehead
37
+ result.should match /\bmap\b/
38
+ result.should match /\bcuke\b/
39
+ result.should match /.*--help\b/
40
+ result.should include ' -h '
41
+ result.should match /.*--overwrite\b/
42
+ result.should include ' -o '
43
+ result.should include ' --mm-filename '
44
+ result.should include ' -m '
45
+ result.should include ' --features-path '
46
+ result.should include ' -f '
47
+ result.should include ' --source-mm '
48
+ result.should include ' -s '
49
+ end
50
+
51
+
52
+ it "should display a help message if '--help' command line argument is given" do
53
+ result = run_cukehead '--help'
54
+ result.should match /.*Usage:.*/
55
+ end
56
+
57
+
58
+ it "should read features and create a mind map if 'map' command line argument is given" do
59
+ params = 'map -o'
60
+ params << ' --features-path ' + @features_dir
61
+ params << ' --mm-filename ' + File.join($testing_tmp, 'test-output.mm')
62
+ result = run_cukehead params
63
+ result.should match /^Reading.*Writing.*/m
64
+ end
65
+
66
+
67
+ it "shows an error message and does not overwrite an existing mind map file" do
68
+ output_filename = File.join $testing_tmp, 'cukehead_spec_test.mm'
69
+ File.open(output_filename, 'w') {|f| f.write("###")}
70
+ File.exists?(output_filename).should be_true
71
+
72
+ params = 'map -f ' + @features_dir + ' -m ' + output_filename
73
+ result = run_cukehead params
74
+ result.should match /.*already exists.*/
75
+
76
+ File.open(output_filename, 'r') {|f|
77
+ s = f.readline
78
+ s.should eql "###"
79
+ }
80
+ end
81
+
82
+
83
+ it "complains about unknown command line options." do
84
+ result = run_cukehead '--unknown-option'
85
+ result.should match /.*error.*unrecognized.*/im
86
+ end
87
+
88
+
89
+ it "complains about unknown command line arguments." do
90
+ result = run_cukehead 'UnknownArgument'
91
+ result.should match /.*error.*unknown.*/im
92
+ end
93
+
94
+
95
+ it "should read a mind map and write features if 'cuke' command is given" do
96
+ params = 'cuke -o'
97
+ params << ' --mm-filename ' + @source_mm
98
+ params << ' --features-path ' + File.join($testing_tmp, 'features')
99
+ result = run_cukehead params
100
+ result.should match /^Reading.*Writing.*/m
101
+ end
102
+
103
+
104
+ end