lucid 0.1.1 → 0.2.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/HISTORY.md +8 -0
  4. data/LICENSE +3 -0
  5. data/README.md +11 -2
  6. data/Rakefile +10 -1
  7. data/lib/autotest/discover.rb +5 -5
  8. data/lib/autotest/lucid_mixin.rb +34 -35
  9. data/lib/lucid/ast/table.rb +5 -4
  10. data/lib/lucid/cli/configuration.rb +0 -6
  11. data/lib/lucid/cli/options.rb +3 -12
  12. data/lib/lucid/formatter/condensed.rb +46 -0
  13. data/lib/lucid/formatter/html.rb +9 -3
  14. data/lib/lucid/formatter/junit.rb +6 -8
  15. data/lib/lucid/formatter/standard.rb +1 -7
  16. data/lib/lucid/generators/project/events-symbiont.rb +1 -1
  17. data/lib/lucid/platform.rb +1 -1
  18. data/lib/lucid/runtime.rb +1 -1
  19. data/lib/lucid/sequence.rb +5 -0
  20. data/lib/lucid/sequence/sequence_errors.rb +64 -0
  21. data/lib/lucid/sequence/sequence_group.rb +35 -0
  22. data/lib/lucid/sequence/sequence_phrase.rb +166 -0
  23. data/lib/lucid/sequence/sequence_steps.rb +20 -0
  24. data/lib/lucid/sequence/sequence_support.rb +26 -0
  25. data/lib/lucid/sequence/sequence_template.rb +354 -0
  26. data/lib/lucid/spec_file.rb +3 -1
  27. data/lib/lucid/step_match.rb +1 -1
  28. data/lib/lucid/wire_support/wire_packet.rb +1 -1
  29. data/lucid.gemspec +11 -9
  30. data/spec/lucid/app_spec.rb +42 -0
  31. data/spec/lucid/configuration_spec.rb +112 -0
  32. data/spec/lucid/sequences/sequence_conditional_spec.rb +74 -0
  33. data/spec/lucid/sequences/sequence_group_spec.rb +55 -0
  34. data/spec/lucid/sequences/sequence_phrase_spec.rb +122 -0
  35. data/spec/lucid/sequences/sequence_placeholder_spec.rb +56 -0
  36. data/spec/lucid/sequences/sequence_section_spec.rb +61 -0
  37. data/spec/lucid/sequences/sequence_support_spec.rb +65 -0
  38. data/spec/lucid/sequences/sequence_template_spec.rb +298 -0
  39. data/spec/spec_helper.rb +13 -0
  40. metadata +86 -54
  41. data/.rspec +0 -1
@@ -73,7 +73,9 @@ module Lucid
73
73
  if @path == "specs"
74
74
  STDOUT.puts ["\nYou don't have a 'specs' directory. This is the default specification",
75
75
  "directory that Lucid will use if one is not specified. So either create",
76
- "that directory or specify where your test repository is located."].join("\n")
76
+ "that directory or specify where your test repository is located.\n\n"].join("\n")
77
+ #e.message << "."
78
+ raise e
77
79
  else
78
80
  STDOUT.puts ["\nThere is no '#{@path}' directory. Since that is what you specified as",
79
81
  "your spec repository, this directory must be present."].join("\n")
@@ -12,7 +12,7 @@ module Lucid
12
12
  end
13
13
 
14
14
  def args
15
- @step_arguments.map{|g| g.val}
15
+ @step_arguments.map{|g| g.val.freeze}
16
16
  end
17
17
 
18
18
  def name
@@ -23,7 +23,7 @@ module Lucid
23
23
  def to_json
24
24
  packet = [@message]
25
25
  packet << @params if @params
26
- packet.to_json
26
+ MultiJson.dump(packet)
27
27
  end
28
28
 
29
29
  def handle_with(handler)
data/lucid.gemspec CHANGED
@@ -8,22 +8,24 @@ Gem::Specification.new do |gem|
8
8
  gem.name = 'lucid'
9
9
  gem.version = Lucid::VERSION
10
10
  gem.authors = ["Jeff Nyman"]
