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.
- 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
|