yari 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.idea/.name +1 -0
- data/.idea/encodings.xml +7 -0
- data/.idea/misc.xml +14 -0
- data/.idea/modules.xml +8 -0
- data/.idea/workspace.xml +712 -0
- data/.idea/yari.iml +135 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/coveralls.yml +1 -0
- data/features/no_scenario.feature +1 -0
- data/features/scenario_outline.feature +10 -0
- data/features/simple_feature.feature +22 -0
- data/features/test_cases.feature +6 -0
- data/features/updated_feature.feature +12 -0
- data/features/updated_scenario.feature +12 -0
- data/lib/yari.rb +74 -0
- data/lib/yari/builder.rb +88 -0
- data/lib/yari/version.rb +3 -0
- data/lib/yari/yari_dsl.rb +125 -0
- data/lib/yari/yari_loader.rb +52 -0
- data/spec/builder_spec.rb +45 -0
- data/spec/features/no_feature_spec.rb +7 -0
- data/spec/features/no_scenario_spec.rb +5 -0
- data/spec/features/scenario_outline_spec.rb +10 -0
- data/spec/features/simple_feature_spec.rb +26 -0
- data/spec/features/test_cases_spec.rb +13 -0
- data/spec/features/updated_feature_spec.rb +11 -0
- data/spec/features/updated_scenario_spec.rb +11 -0
- data/spec/integration_spec.rb +87 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/yari_spec.rb +58 -0
- data/yari.gemspec +27 -0
- metadata +169 -0
data/lib/yari/version.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
class << self
|
2
|
+
def feature(name = nil, new_metadata = {}, &block)
|
3
|
+
raise ArgumentError.new("requires a name") if name.nil?
|
4
|
+
|
5
|
+
new_metadata = ::RSpec.configuration.feature_metadata.merge(new_metadata)
|
6
|
+
matching_feature = find_feature(name)
|
7
|
+
new_metadata[:feature_name]= name
|
8
|
+
|
9
|
+
if matching_feature
|
10
|
+
if matching_feature.tags.include?('@updated')
|
11
|
+
pending_feature(name, new_metadata, block.source_location,
|
12
|
+
"Feature has been marked as updated\n \t# Update specs for this feature and remove the @updated tag\n \t# Feature file: '#{feature_path(block.source_location)}"
|
13
|
+
)
|
14
|
+
else
|
15
|
+
describe("Feature: #{name}", new_metadata.merge(:current_feature => matching_feature), &block)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
pending_feature(name, new_metadata, block.source_location, "No such feature in '#{feature_path(block.source_location)}'")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def find_feature(name)
|
25
|
+
Yari.features.find do |feature|
|
26
|
+
feature.name == name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def feature_path(spec_location)
|
31
|
+
Yari.spec_to_feature(spec_location.first, false)
|
32
|
+
end
|
33
|
+
|
34
|
+
def pending_feature(name, new_metadata, spec_location, reason)
|
35
|
+
describe "Feature: #{name}", new_metadata do
|
36
|
+
it do |example|
|
37
|
+
example.metadata.merge!(
|
38
|
+
file_path: spec_location[0],
|
39
|
+
location: "#{spec_location[0]}:#{spec_location[1]}"
|
40
|
+
)
|
41
|
+
skip reason
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Yari
|
48
|
+
module DSL
|
49
|
+
module Rspec
|
50
|
+
def background(&block)
|
51
|
+
before(:each, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def scenario(name = nil, new_metadata = {}, &block)
|
55
|
+
raise ArgumentError.new("requires a name") if name.nil?
|
56
|
+
|
57
|
+
matching_scenario = find_scenario(self.metadata[:current_feature], name)
|
58
|
+
new_metadata[:scenario_name]= name
|
59
|
+
|
60
|
+
if matching_scenario
|
61
|
+
if matching_scenario.tags.include?('@updated')
|
62
|
+
pending_scenario(name, new_metadata, block.source_location,
|
63
|
+
"Scenario has been marked as updated\n \t# Update specs for this scenario and remove the @updated tag\n \t# Feature file: '#{feature_path(block.source_location)}'"
|
64
|
+
)
|
65
|
+
# elsif matching_scenario.arguments
|
66
|
+
# specify "Scenario: #{name}", new_metadata do
|
67
|
+
# instance_exec(*matching_scenario.arguments, &block)
|
68
|
+
# end
|
69
|
+
else
|
70
|
+
specify("Scenario: #{name}", new_metadata, &block)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
pending_scenario(name, new_metadata, block.source_location, "No such scenario in '#{feature_path(block.source_location)}'")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def scenario_with_test_cases(name = nil, new_metadata = {}, &block)
|
79
|
+
raise ArgumentError.new("requires a name") if name.nil?
|
80
|
+
|
81
|
+
matching_scenario = find_scenario(self.metadata[:current_feature], name)
|
82
|
+
|
83
|
+
if matching_scenario
|
84
|
+
if matching_scenario.tags.include?('@updated')
|
85
|
+
pending_scenario(name, new_metadata, block.source_location,
|
86
|
+
"Scenario has been marked as updated\n \t# Update specs for this scenario and remove the @updated tag\n \t# Feature file: '#{feature_path(block.source_location)}'"
|
87
|
+
)
|
88
|
+
else
|
89
|
+
describe("Scenario: #{name}", new_metadata.merge(:scenario_name => name), &block)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
pending_scenario(name, new_metadata, block.source_location, "No such scenario in '#{feature_path(block.source_location)}'")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_case(name = nil, new_metadata = {}, &block)
|
97
|
+
raise ArgumentError.new("requires a name") if name.nil?
|
98
|
+
new_metadata[:test_case_name]= name
|
99
|
+
specify("Test_Case: #{name}", new_metadata, &block)
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def find_scenario(feature, name)
|
105
|
+
feature.scenarios.find do |scenario|
|
106
|
+
scenario.name == name
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def feature_path(spec_location)
|
111
|
+
Yari.spec_to_feature(spec_location.first, false)
|
112
|
+
end
|
113
|
+
|
114
|
+
def pending_scenario(name, new_metadata, spec_location, reason)
|
115
|
+
specify name, new_metadata do |example|
|
116
|
+
example.metadata.merge!(
|
117
|
+
full_description: "Scenario: #{name}",
|
118
|
+
location: "#{spec_location[0]}:#{spec_location[1]}"
|
119
|
+
)
|
120
|
+
skip reason
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'yari'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
module Yari
|
5
|
+
module RSpec
|
6
|
+
module Loader
|
7
|
+
def load(*paths, &block)
|
8
|
+
|
9
|
+
# Override feature exclusion filter if running features
|
10
|
+
if paths.any? { |path| Yari.feature?(path) }
|
11
|
+
::RSpec.configuration.filter_manager.exclusions.rules.reject! do |key, value|
|
12
|
+
key == :feature || (key == :type && value == 'feature')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
paths = paths.map do |path|
|
17
|
+
if Yari.feature?(path)
|
18
|
+
spec_path = Yari.feature_to_spec(path)
|
19
|
+
if File.exist?(spec_path)
|
20
|
+
spec_path
|
21
|
+
else
|
22
|
+
Yari::Builder.build(path).features.each do |feature|
|
23
|
+
::RSpec.describe("Feature: #{feature.name}", :type => :feature, :feature => true) do
|
24
|
+
it do |example|
|
25
|
+
example.metadata[:location] = path << ':1'
|
26
|
+
skip('No spec implemented for feature')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
else
|
33
|
+
path
|
34
|
+
end
|
35
|
+
end.compact
|
36
|
+
|
37
|
+
# Load needed features to Yari.features array
|
38
|
+
paths.each do |path|
|
39
|
+
if Yari.feature_spec?(path)
|
40
|
+
feature_path = Yari.spec_to_feature(path)
|
41
|
+
|
42
|
+
if File.exists?(feature_path)
|
43
|
+
Yari.features += Yari::Builder.build(feature_path).features
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
super(*paths, &block) if paths.size > 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Yari::Builder do
|
4
|
+
|
5
|
+
context 'a simple feature' do
|
6
|
+
let(:feature_file) { File.expand_path('../features/simple_feature.feature', File.dirname(__FILE__)) }
|
7
|
+
let(:builder) { Yari::Builder.build(feature_file) }
|
8
|
+
let(:feature) { builder.features.first }
|
9
|
+
|
10
|
+
it 'parses the feature' do
|
11
|
+
expect(feature).to be_a_kind_of(Yari::Feature)
|
12
|
+
expect(feature.name).to eq('A simple feature')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'gets all the scenarios in the feature' do
|
16
|
+
expect(feature.scenarios.count).to eq(4)
|
17
|
+
expect(feature.scenarios.first).to be_a_kind_of(Yari::Scenario)
|
18
|
+
expect(feature.scenarios.first.name).to eq('A simple scenario')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'gets tags associated with the feature' do
|
22
|
+
expect(feature.tags).to eq(['@feature_tag'])
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'gets tags associated with the scenario' do
|
26
|
+
expect(feature.scenarios.first.tags).to eq(['@scenario_tag'])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with scenario outlines' do
|
31
|
+
let(:feature_file) { File.expand_path('../features/scenario_outline.feature', File.dirname(__FILE__)) }
|
32
|
+
let(:builder) { Yari::Builder.build(feature_file) }
|
33
|
+
let(:feature) { builder.features.first }
|
34
|
+
|
35
|
+
|
36
|
+
it 'extracts scenario' do
|
37
|
+
expect(feature.scenarios.map(&:name)).to eq(['a simple outline'])
|
38
|
+
expect(feature.scenarios.first.type).to eq(:ScenarioOutline)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'extracts examples from an outline' do
|
42
|
+
expect(feature.scenarios.first.examples).to eq(Yari::Example)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
feature 'using scenario outlines' do
|
4
|
+
scenario 'a simple outline' do |hp, damage, state, happy|
|
5
|
+
expect(hp).to be_a(Float)
|
6
|
+
expect(damage).to be_a(Fixnum)
|
7
|
+
expect(state).to be_a(String)
|
8
|
+
expect([true, false]).to include happy
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
feature 'A simple feature' do
|
4
|
+
# ensure background is executed as before(:each)
|
5
|
+
background do
|
6
|
+
@number ||= 41
|
7
|
+
@number += 1
|
8
|
+
end
|
9
|
+
|
10
|
+
scenario 'A simple scenario' do
|
11
|
+
expect(@number).to eq(42)
|
12
|
+
end
|
13
|
+
|
14
|
+
scenario 'Raising error' do
|
15
|
+
raise ArgumentError.new("Your argument is invalid!")
|
16
|
+
end
|
17
|
+
|
18
|
+
scenario 'Different metadata type', :type => :controller, :feature => false do
|
19
|
+
expect(RSpec.current_example.metadata[:type]).to eq(:controller)
|
20
|
+
expect(RSpec.current_example.metadata[:feature]).to eq(false)
|
21
|
+
end
|
22
|
+
|
23
|
+
scenario 'Custom metadata tag', :custom => "foobar" do
|
24
|
+
expect(RSpec.current_example.metadata[:custom]).to eq("foobar")
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
feature 'A feature with test cases' do
|
2
|
+
|
3
|
+
scenario_with_test_cases 'A scenario requiring multiple cases to prove' do
|
4
|
+
test_case 'a passing test case' do
|
5
|
+
fail('the first test case failed')
|
6
|
+
end
|
7
|
+
|
8
|
+
test_case 'a failing test case' do
|
9
|
+
fail('the second test case also also failed')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This feature should be marked as pending
|
2
|
+
# because it is tagged as @updated in gherkin
|
3
|
+
feature 'Updated feature' do
|
4
|
+
scenario 'Attack a monster with cool tag' do
|
5
|
+
expect(1).to eq(1)
|
6
|
+
end
|
7
|
+
|
8
|
+
scenario 'Attack another monster' do
|
9
|
+
expect(1).to eq(1)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# This feature should be marked as pending
|
2
|
+
# because it is tagged as @updated in gherkin
|
3
|
+
feature 'Feature with updated scenario' do
|
4
|
+
scenario 'Updated scenario' do
|
5
|
+
expect(1).to eq(1)
|
6
|
+
end
|
7
|
+
|
8
|
+
scenario 'Attack another monster' do
|
9
|
+
expect(1).to eq(1)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'The CLI', :type => :integration do
|
4
|
+
context 'running features from features directory' do
|
5
|
+
|
6
|
+
it 'ignores --tag ~feature flag when running features' do
|
7
|
+
expect(%x(rspec features --tag ~feature 2>&1)).to include('11 examples, 3 failures, 3 pending')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'ignores --tag ~type:feature flag when running features' do
|
11
|
+
expect(%x(rspec features --tag ~type:feature 2>&1)).to include('11 examples, 3 failures, 3 pending')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'running features specs on their own' do
|
16
|
+
before(:all) do
|
17
|
+
@result = %x(rspec --tag feature --format documentation 2>&1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'passes all specs' do
|
21
|
+
expect(@result).to include('11 examples, 3 failures, 4 pending')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'prepends features with "Feature: " prefix' do
|
25
|
+
expect(@result).to include('Feature: A simple feature')
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'prepends scenarios with "Scenario: " prefix' do
|
29
|
+
expect(@result).to include('Scenario: A simple scenario')
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'non-existing scenario' do
|
33
|
+
it 'shows name of non-existing scenario' do
|
34
|
+
expect(@result).to include("Scenario: Non-existing scenario")
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'shows that spec implements non-existing scenario' do
|
38
|
+
expect(@result).to include("No such scenario in 'features/no_scenario.feature'")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'shows line number where missing scenario is mentioned' do
|
42
|
+
expect(@result).to include("/spec/features/no_scenario_spec.rb:2")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'non-existing feature' do
|
47
|
+
it 'shows name of non-existing feature' do
|
48
|
+
expect(@result).to include("Feature: Missing feature")
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'shows that spec implements non-existing feature' do
|
52
|
+
expect(@result).to include("No such feature in 'features/no_feature.feature'")
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'shows line number where missing scenario is mentioned' do
|
56
|
+
expect(@result).to include("/spec/features/no_feature_spec.rb:3")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'updated features and scenarios' do
|
61
|
+
it 'recognizes and notifies when feature is marked as @updated' do
|
62
|
+
expect(@result).to include('Feature has been marked as updated')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'recognizes and notifies when scenario is marked as @updated' do
|
66
|
+
expect(@result).to include('Scenario has been marked as updated')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# it "shows the correct description" do
|
72
|
+
# @result.should include('A simple feature')
|
73
|
+
# @result.should include('is a simple feature')
|
74
|
+
# end
|
75
|
+
|
76
|
+
# it "prints out failures and successes" do
|
77
|
+
# @result.should include('35 examples, 3 failures, 5 pending')
|
78
|
+
# end
|
79
|
+
|
80
|
+
# it "includes features in backtraces" do
|
81
|
+
# @result.should include('examples/errors.feature:5:in `raise error')
|
82
|
+
# end
|
83
|
+
|
84
|
+
# it "includes the right step name when steps call steps" do
|
85
|
+
# @result.should include("No such step: 'this is an unimplemented step'")
|
86
|
+
# end
|
87
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/yari_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Yari do
|
4
|
+
let(:feature_path) { '/project/features/awesome-def_file.feature' }
|
5
|
+
let(:spec_path) { '/project/spec/features/awesome-def_file_spec.rb' }
|
6
|
+
|
7
|
+
let(:short_feature_path) { 'features/awesome-def_file.feature' }
|
8
|
+
let(:short_spec_path) { 'spec/features/awesome-def_file_spec.rb' }
|
9
|
+
|
10
|
+
context '#feature?' do
|
11
|
+
it 'recognizes if path is feature path' do
|
12
|
+
expect(Yari.feature?(feature_path)).to eq(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'recognizes if path is not feature path' do
|
16
|
+
expect(Yari.feature?(spec_path)).to eq(false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context '#feature_spec?' do
|
21
|
+
it 'recognizes if path is spec path' do
|
22
|
+
expect(Yari.feature_spec?(spec_path)).to eq(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'recognizes if path is not spec path' do
|
26
|
+
expect(Yari.feature_spec?(feature_path)).to eq(false)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#feature_to_spec' do
|
31
|
+
it 'properly translates feature file to spec file' do
|
32
|
+
expect(Yari.feature_to_spec(feature_path)).to eq(spec_path)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'excludes prefix when requested' do
|
36
|
+
expect(Yari.feature_to_spec(feature_path, false)).
|
37
|
+
to eq(short_spec_path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#spec_to_feature' do
|
42
|
+
it 'properly translates spec file to feature file' do
|
43
|
+
expect(Yari.spec_to_feature(spec_path)).
|
44
|
+
to eq(feature_path)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'excludes prefix when requested' do
|
48
|
+
expect(Yari.spec_to_feature(spec_path, false)).
|
49
|
+
to eq(short_feature_path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
specify '#spec_to_feature and #feature_to_spec should be independent' do
|
54
|
+
p1 = Yari.feature_to_spec(Yari.spec_to_feature(spec_path))
|
55
|
+
p2 = Yari.feature_to_spec(feature_path)
|
56
|
+
expect(p1).to eq(p2)
|
57
|
+
end
|
58
|
+
end
|