11
- gem.description = "Test Description Language Execution Engine"
11
+ gem.description = 'Test Description Language Execution Engine'
12
12
  gem.summary = "lucid-#{gem.version}"
13
- gem.email = ["jeffnyman@gmail.com"]
14
- gem.license = "MIT"
15
- gem.homepage = "https://github.com/jnyman/lucid"
13
+ gem.email = ['jeffnyman@gmail.com']
14
+ gem.license = 'MIT'
15
+ gem.homepage = 'https://github.com/jnyman/lucid'
16
16
  gem.platform = Gem::Platform::RUBY
17
17
 
18
- gem.required_ruby_version = ">= 1.9.3"
19
- gem.rubygems_version = ">= 1.6.1"
18
+ gem.required_ruby_version = '>= 1.9.3'
19
+ gem.rubygems_version = '>= 1.6.1'
20
20
 
21
- gem.add_runtime_dependency 'rake', '>= 10.0.4'
22
21
  gem.add_runtime_dependency 'thor', '>= 0.18.1'
23
22
  gem.add_runtime_dependency 'builder', '>= 2.1.2'
24
23
  gem.add_runtime_dependency 'diff-lcs', '>= 1.1.3'
25
- gem.add_runtime_dependency 'gherkin', '~> 2.12.0'
26
- gem.add_runtime_dependency 'multi_json', '~> 1.7.5'
24
+ gem.add_runtime_dependency 'gherkin', '>= 2.12.0'
25
+ gem.add_runtime_dependency 'multi_json', '>= 1.8.0', '< 2.0'
26
+
27
+ gem.add_development_dependency 'rspec', '>= 2.14'
28
+ gem.add_development_dependency 'simplecov', '>= 0.7.1'
27
29
 
