yari 0.0.2
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/.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
|