lucid 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/HISTORY.md +8 -0
- data/LICENSE +3 -0
- data/README.md +11 -2
- data/Rakefile +10 -1
- data/lib/autotest/discover.rb +5 -5
- data/lib/autotest/lucid_mixin.rb +34 -35
- data/lib/lucid/ast/table.rb +5 -4
- data/lib/lucid/cli/configuration.rb +0 -6
- data/lib/lucid/cli/options.rb +3 -12
- data/lib/lucid/formatter/condensed.rb +46 -0
- data/lib/lucid/formatter/html.rb +9 -3
- data/lib/lucid/formatter/junit.rb +6 -8
- data/lib/lucid/formatter/standard.rb +1 -7
- data/lib/lucid/generators/project/events-symbiont.rb +1 -1
- data/lib/lucid/platform.rb +1 -1
- data/lib/lucid/runtime.rb +1 -1
- data/lib/lucid/sequence.rb +5 -0
- data/lib/lucid/sequence/sequence_errors.rb +64 -0
- data/lib/lucid/sequence/sequence_group.rb +35 -0
- data/lib/lucid/sequence/sequence_phrase.rb +166 -0
- data/lib/lucid/sequence/sequence_steps.rb +20 -0
- data/lib/lucid/sequence/sequence_support.rb +26 -0
- data/lib/lucid/sequence/sequence_template.rb +354 -0
- data/lib/lucid/spec_file.rb +3 -1
- data/lib/lucid/step_match.rb +1 -1
- data/lib/lucid/wire_support/wire_packet.rb +1 -1
- data/lucid.gemspec +11 -9
- data/spec/lucid/app_spec.rb +42 -0
- data/spec/lucid/configuration_spec.rb +112 -0
- data/spec/lucid/sequences/sequence_conditional_spec.rb +74 -0
- data/spec/lucid/sequences/sequence_group_spec.rb +55 -0
- data/spec/lucid/sequences/sequence_phrase_spec.rb +122 -0
- data/spec/lucid/sequences/sequence_placeholder_spec.rb +56 -0
- data/spec/lucid/sequences/sequence_section_spec.rb +61 -0
- data/spec/lucid/sequences/sequence_support_spec.rb +65 -0
- data/spec/lucid/sequences/sequence_template_spec.rb +298 -0
- data/spec/spec_helper.rb +13 -0
- metadata +86 -54
- data/.rspec +0 -1
data/lib/lucid/spec_file.rb
CHANGED
@@ -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
|
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")
|
data/lib/lucid/step_match.rb
CHANGED
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 =
|
11
|
+
gem.description = 'Test Description Language Execution Engine'
|
12
12
|
gem.summary = "lucid-#{gem.version}"
|
13
|
-
gem.email = [
|
14
|
-
gem.license =
|
15
|
-
gem.homepage =
|
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 =
|
19
|
-
gem.rubygems_version =
|
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', '
|
26
|
-
gem.add_runtime_dependency 'multi_json', '
|
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
|