lucid 0.2.1 → 0.3.0

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/HISTORY.md +13 -0
  3. data/bin/lucid +1 -7
  4. data/lib/lucid/ast/doc_string.rb +1 -1
  5. data/lib/lucid/ast/step_invocation.rb +2 -2
  6. data/lib/lucid/cli/app.rb +6 -2
  7. data/lib/lucid/cli/configuration.rb +18 -5
  8. data/lib/lucid/cli/options.rb +56 -53
  9. data/lib/lucid/core_ext/instance_exec.rb +1 -2
  10. data/lib/lucid/core_ext/proc.rb +2 -2
  11. data/lib/lucid/errors.rb +3 -3
  12. data/lib/lucid/formatter/ansicolor.rb +20 -21
  13. data/lib/lucid/formatter/console.rb +1 -1
  14. data/lib/lucid/formatter/progress.rb +1 -1
  15. data/lib/lucid/generators/project.rb +1 -7
  16. data/lib/lucid/generators/project/browser-fluent.rb +0 -1
  17. data/lib/lucid/generators/project/events-fluent.rb +1 -4
  18. data/lib/lucid/interface_rb/matcher.rb +7 -7
  19. data/lib/lucid/interface_rb/rb_lucid.rb +2 -0
  20. data/lib/lucid/interface_rb/rb_step_definition.rb +5 -6
  21. data/lib/lucid/interface_rb/rb_world.rb +2 -3
  22. data/lib/lucid/platform.rb +3 -3
  23. data/lib/lucid/runtime/facade.rb +9 -11
  24. data/lib/lucid/runtime/orchestrator.rb +2 -3
  25. data/lib/lucid/runtime/results.rb +0 -2
  26. data/lib/lucid/spec_file.rb +1 -3
  27. data/spec/lucid/ansicolor_spec.rb +31 -0
  28. data/spec/lucid/app_spec.rb +73 -4
  29. data/spec/lucid/ast/background_spec.rb +128 -0
  30. data/spec/lucid/ast/doc_string_spec.rb +36 -0
  31. data/spec/lucid/ast/feature_spec.rb +66 -0
  32. data/spec/lucid/ast/outline_table_spec.rb +21 -0
  33. data/spec/lucid/ast/scenario_outline_spec.rb +81 -0
  34. data/spec/lucid/ast/specs_spec.rb +48 -0
  35. data/spec/lucid/ast/step_invocation_spec.rb +45 -0
  36. data/spec/lucid/ast/step_spec.rb +72 -0
  37. data/spec/lucid/ast/table_spec.rb +265 -0
  38. data/spec/lucid/ast/tdl_factory.rb +78 -0
  39. data/spec/lucid/ast/tdl_walker_spec.rb +21 -0
  40. data/spec/lucid/configuration_spec.rb +163 -8
  41. data/spec/lucid/duration_spec.rb +22 -0
  42. data/spec/lucid/facade_spec.rb +31 -0
  43. data/spec/lucid/matcher_spec.rb +127 -0
  44. data/spec/lucid/options_spec.rb +223 -3
  45. data/spec/lucid/orchestrator_spec.rb +117 -0
  46. data/spec/lucid/pending_spec.rb +45 -0
  47. data/spec/lucid/progress_spec.rb +34 -0
  48. data/spec/lucid/rb_step_definition_spec.rb +127 -0
  49. data/spec/lucid/rb_transform_spec.rb +24 -0
  50. data/spec/lucid/regexp_argument_matcher_spec.rb +19 -0
  51. data/spec/lucid/results_spec.rb +81 -0
  52. data/spec/lucid/runtime_spec.rb +1 -1
  53. data/spec/lucid/step_match_spec.rb +55 -0
  54. data/spec/spec_helper.rb +11 -5
  55. metadata +51 -7
  56. data/.ruby-gemset +0 -1
  57. data/.ruby-version +0 -1
  58. data/lib/lucid/generators/project/lucid-fluent.yml +0 -6