28
30
  gem.post_install_message = %{
29
31
  (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::) (::)
@@ -0,0 +1,42 @@
1
+ require_relative '../spec_helper'
2
+
3
+ module Lucid
4
+ module CLI
5
+ describe App do
6
+
7
+ let(:args) { [] }
8
+ let(:stdin) { StringIO.new }
9
+ let(:stdout) { StringIO.new }
10
+ let(:stderr) { StringIO.new }
11
+ let(:kernel) { double(:kernel) }
12
+ subject { App.new(args, stdin, stdout, stderr, kernel) }
13
+
14
+ describe "start" do
15
+ context "passed a runtime" do
16
+ let(:runtime) { double('runtime').as_null_object }
17
+
18
+ def do_start
19
+ subject.start(runtime)
20
+ end
21
+
22
+ it "configures the runtime" do
23
+ configuration = double('Configuration').as_null_object
24
+ Configuration.stub(:new => configuration)
25
+ runtime.should_receive(:configure).with(configuration)
26
+ kernel.should_receive(:exit).with(1)
27
+ do_start
28
+ end
29
+
30
+ it "uses that runtime for running and reporting results" do
31
+ results = double('results', :failure? => true)
32
+ runtime.should_receive(:run)
33
+ runtime.stub(:results).and_return(results)
34
+ kernel.should_receive(:exit).with(1)
35
+ do_start
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,112 @@
1
+ require_relative '../spec_helper'
2
+
3
+ module Lucid
4
+ module CLI
5
+ describe Configuration do
6
+
7
+ module ExposeOptions
8
+ attr_reader :options
9
+ end
10
+
11
+ def config
12
+ @config ||= Configuration.new(@out = StringIO.new, @error = StringIO.new).extend(ExposeOptions)
13
+ end
14
+
15
+ def with_these_files(*files)
16
+ File.stub(:directory?).and_return(true)
17
+ File.stub(:file?).and_return(true)
18
+ Dir.stub(:[]).and_return(files)
19
+ end
20
+
21
+ def with_this_configuration_file(info)
22
+ File.stub(:exist?).and_return(true)
23
+ profile_file = info.is_a?(Hash) ? info.to_yaml : info
24
+ IO.stub(:read).with('lucid.yml').and_return(profile_file)
25
+ end
26
+
27
+ it 'should require driver.rb files first' do
28
+ with_these_files('/common/support/browser.rb', '/common/support/driver.rb')
29
+ config.parse(%w{--require /common})
30
+
31
+ config.library_context.should == %w(
32
+ /common/support/driver.rb
33
+ /common/support/browser.rb
34
+ )
35
+ end
36
+
37
+ it 'should not require driver.rb files when a dry run is attempted' do
38
+ with_these_files('/common/support/browser.rb', '/common/support/driver.rb')
39
+ config.parse(%w{--require /common --dry-run})
40
+
41
+ config.library_context.should == %w(
42
+ /common/support/browser.rb
43
+ )
44
+ end
45
+
46
+ it 'should require files in default definition locations' do
47
+ with_these_files('/pages/page.rb', '/steps/steps.rb')
48
+ config.parse(%w{--require /specs})
49
+
50
+ config.definition_context.should == %w(
51
+ /pages/page.rb
52
+ /steps/steps.rb
53
+ )
54
+ end
55
+
56
+ it 'should be able to exclude files based on a specific reference' do
57
+ with_these_files('/common/support/browser.rb', '/common/support/driver.rb')
58
+ config.parse(%w{--require /common --exclude browser.rb})
59
+
60
+ config.spec_requires.should == %w(
61
+ /common/support/driver.rb
62
+ )
63
+ end
64
+
65
+ it 'should be able to exclude files based on a general pattern' do
66
+ with_these_files('/steps/tester.rb', '/steps/tested.rb', '/steps/testing.rb', '/steps/quality.rb')
67
+ config.parse(%w{--require /steps --exclude test(er|ed) --exclude quality})
68
+
69
+ config.spec_requires.should == %w(
70
+ /steps/testing.rb
71
+ )
72
+ end
73
+
74
+ it 'should be able to use a --dry-run option' do
75
+ config.parse(%w{--dry-run})
76
+ config.options[:dry_run].should be_true
77
+ end
78
+
79
+ it 'should be able to use a --no-source option' do
80
+ config.parse(%w{--no-source})
81
+ config.options[:source].should be_false
82
+ end
83
+
84
+ it 'should be able to use a --no-matchers option' do
85
+ config.parse(%w{--no-matchers})
86
+ config.options[:matchers].should be_false
87
+ end
88
+
89
+ it 'should be able to use a --quiet option' do
90
+ config.parse(%w{--quiet})
91
+ config.options[:source].should be_false
92
+ config.options[:matchers].should be_false
93
+ end
94
+
95
+ it 'should be able to use a --verbose option' do
96
+ config.parse(%w{--verbose})
97
+ config.options[:verbose].should be_true
98
+ end
99
+
100
+ it 'should be able to use an --out option' do
101
+ config.parse(%w{--out report.txt})
102
+ config.formats.should == [%w(standard report.txt)]
103
+ end
104
+
105
+ it 'should be able to use multiple --out options' do
106
+ config.parse(%w{--format standard --out report1.txt --out report2.txt})
107
+ config.formats.should == [%w(standard report2.txt)]
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,74 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/lucid/sequence/sequence_template'
3
+
4
+ module Sequence
5
+ module SequenceTemplate
6
+
7
+ describe ConditionalSection do
8
+
9
+ context 'establishing a conditional section' do
10
+ it 'should be created with a variable name and a boolean' do
11
+ expect { ConditionalSection.new('testing', false) }.not_to raise_error
12
+ expect { ConditionalSection.new('testing', true) }.not_to raise_error
13
+ end
14
+
15
+ it 'should know whether to generated based on the existence of an actual value' do
16
+ [false, true].each do |existence|
17
+ instance = ConditionalSection.new('testing', existence)
18
+ expect(instance.existence).to eq(existence)
19
+ end
20
+ end
21
+ end
22
+
23
+ context 'generating a conditional section' do
24
+ let(:example_child_elements) do
25
+ [ StaticText.new('Test '),
26
+ Placeholder.new('content'),
27
+ EOLine.new
28
+ ]
29
+ end
30
+
31
+ subject { ConditionalSection.new('testing', true) }
32
+
33
+ it 'should know its original source text' do
34
+ expect(subject.to_s).to eq('<?testing>')
35
+ end
36
+
37
+ it 'should generate the children when conditions are met' do
38
+ example_child_elements.each { |child| subject.add_child(child) }
39
+
40
+ locals = { 'content' => 'found', 'testing' => 'exists' }
41
+ generated_text = subject.output(Object.new, locals)
42
+ expected_text = "Test found\n"
43
+ expect(generated_text).to eq(expected_text)
44
+ end
45
+
46
+ it 'should generate the children even when value does not exist' do
47
+ instance = ConditionalSection.new('testing', false)
48
+ example_child_elements.each { |child| instance.add_child(child) }
49
+ locals = { 'content' => 'found' }
50
+ generated_text = instance.output(Object.new, locals)
51
+ expected_text = "Test found\n"
52
+ expect(generated_text).to eq(expected_text)
53
+ end
54
+
55
+ it "should generate nothing when conditions are not met" do
56
+ example_child_elements.each { |child| subject.add_child(child) }
57
+
58
+ locals = { 'content' => 'found' }
59
+ generated_text = subject.output(Object.new, locals)
60
+ expect(generated_text).to eq('')
61
+
62
+ instance = ConditionalSection.new('testing', false)
63
+ example_child_elements.each { |child| instance.add_child(child) }
64
+
65
+ locals = { 'content' => 'found', 'testing' => 'exists' }
66
+ generated_text = instance.output(Object.new, locals)
67
+ expect(generated_text).to eq('')
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,55 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ require_relative '../../../lib/lucid/sequence/sequence_group'
4
+
5
+ module Sequence
6
+ describe SequenceGroup do
7
+
8
+ let(:singleton) { SequenceGroup.instance() }
9
+
10
+ context 'starting state' do
11
+ it 'should be empty' do
12
+ expect(singleton.sequence_steps).to be_empty
13
+ end
14
+ end
15
+
16
+ context 'basic operation' do
17
+ let(:example_steps) do
18
+ example = <<-EXAMPLE
19
+ Given the login page
20
+ When the username is "<username>"
21
+ And the password is "<password>"
22
+ And login is clicked
23
+ EXAMPLE
24
+ end
25
+
26
+ it 'should accept the addition of a new sequence phrase' do
27
+ phrase = '[enter credentials]'
28
+ args = [phrase, example_steps, true]
29
+ expect { singleton.add_sequence(*args) }.not_to raise_error
30
+ expect(singleton).to have(1).sequence_steps
31
+ end
32
+
33
+ it 'should not accept the addition of an existing sequence phrase' do
34
+ phrase = '[enter credentials]'
35
+ args = [phrase, example_steps, true]
36
+ msg = "A sequence with phrase '[enter credentials]' already exists."
37
+ expect { singleton.add_sequence(*args) }.to raise_error(Sequence::DuplicateSequenceError, msg)
38
+ end
39
+
40
+ it 'should return the steps of a sequence phrase' do
41
+ phrase = '[enter credentials]'
42
+ input_values = [['username', 'jnyman'], ['password', 'thx1138']]
43
+ generated = singleton.generate_steps(phrase, input_values)
44
+ expected = <<-EXAMPLE
45
+ Given the login page
46
+ When the username is "jnyman"
47
+ And the password is "thx1138"
48
+ And login is clicked
49
+ EXAMPLE
50
+ expect(generated).to eq(expected)
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,122 @@
1
+ require_relative '../../spec_helper'
2
+ require_relative '../../../lib/lucid/sequence/sequence_phrase'
3
+
4
+ module Sequence
5
+
6
+ describe SequencePhrase do
7
+ let(:example_phrase) { 'enter login credentials as <username>' }
8
+
9
+ let(:example_template) do
10
+ example = <<-EXAMPLE
11
+ Given the login page
12
+ When the username is "<username>"
13
+ And the password is "<password>"
14
+ And login is clicked
15
+ EXAMPLE
16
+ end
17
+
18
+ subject { SequencePhrase.new(example_phrase, example_template, true) }
19
+
20
+ context 'sequence phrase generation' do
21
+ it 'should be created with a phrase, steps and a data table indicator' do
22
+ expect { SequencePhrase.new(example_phrase, example_template, true) }.not_to raise_error
23
+ end
24
+
25
+ it 'should indicate that a step argument can never be assigned a value via the phrase' do
26
+ msg = "The step parameter 'password' does not appear in the phrase."
27
+ expect { SequencePhrase.new(example_phrase, example_template, false) }.to raise_error(Sequence::UnreachableStepParameter, msg)
28
+ end
29
+
30
+ it 'should indicate when an argument in the phrase never occurs in any steps' do
31
+ phrase = 'enter credentials as <tester>'
32
+ msg = "The phrase parameter 'tester' does not appear in any step."
33
+ expect { SequencePhrase.new(phrase, example_template, true) }.to raise_error(Sequence::UselessPhraseParameter, msg)
34
+ end
35
+
36
+ it 'should have a sequence key' do
37
+ expect(subject.key).to eq('enter_login_credentials_as_X_T')
38
+ end
39
+
40
+ it 'should establish the placeholders from the sequence phrase' do
41
+ expect(subject.phrase_params).to eq(%w[username])
42
+ end
43
+
44
+ it 'should know the placeholders from the phrase and generated template' do
45
+ expect(subject.values).to eq(%w[username password])
46
+ end
47
+ end
48
+
49
+ context 'sequence phrase execution' do
50
+
51
+ let(:phrase_instance) { %Q|enter credentials as "jnyman"| }
52
+
53
+ it 'should generate the steps' do
54
+ text = subject.expand(phrase_instance, [ %w(password thx1138) ])
55
+ expectation = <<-EXAMPLE
56
+ Given the login page
57
+ When the username is "jnyman"
58
+ And the password is "thx1138"
59
+ And login is clicked
60
+ EXAMPLE
61
+
62
+ expect(text).to eq(expectation)
63
+ end
64
+
65
+ it 'should generate steps even when a step argument has no value' do
66
+ text = subject.expand(phrase_instance, [ ])
67
+ expectation = <<-EXAMPLE
68
+ Given the login page
69
+ When the username is "jnyman"
70
+ And the password is ""
71
+ And login is clicked
72
+ EXAMPLE
73
+
74
+ expect(text).to eq(expectation)
75
+ end
76
+
77
+ it 'should unescape any double-quotes for phrase arguments' do
78
+ specific_phrase = %q|enter credentials as "jnyman\""|
79
+ text = subject.expand(specific_phrase, [ %w(password thx1138) ])
80
+ expectation = <<-EXAMPLE
81
+ Given the login page
82
+ When the username is "jnyman""
83
+ And the password is "thx1138"
84
+ And login is clicked
85
+ EXAMPLE
86
+
87
+ expect(text).to eq(expectation)
88
+ end
89
+
90
+ it 'should indicate when an unknown variable is used' do
91
+ error_message = "Unknown sequence step parameter 'unknown'."
92
+ args = [ %w(unknown anything) ]
93
+ expect { subject.expand(phrase_instance, args) }.to raise_error(UnknownParameterError, error_message)
94
+ end
95
+
96
+ it 'should indicate when an argument gets a value from a phrase and a table' do
97
+ phrase = %Q|enter credentials as "jnyman"|
98
+ msg = "The sequence parameter 'username' has value 'jnyman' and 'tester'."
99
+ args = [ %w(username tester), %w(password thx1138) ]
100
+ expect { subject.expand(phrase, args) }.to raise_error(AmbiguousParameterValue, msg)
101
+ end
102
+
103
+ it 'should expand any built-in variables' do
104
+ phrase = 'do nothing useful'
105
+ quoted_steps = <<-EXAMPLE
106
+ Given the following films:
107
+ <quotes>
108
+ Iron Man 3
109
+ World War Z
110
+ <quotes>
111
+ EXAMPLE
112
+
113
+ instance = SequencePhrase.new(phrase, quoted_steps, true)
114
+ actual = instance.expand(phrase, [])
115
+ expected = quoted_steps.gsub(/<quotes>/, '"""')
116
+ expect(actual).to eq(expected)
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ end