gherkin 0.0.4-i386-mswin32

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 (79) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +66 -0
  4. data/Rakefile +49 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/gherkin +10 -0
  7. data/cucumber.yml +3 -0
  8. data/features/feature_parser.feature +206 -0
  9. data/features/native_lexer.feature +19 -0
  10. data/features/parser_with_native_lexer.feature +205 -0
  11. data/features/pretty_printer.feature +14 -0
  12. data/features/step_definitions/gherkin_steps.rb +34 -0
  13. data/features/step_definitions/pretty_printer_steps.rb +56 -0
  14. data/features/steps_parser.feature +46 -0
  15. data/features/support/env.rb +33 -0
  16. data/gherkin.gemspec +180 -0
  17. data/java/.gitignore +2 -0
  18. data/java/Gherkin.iml +24 -0
  19. data/java/build.xml +13 -0
  20. data/java/src/gherkin/FixJava.java +34 -0
  21. data/java/src/gherkin/Lexer.java +5 -0
  22. data/java/src/gherkin/LexingError.java +7 -0
  23. data/java/src/gherkin/Listener.java +27 -0
  24. data/java/src/gherkin/ParseError.java +22 -0
  25. data/java/src/gherkin/Parser.java +185 -0
  26. data/java/src/gherkin/lexer/.gitignore +1 -0
  27. data/java/src/gherkin/parser/StateMachineReader.java +62 -0
  28. data/lib/.gitignore +4 -0
  29. data/lib/gherkin.rb +2 -0
  30. data/lib/gherkin/c_lexer.rb +10 -0
  31. data/lib/gherkin/core_ext/array.rb +5 -0
  32. data/lib/gherkin/i18n.yml +535 -0
  33. data/lib/gherkin/i18n_lexer.rb +29 -0
  34. data/lib/gherkin/java_lexer.rb +10 -0
  35. data/lib/gherkin/lexer.rb +43 -0
  36. data/lib/gherkin/parser.rb +19 -0
  37. data/lib/gherkin/parser/meta.txt +4 -0
  38. data/lib/gherkin/parser/root.txt +9 -0
  39. data/lib/gherkin/parser/steps.txt +3 -0
  40. data/lib/gherkin/rb_lexer.rb +10 -0
  41. data/lib/gherkin/rb_lexer/.gitignore +1 -0
  42. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  43. data/lib/gherkin/rb_parser.rb +117 -0
  44. data/lib/gherkin/tools/pretty_printer.rb +83 -0
  45. data/nativegems.sh +5 -0
  46. data/ragel/i18n/.gitignore +1 -0
  47. data/ragel/lexer.c.rl.erb +401 -0
  48. data/ragel/lexer.java.rl.erb +200 -0
  49. data/ragel/lexer.rb.rl.erb +171 -0
  50. data/ragel/lexer_common.rl.erb +46 -0
  51. data/spec/gherkin/c_lexer_spec.rb +21 -0
  52. data/spec/gherkin/fixtures/1.feature +8 -0
  53. data/spec/gherkin/fixtures/complex.feature +43 -0
  54. data/spec/gherkin/fixtures/i18n_fr.feature +13 -0
  55. data/spec/gherkin/fixtures/i18n_no.feature +6 -0
  56. data/spec/gherkin/fixtures/i18n_zh-CN.feature +8 -0
  57. data/spec/gherkin/fixtures/simple.feature +3 -0
  58. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  59. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  60. data/spec/gherkin/i18n_spec.rb +57 -0
  61. data/spec/gherkin/java_lexer_spec.rb +20 -0
  62. data/spec/gherkin/parser_spec.rb +28 -0
  63. data/spec/gherkin/rb_lexer_spec.rb +18 -0
  64. data/spec/gherkin/sexp_recorder.rb +29 -0
  65. data/spec/gherkin/shared/lexer_spec.rb +433 -0
  66. data/spec/gherkin/shared/py_string_spec.rb +124 -0
  67. data/spec/gherkin/shared/table_spec.rb +97 -0
  68. data/spec/gherkin/shared/tags_spec.rb +50 -0
  69. data/spec/spec_helper.rb +53 -0
  70. data/tasks/bench.rake +193 -0
  71. data/tasks/bench/feature_builder.rb +49 -0
  72. data/tasks/bench/generated/.gitignore +1 -0
  73. data/tasks/bench/null_listener.rb +4 -0
  74. data/tasks/compile.rake +70 -0
  75. data/tasks/cucumber.rake +20 -0
  76. data/tasks/ragel_task.rb +83 -0
  77. data/tasks/rdoc.rake +12 -0
  78. data/tasks/rspec.rake +15 -0
  79. metadata +214 -0
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing py_strings" do
7
+
8
+ def ps(content)
9
+ '"""%s"""' % ("\n" + content + "\n")
10
+ end
11
+
12
+ it "should provide the amount of indentation of the triple quotes to the listener" do
13
+ str = <<EOS
14
+ Feature: some feature
15
+ Scenario: some scenario
16
+ Given foo
17
+ """
18
+ Hello
19
+ Goodbye
20
+ """
21
+ Then bar
22
+ EOS
23
+ @listener.should_receive(:py_string).with(" Hello\nGoodbye", 4)
24
+ @lexer.scan(str)
25
+ end
26
+
27
+ it "should parse a simple py_string" do
28
+ @listener.should_receive(:py_string).with("I am a py_string", 1)
29
+ @lexer.scan ps("I am a py_string")
30
+ end
31
+
32
+ it "should parse an empty py_string" do
33
+ @listener.should_receive(:py_string).with("", 4)
34
+ @lexer.scan("Feature: Hi\nScenario: Hi\nGiven a step\n\"\"\"\n\"\"\"")
35
+ end
36
+
37
+ it "should treat a string containing only newlines as only newlines" do
38
+ py_string = <<EOS
39
+ """
40
+
41
+
42
+
43
+ """
44
+ EOS
45
+ @listener.should_receive(:py_string).with("\n\n", 1)
46
+ @lexer.scan(py_string)
47
+ end
48
+
49
+ it "should parse content separated by two newlines" do
50
+ @lexer.scan ps("A\n\nB")
51
+ @listener.to_sexp.should == [
52
+ [:py_string, "A\n\nB", 1],
53
+ ]
54
+ end
55
+
56
+ it "should parse a multiline string" do
57
+ @listener.should_receive(:py_string).with("A\nB\nC\nD", 1)
58
+ @lexer.scan ps("A\nB\nC\nD")
59
+ end
60
+
61
+ it "should ignore unescaped quotes inside the string delimeters" do
62
+ @listener.should_receive(:py_string).with("What does \"this\" mean?", 1)
63
+ @lexer.scan ps('What does "this" mean?')
64
+ end
65
+
66
+ it "should preserve whitespace within the triple quotes" do
67
+ str = <<EOS
68
+ """
69
+ Line one
70
+ Line two
71
+ """
72
+ EOS
73
+ @listener.should_receive(:py_string).with(" Line one\nLine two", 1)
74
+ @lexer.scan(str)
75
+ end
76
+
77
+ it "should preserve tabs within the content" do
78
+ @listener.should_receive(:py_string).with("I have\tsome tabs\nInside\t\tthe content", 1)
79
+ @lexer.scan ps("I have\tsome tabs\nInside\t\tthe content")
80
+ end
81
+
82
+ it "should handle complex py_strings" do
83
+ py_string = <<EOS
84
+ # Feature comment
85
+ @one
86
+ Feature: Sample
87
+
88
+ @two @three
89
+ Scenario: Missing
90
+ Given missing
91
+
92
+ 1 scenario (1 passed)
93
+ 1 step (1 passed)
94
+
95
+ EOS
96
+
97
+ @listener.should_receive(:py_string).with(py_string, 1)
98
+ @lexer.scan ps(py_string)
99
+ end
100
+
101
+ it "should allow whitespace after the closing py_string delimiter" do
102
+ str = <<EOS
103
+ """
104
+ Line one
105
+ """
106
+ EOS
107
+ @listener.should_receive(:py_string).with(" Line one", 1)
108
+ @lexer.scan(str)
109
+ end
110
+
111
+ it "should preserve the last newline(s) at the end of a py_string" do
112
+ str = <<EOS
113
+ """
114
+ PyString text
115
+
116
+
117
+ """
118
+ EOS
119
+ @listener.should_receive(:py_string).with("PyString text\n\n",1)
120
+ @lexer.scan(str)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,97 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing tables" do
7
+ tables = {
8
+ "|a|b|\n" => [%w{a b}],
9
+ "|a|b|c|\n" => [%w{a b c}],
10
+ "|c|d|\n|e|f|\n" => [%w{c d}, %w{e f}]
11
+ }
12
+
13
+ tables.each do |text, expected|
14
+ it "should parse #{text}" do
15
+ @listener.should_receive(:table).with(t(expected), 1)
16
+ @lexer.scan(text.dup)
17
+ end
18
+ end
19
+
20
+ it "should parse a table with many columns" do
21
+ @listener.should_receive(:table).with(t([%w{a b c d e f g h i j k l m n o p}]), 1)
22
+ @lexer.scan("|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|\n")
23
+ end
24
+
25
+ it "should parse a multicharacter cell content" do
26
+ @listener.should_receive(:table).with(t([%w{foo bar}]), 1)
27
+ @lexer.scan("| foo | bar |\n")
28
+ end
29
+
30
+ it "should parse cells with spaces within the content" do
31
+ @listener.should_receive(:table).with(t([["Dill pickle", "Valencia orange"], ["Ruby red grapefruit", "Tire iron"]]), 1)
32
+ @lexer.scan("| Dill pickle | Valencia orange |\n| Ruby red grapefruit | Tire iron |\n")
33
+ end
34
+
35
+ it "should allow utf-8" do
36
+ # Fails in 1.9.1!
37
+ # 'Gherkin::Lexer::Table should allow utf-8 with using == to evaluate' FAILED
38
+ # expected: [[:table, [["ůﻚ", "2"]], 1]],
39
+ # got: [[:table, [["\xC5\xAF\xEF\xBB\x9A", "2"]], 1]] (using ==)
40
+ # BUT, simply running:
41
+ # [[:table, [["ůﻚ", "2"]], 1]].should == [[:table, [["\xC5\xAF\xEF\xBB\x9A", "2"]], 1]]
42
+ # passes
43
+ #
44
+ @lexer.scan(" | ůﻚ | 2 | \n")
45
+ @listener.to_sexp.should == [
46
+ [:table, [["ůﻚ", "2"]], 1]
47
+ ]
48
+ end
49
+
50
+ it "should allow utf-8 using should_receive" do
51
+ @listener.should_receive(:table).with(t([['繁體中文 而且','並且','繁體中文 而且','並且']]), 1)
52
+ @lexer.scan("| 繁體中文 而且|並且| 繁體中文 而且|並且|\n")
53
+ end
54
+
55
+ it "should parse a 2x2 table" do
56
+ @listener.should_receive(:table).with(t([%w{1 2}, %w{3 4}]), 1)
57
+ @lexer.scan("| 1 | 2 |\n| 3 | 4 |\n")
58
+ end
59
+
60
+ it "should parse a 2x2 table with several newlines" do
61
+ @listener.should_receive(:table).with(t([%w{1 2}, %w{3 4}]), 1)
62
+ @lexer.scan("| 1 | 2 |\n| 3 | 4 |\n\n\n")
63
+ end
64
+
65
+ it "should parse a 2x2 table with empty cells" do
66
+ @listener.should_receive(:table).with(t([['1', ''], ['', '4']]), 1)
67
+ @lexer.scan("| 1 | |\n|| 4 |\n")
68
+ end
69
+
70
+ it "should parse a 1x2 table that does not end in a newline" do
71
+ @listener.should_receive(:table).with(t([%w{1 2}]), 1)
72
+ @lexer.scan("| 1 | 2 |")
73
+ end
74
+
75
+ it "should parse a 1x2 table without spaces and newline" do
76
+ @listener.should_receive(:table).with(t([%w{1 2}]), 1)
77
+ @lexer.scan("|1|2|\n")
78
+ end
79
+
80
+ it "should parse a row with whitespace after the rows" do
81
+ @listener.should_receive(:table).with(t([%w{1 2}, %w{a b}]), 1)
82
+ @lexer.scan("| 1 | 2 | \n | a | b | \n")
83
+ end
84
+
85
+ it "should parse a table with lots of whitespace" do
86
+ @listener.should_receive(:table).with(t([["abc", "123"]]), 1)
87
+ @lexer.scan(" \t| \t abc\t| \t123\t \t\t| \t\t \t \t\n ")
88
+ end
89
+
90
+ it "should raise LexingError for rows that aren't closed" do
91
+ lambda {
92
+ @lexer.scan("|| oh hello \n")
93
+ }.should raise_error(/Parsing error on line 1: '|| oh hello/)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,50 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing tags" do
7
+ it "should lex a single tag" do
8
+ @listener.should_receive(:tag).with("dog", 1)
9
+ @lexer.scan("@dog\n")
10
+ end
11
+
12
+ it "should lex multiple tags" do
13
+ @listener.should_receive(:tag).twice
14
+ @lexer.scan("@dog @cat\n")
15
+ end
16
+
17
+ it "should lex UTF-8 tags" do
18
+ @listener.should_receive(:tag).with("シナリオテンプレート", 1)
19
+ @lexer.scan("@シナリオテンプレート\n")
20
+ end
21
+
22
+ it "should lex mixed tags" do
23
+ @listener.should_receive(:tag).with("wip", 1).ordered
24
+ @listener.should_receive(:tag).with("Значения", 1).ordered
25
+ @lexer.scan("@wip @Значения\n")
26
+ end
27
+
28
+ it "should lex wacky identifiers" do
29
+ @listener.should_receive(:tag).exactly(4).times
30
+ @lexer.scan("@BJ-x98.77 @BJ-z12.33 @O_o" "@#not_a_comment\n")
31
+ end
32
+
33
+ # TODO: Ask on ML for opinions about this one
34
+ it "should lex tags without spaces between them?" do
35
+ @listener.should_receive(:tag).twice
36
+ @lexer.scan("@one@two\n")
37
+ end
38
+
39
+ it "should not lex tags beginning with two @@ signs" do
40
+ @listener.should_not_receive(:tag)
41
+ lambda { @lexer.scan("@@test\n") }.should raise_error(/Lexing error on line 1/)
42
+ end
43
+
44
+ it "should not lex a lone @ sign" do
45
+ @listener.should_not_receive(:tag)
46
+ lambda { @lexer.scan("@\n") }.should raise_error(/Lexing error on line 1/)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'spec/gherkin'))
4
+ require 'gherkin'
5
+ require 'gherkin/sexp_recorder'
6
+ require 'rubygems'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'shared/lexer_spec'
10
+ require 'shared/tags_spec'
11
+ require 'shared/py_string_spec'
12
+ require 'shared/table_spec'
13
+
14
+ module GherkinSpecHelper
15
+ def scan_file(file)
16
+ @lexer.scan(File.new(File.dirname(__FILE__) + "/gherkin/fixtures/" + file).read)
17
+ end
18
+ end
19
+
20
+ Spec::Runner.configure do |c|
21
+ c.include(GherkinSpecHelper)
22
+ end
23
+
24
+ # Allows comparison of Java List with Ruby Array (tables)
25
+ Spec::Matchers.define :t do |expected|
26
+ match do |table|
27
+ def table.inspect
28
+ "t " + self.map{|row| row.map{|cell| cell}}.inspect
29
+ end
30
+ table.map{|row| row.map{|cell| cell}}.should == expected
31
+ end
32
+ end
33
+
34
+ Spec::Matchers.define :a do |expected|
35
+ match do |array|
36
+ def array.inspect
37
+ "a " + self.map{|e| e.to_sym}.inspect
38
+ end
39
+ array.map{|e| e.to_sym}.should == expected
40
+ end
41
+ end
42
+
43
+ Spec::Matchers.define :sym do |expected|
44
+ match do |actual|
45
+ expected.to_s == actual.to_s
46
+ end
47
+ end
48
+
49
+ Spec::Matchers.define :allow do |event|
50
+ match do |parser|
51
+ parser.expected.index(event)
52
+ end
53
+ end
@@ -0,0 +1,193 @@
1
+ %w{/../lib /bench}.each do |l|
2
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + l)
3
+ end
4
+
5
+ require 'benchmark'
6
+
7
+ GENERATED_FEATURES = File.expand_path(File.dirname(__FILE__) + "/bench/generated")
8
+
9
+ class RandomFeatureGenerator
10
+ def initialize(number)
11
+ require 'faker'
12
+ require 'feature_builder'
13
+
14
+ @number = number
15
+ end
16
+
17
+ def generate
18
+ @number.times do
19
+ name = catch_phrase
20
+ feature = FeatureBuilder.new(name) do |f|
21
+ num_scenarios = rand_in(1..10)
22
+ num_scenarios.times do
23
+ f.scenario(bs) do |steps|
24
+ num_steps = rand_in(3..10)
25
+ num_steps.times do
26
+ steps.step(sentence, self)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ write feature.to_s, name
32
+ end
33
+ end
34
+
35
+ def write(content, name)
36
+ File.open(GENERATED_FEATURES + "/#{name.downcase.gsub(/[\s\-\/]/, '_')}.feature", "w+") do |file|
37
+ file << content
38
+ end
39
+ end
40
+
41
+ def rand_in(range)
42
+ ary = range.to_a
43
+ ary[rand(ary.length - 1)]
44
+ end
45
+
46
+ def catch_phrase
47
+ Faker::Company.catch_phrase
48
+ end
49
+
50
+ def bs
51
+ Faker::Company.bs.capitalize
52
+ end
53
+
54
+ def sentence
55
+ Faker::Lorem.sentence
56
+ end
57
+
58
+ def table_cell
59
+ Faker::Lorem.words(rand(2)+1).join(" ")
60
+ end
61
+ end
62
+
63
+ class Benchmarker
64
+ def initialize
65
+ @features = Dir[GENERATED_FEATURES + "/**/*feature"]
66
+ end
67
+
68
+ def report(lexer)
69
+ Benchmark.bm do |x|
70
+ x.report("#{lexer}:") { send :"run_#{lexer}" }
71
+ end
72
+ end
73
+
74
+ def report_all
75
+ Benchmark.bmbm do |x|
76
+ x.report("c_gherkin:") { run_c_gherkin }
77
+ x.report("c_gherkin_no_parser:") { run_c_gherkin_no_parser }
78
+ x.report("rb_gherkin:") { run_rb_gherkin }
79
+ x.report("cucumber:") { run_cucumber }
80
+ x.report("tt:") { run_tt }
81
+ end
82
+ end
83
+
84
+ def run_cucumber
85
+ require 'cucumber'
86
+ require 'logger'
87
+ step_mother = Cucumber::StepMother.new
88
+ logger = Logger.new(STDOUT)
89
+ logger.level = Logger::INFO
90
+ step_mother.log = logger
91
+ step_mother.load_plain_text_features(@features)
92
+ end
93
+
94
+ def run_tt
95
+ require 'cucumber'
96
+ # Using Cucumber's Treetop lexer, but never calling #build to build the AST
97
+ lexer = Cucumber::Parser::NaturalLanguage.new(nil, 'en').parser
98
+ @features.each do |file|
99
+ source = IO.read(file)
100
+ parse_tree = lexer.parse(source)
101
+ if parse_tree.nil?
102
+ raise Cucumber::Parser::SyntaxError.new(lexer, file, 0)
103
+ end
104
+ end
105
+ end
106
+
107
+ def run_rb_gherkin
108
+ require 'gherkin'
109
+ require 'gherkin/rb_lexer'
110
+ require 'null_listener'
111
+ @features.each do |feature|
112
+ parser = Gherkin::Parser.new(NullListener.new, true, "root")
113
+ lexer = Gherkin::RbLexer['en'].new(parser)
114
+ lexer.scan(File.read(feature))
115
+ end
116
+ end
117
+
118
+ def run_c_gherkin
119
+ require 'gherkin'
120
+ require 'null_listener'
121
+ @features.each do |feature|
122
+ parser = Gherkin::Parser.new(NullListener.new, true, "root")
123
+ lexer = Gherkin::CLexer['en'].new(parser)
124
+ lexer.scan(File.read(feature))
125
+ end
126
+ end
127
+
128
+ def run_c_gherkin_no_parser
129
+ require 'gherkin'
130
+ require 'null_listener'
131
+ @features.each do |feature|
132
+ lexer = Gherkin::CLexer['en'].new(NullListener.new)
133
+ lexer.scan(File.read(feature))
134
+ end
135
+ end
136
+ end
137
+
138
+ desc "Generate 500 random features and benchmark Cucumber, Treetop and Gherkin with them"
139
+ task :bench => ["bench:clean", "bench:gen"] do
140
+ benchmarker = Benchmarker.new
141
+ benchmarker.report_all
142
+ end
143
+
144
+ namespace :bench do
145
+ desc "Generate [number] features with random content, or 500 features if number is not provided"
146
+ task :gen, :number do |t, args|
147
+ args.with_defaults(:number => 500)
148
+ generator = RandomFeatureGenerator.new(args.number.to_i)
149
+ generator.generate
150
+ end
151
+
152
+ desc "Benchmark Cucumber AST building from the features in tasks/bench/generated"
153
+ task :cucumber do
154
+ benchmarker = Benchmarker.new
155
+ benchmarker.report("cucumber")
156
+ end
157
+
158
+ desc "Benchmark the Treetop parser with the features in tasks/bench/generated"
159
+ task :tt do
160
+ benchmarker = Benchmarker.new
161
+ benchmarker.report("tt")
162
+ end
163
+
164
+ desc "Benchmark the Ruby Gherkin lexer+parser with the features in tasks/bench/generated"
165
+ task :rb_gherkin do
166
+ benchmarker = Benchmarker.new
167
+ benchmarker.report("rb_gherkin")
168
+ end
169
+
170
+ desc "Benchmark the C Gherkin lexer+parser with the features in tasks/bench/generated"
171
+ task :c_gherkin do
172
+ benchmarker = Benchmarker.new
173
+ benchmarker.report("c_gherkin")
174
+ end
175
+
176
+ desc "Benchmark the C Gherkin lexer (no parser) with the features in tasks/bench/generated"
177
+ task :c_gherkin_no_parser do
178
+ benchmarker = Benchmarker.new
179
+ benchmarker.report("c_gherkin_no_parser")
180
+ end
181
+
182
+ desc "Show basic statistics about the features in tasks/bench/generated"
183
+ task :stats do
184
+ ["Feature", "Scenario", "Given"].each do |kw|
185
+ sh "grep #{kw} #{GENERATED_FEATURES}/* | wc -l"
186
+ end
187
+ end
188
+
189
+ desc "Remove all generated features in tasks/bench/generated"
190
+ task :clean do
191
+ rm_f FileList[GENERATED_FEATURES + "/**/*feature"]
192
+ end
193
+ end