@@ -0,0 +1,22 @@
1
+ require_relative '../spec_helper'
2
+
3
+ module Lucid
4
+ module Formatter
5
+ describe Duration do
6
+ include Duration
7
+
8
+ it 'should format ms' do
9
+ format_duration(0.002103).should == '0m0.002s'
10
+ end
11
+
12
+ it 'should format m' do
13
+ format_duration(61.002503).should == '1m1.003s'
14
+ end
15
+
16
+ it 'should format h' do
17
+ format_duration(3661.002503).should == '61m1.003s'
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module Lucid
4
+ describe Runtime::Facade do
5
+
6
+ let(:interface) {double('interface')}
7
+ subject { Runtime::Orchestrator.new(interface,{}) }
8
+ let(:facade) { Runtime::Facade.new(subject, interface) }
9
+
10
+ it 'should produce AST::Table by #table' do
11
+ facade.table( %{
12
+ | study | phase |
13
+ | test-01 | I |
14
+ | test-02 | II |
15
+ } ).should be_kind_of(AST::Table)
16
+ end
17
+
18
+ it 'should produce AST::DocString by #doc_string with default content-type' do
19
+ str = facade.doc_string('TEST')
20
+ str.should be_kind_of(AST::DocString)
21
+ str.content_type.should eq('')
22
+ end
23
+
24
+ it 'should produce AST::DocString by #doc_string with ruby content-type' do
25
+ str = facade.doc_string('TEST','ruby')
26
+ str.should be_kind_of(AST::DocString)
27
+ str.content_type.should eq('ruby')
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,127 @@
1
+ require_relative '../spec_helper'
2
+
3
+ module Lucid
4
+ module InterfaceRb
5
+ describe Matcher do
6
+
7
+ before do
8
+ @pattern = 'There is a missing step'
9
+ @multiline_argument_class = nil
10
+ end
11
+
12
+ let(:code_keyword) { 'Given' }
13
+
14
+ let(:matcher) do
15
+ matcher_class.new(code_keyword, @pattern, @multiline_argument_class)
16
+ end
17
+
18
+ def unindent(s)
19
+ s.split("\n")[1..-2].join("\n").indent(-10)
20
+ end
21
+
22
+ describe Matcher::Regexp do
23
+ let(:matcher_class) { Matcher::Regexp }
24
+ let(:matcher_text) { matcher.to_s }
25
+
26
+ it 'should wrap matcher patterns in parentheses' do
27
+ @pattern = 'A "string" with 4 spaces'
28
+
29
+ matcher_text.should == unindent(%{
30
+ Given (/^A "(.*?)" with (\\d+) spaces$/) do |arg1, arg2|
31
+ pending
32
+ end
33
+ })
34
+ end
35
+
36
+ it 'should recognize numbers in name and make an according regexp' do
37
+ @pattern = 'There are 4 spaces'
38
+
39
+ matcher_text.should == unindent(%{
40
+ Given (/^There are (\\d+) spaces$/) do |arg1|
41
+ pending
42
+ end
43
+ })
44
+ end
45
+
46
+ it 'should recognise a mix of ints, strings and a table' do
47
+ @pattern = 'There are 9 "lucid" tests in 20 "categories"'
48
+ @multiline_argument_class = Lucid::AST::Table
49
+
50
+ matcher_text.should == unindent(%{
51
+ Given (/^There are (\\d+) "(.*?)" tests in (\\d+) "(.*?)"$/) do |arg1, arg2, arg3, arg4, table|
52
+ # table is a Lucid::AST::Table
53
+ pending
54
+ end
55
+ })
56
+ end
57
+
58
+ it "should recognize quotes in name and make according regexp" do
59
+ @pattern = 'A "lucid" test'
60
+
61
+ matcher_text.should == unindent(%{
62
+ Given (/^A "(.*?)" test$/) do |arg1|
63
+ pending
64
+ end
65
+ })
66
+ end
67
+
68
+ it 'should recognize several quoted words in name and make according regexp and args' do
69
+ @pattern = 'A "first" and "second" arg'
70
+
71
+ matcher_text.should == unindent(%{
72
+ Given (/^A "(.*?)" and "(.*?)" arg$/) do |arg1, arg2|
73
+ pending
74
+ end
75
+ })
76
+ end
77
+
78
+ it 'should not use quote group when there are no quotes' do
79
+ @pattern = 'A first arg'
80
+
81
+ matcher_text.should == unindent(%{
82
+ Given (/^A first arg$/) do
83
+ pending
84
+ end
85
+ })
86
+ end
87
+
88
+ it 'should be helpful with tables' do
89
+ @pattern = 'A "first" arg'
90
+ @multiline_argument_class = Lucid::AST::Table
91
+
92
+ matcher_text.should == unindent(%{
93
+ Given (/^A "(.*?)" arg$/) do |arg1, table|
94
+ # table is a Lucid::AST::Table
95
+ pending
96
+ end
97
+ })
98
+ end
99
+ end
100
+
101
+ describe Matcher::Classic do
102
+ let(:matcher_class) { Matcher::Classic }
103
+
104
+ it 'renders matcher as unwrapped regular expression' do
105
+ matcher.to_s.should eql unindent(%{
106
+ Given /^There is a missing step$/ do
107
+ pending
108
+ end
109
+ })
110
+ end
111
+ end
112
+
113
+ describe Matcher::Percent do
114
+ let(:matcher_class) { Matcher::Percent }
115
+
116
+ it 'renders matcher as percent-style regular expression' do
117
+ matcher.to_s.should eql unindent(%{
118
+ Given %r{^There is a missing step$} do
119
+ pending
120
+ end
121
+ })
122
+ end
123
+ end
124
+
125
+ end
126
+ end
127
+ end
@@ -1,4 +1,5 @@
1
- require_relative '../spec_helper'
1
+ require 'spec_helper'
2
+ require 'lucid/cli/options'
2
3
 
3
4
  module Lucid
4
5
  module CLI
@@ -23,6 +24,13 @@ module Lucid
23
24
  def prep_args(args)
24
25
  args.is_a?(Array) ? args : args.split(' ')
25
26
  end
27
+
28
+ def with_this_configuration(info)
29
+ Dir.stub(:glob).with('{,.config/,config/}lucid{.yml,.yaml}').and_return(['lucid.yml'])
30
+ File.stub(:exist?).and_return(true)
31
+ lucid_yml = info.is_a?(Hash) ? info.to_yaml : info
32
+ IO.stub(:read).with('lucid.yml').and_return(lucid_yml)
33
+ end
26
34
 
27
35
  describe 'parsing options' do
28
36
 
@@ -37,7 +45,7 @@ module Lucid
37
45
  end
38
46
 
39
47
  context '--version' do
40
- it "should display Lucid's version" do
48
+ it 'should display Lucid version' do
41
49
  after_parsing('--version') do
42
50
  output_stream.string.should =~ /#{Lucid::VERSION}/
43
51
  end
@@ -55,6 +63,20 @@ module Lucid
55
63
  end
56
64
  end
57
65
  end
66
+
67
+ context '--i18n' do
68
+ context "with LANG specified as 'help'" do
69
+ it 'lists all known languages' do
70
+ during_parsing '--i18n help' do
71
+ Kernel.should_receive(:exit)
72
+ end
73
+ end
74
+
75
+ it 'exits the program' do
76
+ during_parsing('--i18n help') { Kernel.should_receive(:exit) }
77
+ end
78
+ end
79
+ end
58
80
 
59
81
  context '-r or --require' do
60
82
  it 'should collect all specified files into an array' do
@@ -85,7 +107,7 @@ module Lucid
85
107
 
86
108
  context '-b or --backtrace' do
87
109
  it 'should use a full backtrace during Lucid execution' do
88
- during_parsing("-b") do
110
+ during_parsing('-b') do
89
111
  Lucid.should_receive(:use_full_backtrace=).with(true)
90
112
  end
91
113
  end
@@ -119,6 +141,204 @@ module Lucid
119
141
  end
120
142
  end
121
143
 
