cql 0.1.0 → 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + "/map_reduce"
2
+ module CQL
3
+ DSL_KEYWORDS = %w(features scenario_outlines scenarios all step_lines examples name)
4
+ module Dsl
5
+ (CQL::QUERY_VALUES + CQL::DSL_KEYWORDS).each do |method_name|
6
+ define_method(method_name) { |*args|
7
+ return method_name if args.size == 0
8
+ {method_name=>args}
9
+ }
10
+ end
11
+
12
+ %w(sc_gt sc_gte sc_lt sc_lte soc_gt soc_gte soc_lt soc_lte ssoc_gt ssoc_gte ssoc_lt ssoc_lte tc_lt tc_lte tc_gt tc_gte lc_lt lc_lte lc_gt lc_gte).each do |meth|
13
+ define_method(meth) { |num| {meth=>num} }
14
+ end
15
+
16
+ alias :everything :all
17
+ alias :complete :all
18
+
19
+ def select *what
20
+ @what = what
21
+ end
22
+
23
+ def from where
24
+ @from = where
25
+ @data
26
+ end
27
+
28
+ def tags *tags
29
+ return "tags" if tags.size == 0
30
+ {'tags'=>tags}
31
+ end
32
+
33
+ def with filter
34
+ if filter.has_key? 'name'
35
+ @data = CQL::MapReduce.filter_features(@data, 'feature'=>filter['name'])
36
+ elsif @from == 'features'
37
+ filter.each { |k, v| @data = CQL::MapReduce.filter_features(@data, k=>v) }
38
+ elsif @from == 'scenarios'
39
+ filter.each { |k, v|
40
+ @data = CQL::MapReduce.filter_sso2(@data, k=>v)
41
+ }
42
+ end
43
+ @data
44
+ end
45
+
46
+ class Query
47
+ include Dsl
48
+ attr_reader :data, :what
49
+
50
+ def initialize features, &block
51
+ @data = features
52
+ @data = self.instance_eval(&block)
53
+
54
+ @data= CQL::MapReduce.filter_sso(@data, 'what'=>@from[0, @from.size-1]) if @from != "features"
55
+ result = Array.new(@data.size)
56
+ result = result.map {|e|{}}
57
+ @what.each do |w|
58
+ CQL::MapReduce.send(w, @data).each_with_index { |e, i| result[i][w]=e }
59
+ end
60
+ @data = result.size == 1 ? result.first : result
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,139 @@
1
+ require File.dirname(__FILE__) + "/dsl"
2
+ require 'set'
3
+ module CQL
4
+ QUERY_VALUES = %w(name uri line description type steps id tags examples)
5
+
6
+ class MapReduce
7
+ CQL::QUERY_VALUES.each do |property|
8
+ define_singleton_method(property) do |input|
9
+ input = [input] if input.class != Array
10
+ input.map { |a| a[property] }
11
+ end
12
+ end
13
+
14
+ %w(all everything complete).each do |method_name|
15
+ define_singleton_method(method_name) { |input| input }
16
+ end
17
+
18
+ def self.step_lines input
19
+ input = [input] if input.class != Array
20
+ steps(input).map do |scen|
21
+ scen.map { |line| line['keyword'] + line['name'] }
22
+ end
23
+ end
24
+
25
+ def self.filter_features input, args
26
+ if args.has_key?('feature') && args['feature'][0].class == String
27
+ input = input.find_all { |feature| feature['name'] == args['feature'][0] }
28
+ elsif args.has_key?('feature') && args['feature'][0].class == Regexp
29
+ input = input.find_all { |feature| feature['name'] =~ args['feature'][0] }
30
+ end
31
+
32
+ %w(sc_gt sc_gte sc_lt sc_lte soc_gt soc_gte soc_lt soc_lte ssoc_gt ssoc_gte ssoc_lt ssoc_lte).each do |fn|
33
+ if args.has_key?(fn)
34
+ what, operator = fn.split "_"
35
+ desc = {"sc"=>["Scenario"], "soc"=>["Scenario Outline"], "ssoc"=>["Scenario", "Scenario Outline"]}
36
+ operator_map = {"lt"=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>='}
37
+ input = input.find_all do |feature|
38
+ size = feature['elements'].find_all { |e| desc[what].include? e['keyword'] }.size
39
+ size.send(operator_map[operator], args[fn])
40
+ end
41
+ end
42
+ end
43
+
44
+ %w(tc_lt tc_lte tc_gt tc_gte).each do |fn|
45
+ what, operator = fn.split "_"
46
+ operator_map = {"lt"=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>='}
47
+ if args.has_key?(fn)
48
+ input = input.find_all do |feature|
49
+ feature['tags'] && feature['tags'].size.send(operator_map[operator], args[fn])
50
+ end
51
+ end
52
+ end
53
+
54
+ input = input.find_all { |feature| has_tags feature['tags'], args['tags'] } if args.has_key? 'tags'
55
+ input
56
+ end
57
+
58
+ def self.filter_sso input, args
59
+ results = []
60
+ input = filter_features(input, 'feature'=>args['feature']) if args.has_key?('feature')
61
+ input.each do |feature|
62
+ feature['elements'].each do |element|
63
+ results.push element if element['type'] == args['what']
64
+ end
65
+ end
66
+ results
67
+ end
68
+
69
+ def self.filter_sso2 input, args
70
+ %w(tc_lt tc_lte tc_gt tc_gte).each do |fn|
71
+ what, operator = fn.split "_"
72
+ operator_map = {"lt"=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>='}
73
+ if args.has_key?(fn)
74
+ input.each_with_index do |feature, index|
75
+ filtered_elements= feature['elements'].find_all do |sso|
76
+ sso['tags'].size.send(operator_map[operator], args[fn])
77
+ end
78
+ input[index]['elements'] = filtered_elements
79
+
80
+ end
81
+ end
82
+ end
83
+
84
+ %w(lc_lt lc_lte lc_gt lc_gte).each do |fn|
85
+ what, operator = fn.split "_"
86
+ operator_map = {"lt"=>'<', 'lte'=>'<=', 'gt'=>'>', 'gte'=>'>='}
87
+ if args.has_key?(fn)
88
+ input.each_with_index do |feature, index|
89
+ filtered_elements= feature['elements'].find_all do |sso|
90
+ sso['steps'].size.send(operator_map[operator], args[fn])
91
+ end
92
+ input[index]['elements'] = filtered_elements
93
+ end
94
+ end
95
+ end
96
+
97
+ if args.has_key? 'line'
98
+ input.each_with_index do |feature, index|
99
+ filtered_elements= feature['elements'].find_all do |sso|
100
+ raw_step_lines = sso['steps'].map { |sl| sl['name'] }
101
+ line_to_match = args['line'].first
102
+ result = nil
103
+ if line_to_match.class == String
104
+ result = raw_step_lines.include? line_to_match
105
+ elsif line_to_match.class == Regexp
106
+ result = raw_step_lines.find { |line| line =~ line_to_match }
107
+ if result.class == String
108
+ result = result.size > 0
109
+ else
110
+ result = false
111
+ end
112
+ end
113
+ result
114
+ end
115
+ input[index]['elements'] = filtered_elements
116
+ end
117
+ end
118
+
119
+ input
120
+ end
121
+
122
+ def self.tag_set input
123
+ tags = Set.new
124
+ input.each do |feature|
125
+ feature['elements'].each do |element|
126
+ break if element['tags'] == nil
127
+ element['tags'].each { |tag| tags.add tag['name'] }
128
+ end
129
+ end
130
+ tags.to_a
131
+ end
132
+
133
+ def self.has_tags given, search
134
+ return false if given == nil
135
+ search.count { |tag_for_search| given.map { |t| t["name"] }.include?(tag_for_search) }==search.size
136
+ end
137
+
138
+ end
139
+ end
@@ -0,0 +1,34 @@
1
+ require 'gherkin/parser/parser'
2
+ require 'gherkin/formatter/json_formatter'
3
+ require 'stringio'
4
+ require File.dirname(__FILE__) + "/dsl"
5
+
6
+ module CQL
7
+ class Repository
8
+ include Dsl
9
+ attr_reader :parsed_feature_files
10
+
11
+ def initialize features_home_dir
12
+ @parsed_feature_files = load_features(list_features(features_home_dir))
13
+ end
14
+
15
+ def query &block
16
+ Query.new(parsed_feature_files.clone, &block).data
17
+ end
18
+
19
+ private
20
+ def list_features base_dir
21
+ Dir.glob(base_dir + "/**/*.feature")
22
+ end
23
+
24
+ def load_features sources
25
+ io = StringIO.new
26
+ formatter = Gherkin::Formatter::JSONFormatter.new(io)
27
+ parser = Gherkin::Parser::Parser.new(formatter)
28
+ sources.each { |s| parser.parse(IO.read(s), s, 0) }
29
+ formatter.done
30
+ JSON.parse(io.string)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,486 @@
1
+ require 'rspec'
2
+ require File.dirname(__FILE__) + "/../lib/repo"
3
+
4
+ describe "cql" do
5
+
6
+ describe 'scenario outline and scenario count functions' do
7
+ it 'should filter based on the number of scenarios for ssoc_gt' do
8
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
9
+
10
+ result = gs.query do
11
+ select name
12
+ from features
13
+ with ssoc_gt 5
14
+ end
15
+
16
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
17
+ {"name"=> "f2_7_scenarios_2_so"}]
18
+ end
19
+
20
+ it 'should filter based on the number of scenario outlines for ssoc_gte' do
21
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
22
+
23
+ result = gs.query do
24
+ select name
25
+ from features
26
+ with ssoc_gte 5
27
+ end
28
+
29
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
30
+ {"name"=> "f2_7_scenarios_2_so"},
31
+ {"name"=> "f3_2_scenarios_3_so"}]
32
+
33
+ result = gs.query do
34
+ select name
35
+ from features
36
+ with ssoc_gte 9
37
+ end
38
+
39
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
40
+ {"name"=> "f2_7_scenarios_2_so"}]
41
+
42
+ result = gs.query do
43
+ select name
44
+ from features
45
+ with soc_gte 1
46
+ end
47
+
48
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
49
+ {"name"=> "f2_7_scenarios_2_so"},
50
+ {"name"=> "f3_2_scenarios_3_so"}]
51
+
52
+ result = gs.query do
53
+ select name
54
+ from features
55
+ with soc_gte 10
56
+ end
57
+
58
+ result.should == []
59
+ end
60
+
61
+ it 'should filter based on the number of scenarios for ssoc_lt' do
62
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
63
+
64
+ result = gs.query do
65
+ select name
66
+ from features
67
+ with ssoc_lt 10
68
+ end
69
+
70
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
71
+ {"name"=> "f2_7_scenarios_2_so"},
72
+ {"name"=> "f3_2_scenarios_3_so"}]
73
+
74
+ result = gs.query do
75
+ select name
76
+ from features
77
+ with ssoc_lt 9
78
+ end
79
+
80
+ result.should == {"name"=> "f3_2_scenarios_3_so"}
81
+
82
+ result = gs.query do
83
+ select name
84
+ from features
85
+ with ssoc_lt 3
86
+ end
87
+
88
+ result.should == []
89
+ end
90
+
91
+ it 'should filter based on the number of scenarios for ssoc_lte' do
92
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
93
+
94
+ result = gs.query do
95
+ select name
96
+ from features
97
+ with ssoc_lte 10
98
+ end
99
+
100
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
101
+ {"name"=>"f2_7_scenarios_2_so"},
102
+ {"name"=> "f3_2_scenarios_3_so"}]
103
+
104
+ result = gs.query do
105
+ select name
106
+ from features
107
+ with ssoc_lte 9
108
+ end
109
+
110
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
111
+ {"name"=>"f2_7_scenarios_2_so"},
112
+ {"name"=> "f3_2_scenarios_3_so"}]
113
+
114
+ result = gs.query do
115
+ select name
116
+ from features
117
+ with ssoc_lte 5
118
+ end
119
+
120
+ result.should == {"name"=> "f3_2_scenarios_3_so"}
121
+
122
+
123
+ result = gs.query do
124
+ select name
125
+ from features
126
+ with ssoc_lte 4
127
+ end
128
+
129
+ result.should == []
130
+ end
131
+
132
+ end
133
+
134
+
135
+ describe 'scenario count functions' do
136
+ it 'should filter based on the number of scenarios for sc_gt' do
137
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
138
+
139
+ result = gs.query do
140
+ select name
141
+ from features
142
+ with sc_gt 2
143
+ end
144
+
145
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
146
+ {"name"=> "f2_7_scenarios_2_so"}]
147
+ end
148
+
149
+ it 'should filter based on the number of scenarios for sc_gte' do
150
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
151
+
152
+ result = gs.query do
153
+ select name
154
+ from features
155
+ with sc_gte 2
156
+ end
157
+
158
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
159
+ {"name"=> "f2_7_scenarios_2_so"},
160
+ {"name"=> "f3_2_scenarios_3_so"}]
161
+
162
+ result = gs.query do
163
+ select name
164
+ from features
165
+ with sc_gte 4
166
+ end
167
+
168
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
169
+ {"name"=> "f2_7_scenarios_2_so"}]
170
+
171
+ result = gs.query do
172
+ select name
173
+ from features
174
+ with sc_gte 3
175
+ end
176
+
177
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
178
+ {"name"=> "f2_7_scenarios_2_so"}]
179
+
180
+ result = gs.query do
181
+ select name
182
+ from features
183
+ with sc_gte 7
184
+ end
185
+
186
+ result.should == {"name"=> "f2_7_scenarios_2_so"}
187
+ end
188
+
189
+ it 'should filter based on the number of scenarios for sc_lt' do
190
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
191
+
192
+ result = gs.query do
193
+ select name
194
+ from features
195
+ with sc_lt 7
196
+ end
197
+
198
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
199
+ {"name"=> "f3_2_scenarios_3_so"}]
200
+
201
+ result = gs.query do
202
+ select name
203
+ from features
204
+ with sc_lt 5
205
+ end
206
+
207
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
208
+ {"name"=> "f3_2_scenarios_3_so"}]
209
+
210
+ result = gs.query do
211
+ select name
212
+ from features
213
+ with sc_lt 4
214
+ end
215
+
216
+ result.should == {"name"=> "f3_2_scenarios_3_so"}
217
+ end
218
+
219
+ it 'should filter based on the number of scenarios for sc_lte' do
220
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
221
+
222
+ result = gs.query do
223
+ select name
224
+ from features
225
+ with sc_lte 7
226
+ end
227
+
228
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
229
+ {"name"=>"f2_7_scenarios_2_so"},
230
+ {"name"=> "f3_2_scenarios_3_so"}]
231
+
232
+ result = gs.query do
233
+ select name
234
+ from features
235
+ with sc_lte 5
236
+ end
237
+
238
+ result.should == [{"name"=> "f1_4_scenarios_5_so"},
239
+ {"name"=> "f3_2_scenarios_3_so"}]
240
+
241
+ result = gs.query do
242
+ select name
243
+ from features
244
+ with sc_lte 4
245
+ end
246
+
247
+ result.should == [{"name"=> "f1_4_scenarios_5_so"}, {"name"=> "f3_2_scenarios_3_so"}]
248
+ end
249
+
250
+ it 'should filter on the number of tags on a feature' do
251
+
252
+ end
253
+ end
254
+
255
+ describe 'filter by tag count' do
256
+
257
+ {
258
+ 0=>[],
259
+ 1=>[],
260
+ 2=>{"name"=> "f1_1_tag"},
261
+ 3=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}],
262
+ 4=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
263
+ 5=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}]
264
+
265
+ }.each do |number, expected|
266
+ it "should filter features by the number of tags with the 'tc_lt' operator for count of #{number}" do
267
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/b"
268
+
269
+ result = gs.query do
270
+ select name
271
+ from features
272
+ with tc_lt number
273
+ end
274
+
275
+ result.should == expected
276
+ end
277
+ end
278
+
279
+ {
280
+ 0=>[],
281
+ 1=>{"name"=> "f1_1_tag"},
282
+ 2=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}],
283
+ 3=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
284
+ 4=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}]
285
+
286
+ }.each do |number, expected|
287
+ it "should filter features by the number of tags with the 'tc_lte' operator for count of #{number}" do
288
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/b"
289
+
290
+ result = gs.query do
291
+ select name
292
+ from features
293
+ with tc_lte number
294
+ end
295
+
296
+ result.should == expected
297
+ end
298
+ end
299
+
300
+ {
301
+ 0=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
302
+ 1=>[{"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
303
+ 2=>{"name"=> "f3_3_tags"},
304
+ 3=>[],
305
+ 4=>[]
306
+
307
+ }.each do |number, expected|
308
+ it "should filter features by the number of tags with the 'tc_gt' operator for count of #{number}" do
309
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/b"
310
+
311
+ result = gs.query do
312
+ select name
313
+ from features
314
+ with tc_gt number
315
+ end
316
+
317
+ result.should == expected
318
+ end
319
+ end
320
+
321
+ {
322
+ 0=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
323
+ 1=>[{"name"=> "f1_1_tag"}, {"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
324
+ 2=>[{"name"=> "f2_2_tags"}, {"name"=> "f3_3_tags"}],
325
+ 3=>{"name"=> "f3_3_tags"},
326
+ 4=>[],
327
+ 5=>[]
328
+
329
+ }.each do |number, expected|
330
+ it "should filter features by the number of tags with the 'tc_gte' operator for count of #{number}" do
331
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/b"
332
+
333
+ result = gs.query do
334
+ select name
335
+ from features
336
+ with tc_gte number
337
+ end
338
+
339
+ result.should == expected
340
+ end
341
+ end
342
+
343
+ end
344
+
345
+ describe 'scenario outline count functions' do
346
+ {
347
+ 2=>[{"name"=> "f1_4_scenarios_5_so"}, {"name"=> "f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
348
+ 3=>[{"name"=> "f1_4_scenarios_5_so"}, {"name"=> "f3_2_scenarios_3_so"}],
349
+ 4=>{"name"=> "f1_4_scenarios_5_so"},
350
+ 7=>[]
351
+
352
+ }.each do |number, expected|
353
+ it "soc_gte filter should filter scenarios for input '#{number}'" do
354
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
355
+
356
+ result = gs.query do
357
+ select name
358
+ from features
359
+ with soc_gte number
360
+ end
361
+
362
+ result.should == expected
363
+ end
364
+ end
365
+
366
+ {
367
+ 7=>[{"name"=> "f1_4_scenarios_5_so"}, {"name"=> "f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
368
+ 5=>[{"name"=> "f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
369
+ 4=>[{"name"=> "f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
370
+
371
+ }.each do |number, expected|
372
+ it "soc_lt filter should filter scenarios for input '#{number}'" do
373
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
374
+ result = gs.query do
375
+ select name
376
+ from features
377
+ with soc_lt number
378
+ end
379
+
380
+ result.should == expected
381
+ end
382
+ end
383
+
384
+
385
+ {
386
+ 7=>[{"name"=> "f1_4_scenarios_5_so"}, {"name"=>"f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
387
+ 5=>[{"name"=> "f1_4_scenarios_5_so"}, {"name"=>"f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
388
+ 4=>[{"name"=> "f2_7_scenarios_2_so"}, {"name"=> "f3_2_scenarios_3_so"}],
389
+ }.each do |num, expected|
390
+ it "should filter based on the number of scenarios for soc_lte with input '#{num}'" do
391
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/combined/a"
392
+
393
+ result = gs.query do
394
+ select name
395
+ from features
396
+ with soc_lte num
397
+ end
398
+
399
+ result.should == expected
400
+ end
401
+ end
402
+ end
403
+
404
+
405
+ describe 'filter features by name' do
406
+ it 'should filter by name' do
407
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/scenario/tagged_features"
408
+
409
+ result = gs.query do
410
+ select name
411
+ from features
412
+ with name 'Test2 Feature'
413
+ end
414
+
415
+ result.should == {"name"=> "Test2 Feature"}
416
+ end
417
+
418
+ it 'should filter by name regexp' do
419
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/scenario/tagged_features"
420
+
421
+ result = gs.query do
422
+ select name
423
+ from features
424
+ with name /Test2 Feature/
425
+ end
426
+
427
+ result.should == {"name"=> "Test2 Feature"}
428
+
429
+ result = gs.query do
430
+ select name
431
+ from features
432
+ with name /Feature/
433
+ end
434
+
435
+ result.size.should == 3
436
+ end
437
+ end
438
+
439
+ describe 'filter features by tag' do
440
+ it 'should filter by a single tag' do
441
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/scenario/tagged_features"
442
+
443
+ result = gs.query do
444
+ select name
445
+ from features
446
+ with tags '@one'
447
+ end
448
+
449
+ result.should == [{"name"=> "Test Feature"}, {"name"=>"Test3 Feature"}]
450
+
451
+ result = gs.query do
452
+ select name
453
+ from features
454
+ with tags '@two'
455
+ end
456
+
457
+ result.should == [{"name"=> "Test2 Feature"}, {"name"=>"Test3 Feature"}]
458
+ end
459
+
460
+ it 'should filter by multiple filters' do
461
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/scenario/tagged_features"
462
+
463
+ result = gs.query do
464
+ select name
465
+ from features
466
+ with tags '@two'
467
+ with tags '@one'
468
+ end
469
+
470
+ result.should == {"name"=>"Test3 Feature"}
471
+ end
472
+
473
+ it 'should filter by a multiple tags' do
474
+ gs = CQL::Repository.new File.dirname(__FILE__) + "/../fixtures/features/scenario/tagged_features"
475
+
476
+ result = gs.query do
477
+ select name
478
+ from features
479
+ with tags '@one', '@two'
480
+ end
481
+
482
+ result.should == {"name"=>"Test3 Feature"}
483
+ end
484
+ end
485
+
486
+ end