144
+ context '-P or --no-profile' do
145
+ it 'disables profiles' do
146
+ with_this_configuration({'default' => '-v --require code_file.rb'})
147
+
148
+ after_parsing('-P --require other_code_file.rb') do
149
+ options[:require].should == ['other_code_file.rb']
150
+ end
151
+ end
152
+
153
+ it 'notifies the user that the profiles are being disabled' do
154
+ with_this_configuration({'default' => '-v'})
155
+
156
+ after_parsing('--no-profile --require other_code_file.rb') do
157
+ output_stream.string.should =~ /Disabling profiles.../
158
+ end
159
+ end
160
+ end
161
+
162
+ context '-p PROFILE or --profile PROFILE' do
163
+ it 'respects --quiet when defined in the profile' do
164
+ with_this_configuration('test' => '-q')
165
+
166
+ options.parse(%w[-p test])
167
+ options[:matchers].should be_false
168
+ options[:source].should be_false
169
+ end
170
+
171
+ it 'uses the default profile passed in during initialization if none is specified by the user' do
172
+ with_this_configuration({'default' => '--require test_helper'})
173
+
174
+ options = Options.new(output_stream, error_stream, :default_profile => 'default')
175
+ options.parse(%w{--format progress})
176
+ options[:require].should include('test_helper')
177
+ end
178
+
179
+ it 'merges all unique values from both the command line and the profile' do
180
+ with_this_configuration('test' => %w[--verbose])
181
+
182
+ options.parse(%w[--wip --profile test])
183
+ options[:wip].should be_true
184
+ options[:verbose].should be_true
185
+ end
186
+
187
+ it 'gives precedence to the original options spec source path' do
188
+ with_this_configuration('test' => %w[specs])
189
+
190
+ options.parse(%w[test.spec -p test])
191
+ options[:spec_source].should == %w[test.spec]
192
+ end
193
+
194
+ it 'combines the require files of both' do
195
+ with_this_configuration('bar' => %w[--require specs -r helper.rb])
196
+
197
+ options.parse(%w[--require test.rb -p bar])
198
+ options[:require].should == %w[test.rb specs helper.rb]
199
+ end
200
+
201
+ it 'combines the tag names of both' do
202
+ with_this_configuration('test' => %w[-t @smoke])
203
+
204
+ options.parse(%w[--tags @wip -p test])
205
+ options[:tag_expressions].should == ['@wip', '@smoke']
206
+ end
207
+
208
+ it 'only takes the paths from the original options, and disregards the profiles' do
209
+ with_this_configuration('test' => %w[specs])
210
+
211
+ options.parse(%w[test.spec -p test])
212
+ options[:spec_source].should == ['test.spec']
213
+ end
214
+
215
+ it 'uses the paths from the profile when none are specified originally' do
216
+ with_this_configuration('test' => %w[test.spec])
217
+
218
+ options.parse(%w[-p test])
219
+ options[:spec_source].should == ['test.spec']
220
+ end
221
+
222
+ it 'combines environment variables from the profile but gives precedence to command line args' do
223
+ with_this_configuration('test' => %w[BROWSER=firefox DRIVER=mechanize])
224
+
225
+ options.parse(%w[-p test DRIVER=watir CI=jenkins])
226
+ options[:env_vars].should == {'CI' => 'jenkins', 'BROWSER' => 'firefox', 'DRIVER' => 'watir'}
227
+ end
228
+
229
+ it 'disregards STDOUT formatter defined in profile when another is passed in via command line' do
230
+ with_this_configuration({'test' => %w[--format standard]})
231
+
232
+ options.parse(%w{--format progress --profile test})
233
+ options[:formats].should == [['progress', output_stream]]
234
+ end
235
+
236
+ it 'includes any non-STDOUT formatters from the profile' do
237
+ with_this_configuration({'report' => %w[--format html -o results.html]})
238
+
239
+ options.parse(%w{--format progress --profile report})
240
+ options[:formats].should == [['progress', output_stream], ['html', 'results.html']]
241
+ end
242
+
243
+ it 'does not include STDOUT formatters from the profile if there is a STDOUT formatter in command line' do
244
+ with_this_configuration({'report' => %w[--format html -o results.html --format standard]})
245
+
246
+ options.parse(%w{--format progress --profile report})
247
+ options[:formats].should == [['progress', output_stream], ['html', 'results.html']]
248
+ end
249
+
250
+ it 'includes any STDOUT formatters from the profile if no STDOUT formatter was specified in command line' do
251
+ with_this_configuration({'report' => %w[--format html]})
252
+
253
+ options.parse(%w{--format rerun -o rerun.txt --profile report})
254
+ options[:formats].should == [['html', output_stream], ['rerun', 'rerun.txt']]
255
+ end
256
+
257
+ it 'assumes all of the formatters defined in the profile when none are specified on command line' do
258
+ with_this_configuration({'report' => %w[--format progress --format html -o results.html]})
259
+
260
+ options.parse(%w{--profile report})
261
+ options[:formats].should == [['progress', output_stream], ['html', 'results.html']]
262
+ end
263
+
264
+ it 'only reads lucid.yml once' do
265
+ original_parse_count = $lucid_yml_read_count
266
+ $lucid_yml_read_count = 0
267
+
268
+ begin
269
+ with_this_configuration(<<-END
270
+ <% $lucid_yml_read_count += 1 %>
271
+ default: --format standard
272
+ END
273
+ )
274
+ options = Options.new(output_stream, error_stream, :default_profile => 'default')
275
+ options.parse(%w(-f progress))
276
+
277
+ $lucid_yml_read_count.should == 1
278
+ ensure
279
+ $lucid_yml_read_count = original_parse_count
280
+ end
281
+ end
282
+ end
283
+
284
+ context '-P or --no-profile' do
285
+ it 'disables profiles' do
286
+ with_this_configuration({'default' => '-v --require file_specified_in_default_profile.rb'})
287
+
288
+ after_parsing('-P --require test_helper.rb') do
289
+ options[:require].should == ['test_helper.rb']
290
+ end
291
+ end
292
+
293
+ it 'notifies the user that the profiles are being disabled' do
294
+ with_this_configuration({'default' => '-v'})
295
+
296
+ after_parsing('--no-profile --require test_helper.rb') do
297
+ output_stream.string.should =~ /Disabling profiles.../
298
+ end
299
+ end
300
+ end
301
+
302
+ context '--matcher-type' do
303
+ it 'parses the matcher type argument' do
304
+ after_parsing('--matcher-type classic') do
305
+ options[:matcher_type].should eql :classic
306
+ end
307
+ end
308
+ end
309
+
310
+ it 'assigns any extra arguments as paths to specs' do
311
+ after_parsing('-f pretty test.spec other_specs') do
312
+ options[:spec_source].should == ['test.spec', 'other_specs']
313
+ end
314
+ end
315
+
316
+ it 'does not mistake environment variables as spec paths' do
317
+ after_parsing('test.spec ENV=ci') do
318
+ options[:spec_source].should == ['test.spec']
319
+ end
320
+ end
321
+
322
+ describe 'dry-run' do
323
+ it 'should have the default value for matchers' do
324
+ with_this_configuration({'test' => %w[--dry-run]})
325
+ options.parse(%w{--dry-run})
326
+ options[:matchers].should == true
327
+ end
328
+
329
+ it 'should set matchers to false when no-matchers is provided after dry-run' do
330
+ with_this_configuration({'test' => %w[--dry-run --no-snippets]})
331
+ options.parse(%w{--dry-run --no-matchers})
332
+ options[:matchers].should == false
333
+ end
334
+
335
+ it 'should set matchers to false when no-matchers is provided before dry-run' do
336
+ with_this_configuration({'test' => %w[--no-snippet --dry-run]})
337
+ options.parse(%w{--no-matchers --dry-run})
338
+ options[:matchers].should == false
339
+ end
340
+ end
341
+
122
342
  end
123
343
 
124
344
  end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ module Lucid
4
+ describe Runtime::Orchestrator do
5
+ let(:options) { {} }
6
+ let(:interface) { double('interface') }
7
+ subject { Runtime::Orchestrator.new(interface, options) }
8
+
9
+ let(:dsl) do
10
+ @rb = subject.load_code_language('rb')
11
+ Object.new.extend(InterfaceRb::RbLucid)
12
+ end
13
+
14
+ it 'should format step names' do
15
+ dsl.Given(/it (.*) in (.*)/) { |what, month| }
16
+ dsl.Given(/some other phrase/) { |what, month| }
17
+
18
+ format = subject.step_match('it works in lucid').format_args('[%s]')
19
+ format.should == 'it [works] in [lucid]'
20
+ end
21
+
22
+ it 'should cache step match results' do
23
+ dsl.Given(/it (.*) in (.*)/) { |what, month| }
24
+ step_match = subject.step_match('it works in lucid')
25
+ @rb.should_not_receive :step_matches
26
+ second_step_match = subject.step_match('it works in lucid')
27
+ step_match.should equal(second_step_match)
28
+ end
29
+
30
+
31
+ describe 'resolving test definition matches' do
32
+ it 'should raise Undefined error when no test definitions match' do
33
+ lambda do
34
+ subject.step_match('Simple Lucid Test')
35
+ end.should raise_error(Undefined)
36
+ end
37
+
38
+ it 'should raise Ambiguous error with guess hint when multiple test definitions match' do
39
+ expected_error = %{Ambiguous match of "Simple Lucid Test":
40
+
41
+ spec/lucid/orchestrator_spec.rb:\\d+:in `/Simple (.*) Test/'
42
+ spec/lucid/orchestrator_spec.rb:\\d+:in `/Simple Lucid (.*)/'
43
+
44
+ You can run again with --guess to make Lucid be a little more smart about it.
45
+ }
46
+ dsl.Given(/Simple (.*) Test/) {|app|}
47
+ dsl.Given(/Simple Lucid (.*)/) {|action|}
48
+
49
+ lambda do
50
+ subject.step_match("Simple Lucid Test")
51
+ end.should raise_error(Ambiguous, /#{expected_error}/)
52
+ end
53
+
54
+ describe 'when --guess is used' do
55
+ let(:options) { {:guess => true} }
56
+
57
+ it 'should not show --guess hint' do
58
+ expected_error = %{Ambiguous match of "Simple lucid test":
59
+
60
+ spec/lucid/orchestrator_spec.rb:\\d+:in `/Simple (.*)/'
61
+ spec/lucid/orchestrator_spec.rb:\\d+:in `/Simple (.*)/'
62
+
63
+ }
64
+ dsl.Given(/Simple (.*)/) {|phrase|}
65
+ dsl.Given(/Simple (.*)/) {|phrase|}
66
+
67
+ lambda do
68
+ subject.step_match('Simple lucid test')
69
+ end.should raise_error(Ambiguous, /#{expected_error}/)
70
+ end
71
+
72
+ it 'should pick right test definition when an equal number of capture groups' do
73
+ right = dsl.Given(/Simple (.*) test/) {|app|}
74
+ wrong = dsl.Given(/Simple (.*)/) {|phrase|}
75
+
76
+ subject.step_match('Simple lucid test').step_definition.should == right
77
+ end
78
+
79
+ it 'should pick right test definition when an unequal number of capture groups' do
80
+ right = dsl.Given(/Simple (.*) test ran (.*)/) {|app|}
81
+ wrong = dsl.Given(/Simple (.*)/) {|phrase|}
82
+
83
+ subject.step_match('Simple lucid test ran well').step_definition.should == right
84
+ end
85
+
86
+ it 'should pick most specific test definition when an unequal number of capture groups' do
87
+ general = dsl.Given(/Simple (.*) test ran (.*)/) {|app|}
88
+ specific = dsl.Given(/Simple lucid test ran well/) do; end
89
+ more_specific = dsl.Given(/^Simple lucid test ran well$/) do; end
90
+
91
+ subject.step_match('Simple lucid test ran well').step_definition.should == more_specific
92
+ end
93
+
94
+ it 'should not raise Ambiguous error when multiple test definitions match' do
95
+ dsl.Given(/Simple (.*) test/) {|app|}
96
+ dsl.Given(/Simple (.*)/) {|phrase|}
97
+
98
+ lambda do
99
+ subject.step_match('Simple lucid test')
100
+ end.should_not raise_error
101
+ end
102
+
103
+ it 'should not raise NoMethodError when guessing from multiple test definitions with nil fields' do
104
+ dsl.Given(/Simple (.*) test( cannot run well)?/) {|app, status|}
105
+ dsl.Given(/Simple (.*)?/) {|phrase|}
106
+
107
+ lambda do
108
+ subject.step_match('Simple lucid test')
109
+ end.should_not raise_error
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+ end