cucumber-core 0.1.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/.coveralls.yml +1 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +9 -0
- data/Rakefile +24 -0
- data/cucumber-core.gemspec +32 -0
- data/lib/cucumber/core.rb +37 -0
- data/lib/cucumber/core/ast.rb +13 -0
- data/lib/cucumber/core/ast/background.rb +33 -0
- data/lib/cucumber/core/ast/comment.rb +17 -0
- data/lib/cucumber/core/ast/data_table.rb +326 -0
- data/lib/cucumber/core/ast/describes_itself.rb +16 -0
- data/lib/cucumber/core/ast/doc_string.rb +83 -0
- data/lib/cucumber/core/ast/empty_background.rb +12 -0
- data/lib/cucumber/core/ast/examples_table.rb +95 -0
- data/lib/cucumber/core/ast/feature.rb +62 -0
- data/lib/cucumber/core/ast/location.rb +140 -0
- data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
- data/lib/cucumber/core/ast/names.rb +19 -0
- data/lib/cucumber/core/ast/outline_step.rb +51 -0
- data/lib/cucumber/core/ast/scenario.rb +43 -0
- data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
- data/lib/cucumber/core/ast/step.rb +38 -0
- data/lib/cucumber/core/ast/tag.rb +14 -0
- data/lib/cucumber/core/compiler.rb +136 -0
- data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
- data/lib/cucumber/core/gherkin/document.rb +20 -0
- data/lib/cucumber/core/gherkin/parser.rb +45 -0
- data/lib/cucumber/core/gherkin/writer.rb +220 -0
- data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
- data/lib/cucumber/core/platform.rb +30 -0
- data/lib/cucumber/core/test/case.rb +143 -0
- data/lib/cucumber/core/test/filters.rb +48 -0
- data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
- data/lib/cucumber/core/test/hook_compiler.rb +109 -0
- data/lib/cucumber/core/test/mapper.rb +56 -0
- data/lib/cucumber/core/test/mapping.rb +67 -0
- data/lib/cucumber/core/test/result.rb +191 -0
- data/lib/cucumber/core/test/runner.rb +149 -0
- data/lib/cucumber/core/test/step.rb +69 -0
- data/lib/cucumber/core/test/timer.rb +31 -0
- data/lib/cucumber/core/version.rb +9 -0
- data/lib/cucumber/initializer.rb +18 -0
- data/spec/capture_warnings.rb +68 -0
- data/spec/coverage.rb +10 -0
- data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
- data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
- data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
- data/spec/cucumber/core/ast/location_spec.rb +105 -0
- data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
- data/spec/cucumber/core/ast/step_spec.rb +44 -0
- data/spec/cucumber/core/compiler_spec.rb +249 -0
- data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
- data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
- data/spec/cucumber/core/test/case_spec.rb +416 -0
- data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
- data/spec/cucumber/core/test/mapper_spec.rb +68 -0
- data/spec/cucumber/core/test/mapping_spec.rb +103 -0
- data/spec/cucumber/core/test/result_spec.rb +178 -0
- data/spec/cucumber/core/test/runner_spec.rb +265 -0
- data/spec/cucumber/core/test/step_spec.rb +58 -0
- data/spec/cucumber/core/test/timer_spec.rb +13 -0
- data/spec/cucumber/core_spec.rb +419 -0
- data/spec/cucumber/initializer_spec.rb +49 -0
- metadata +221 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Core
|
3
|
+
module Test
|
4
|
+
class Timer
|
5
|
+
def start
|
6
|
+
@start_time = time_in_nanoseconds
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def duration
|
11
|
+
nsec
|
12
|
+
end
|
13
|
+
|
14
|
+
def nsec
|
15
|
+
time_in_nanoseconds - @start_time
|
16
|
+
end
|
17
|
+
|
18
|
+
def sec
|
19
|
+
nsec / 10 ** 9.0
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def time_in_nanoseconds
|
25
|
+
t = Time.now
|
26
|
+
t.to_i * 10 ** 9 + t.nsec
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Cucumber
|
2
|
+
def self.initializer(*attributes)
|
3
|
+
Module.new do
|
4
|
+
attr_reader(*attributes)
|
5
|
+
private(*attributes)
|
6
|
+
|
7
|
+
define_method(:initialize) do |*arguments|
|
8
|
+
if attributes.size != arguments.size
|
9
|
+
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attributes.size})"
|
10
|
+
end
|
11
|
+
|
12
|
+
attributes.zip(arguments) do |attribute, argument|
|
13
|
+
instance_variable_set("@#{attribute}", argument)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# With thanks to @myronmarston
|
2
|
+
# https://github.com/vcr/vcr/blob/master/spec/capture_warnings.rb
|
3
|
+
|
4
|
+
module CaptureWarnings
|
5
|
+
def report_warnings(&block)
|
6
|
+
current_dir = Dir.pwd
|
7
|
+
warnings, errors = capture_error(&block).partition { |line| line.include?('warning') }
|
8
|
+
project_warnings, other_warnings = warnings.uniq.partition { |line| line.include?(current_dir) }
|
9
|
+
|
10
|
+
if errors.any?
|
11
|
+
puts errors.join("\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
if other_warnings.any?
|
15
|
+
puts "#{ other_warnings.count } non-cucumber-core warnings detected, set VIEW_OTHER_WARNINGS=true to see them."
|
16
|
+
print_warnings('other', other_warnings) if ENV['VIEW_OTHER_WARNINGS']
|
17
|
+
end
|
18
|
+
|
19
|
+
if project_warnings.any?
|
20
|
+
puts "#{ project_warnings.count } cucumber-core warnings detected"
|
21
|
+
print_warnings('cucumber-core', project_warnings)
|
22
|
+
fail "Please remove all cucumber-core warnings."
|
23
|
+
end
|
24
|
+
|
25
|
+
ensure_system_exit_if_required
|
26
|
+
end
|
27
|
+
|
28
|
+
def capture_error(&block)
|
29
|
+
old_stderr = STDERR.clone
|
30
|
+
pipe_r, pipe_w = IO.pipe
|
31
|
+
pipe_r.sync = true
|
32
|
+
error = ""
|
33
|
+
reader = Thread.new do
|
34
|
+
begin
|
35
|
+
loop do
|
36
|
+
error << pipe_r.readpartial(1024)
|
37
|
+
end
|
38
|
+
rescue EOFError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
STDERR.reopen(pipe_w)
|
42
|
+
block.call
|
43
|
+
ensure
|
44
|
+
capture_system_exit
|
45
|
+
STDERR.reopen(old_stderr)
|
46
|
+
pipe_w.close
|
47
|
+
reader.join
|
48
|
+
return error.split("\n")
|
49
|
+
end
|
50
|
+
|
51
|
+
def print_warnings(type, warnings)
|
52
|
+
puts
|
53
|
+
puts "-" * 30 + " #{type} warnings: " + "-" * 30
|
54
|
+
puts
|
55
|
+
puts warnings.join("\n")
|
56
|
+
puts
|
57
|
+
puts "-" * 75
|
58
|
+
puts
|
59
|
+
end
|
60
|
+
|
61
|
+
def ensure_system_exit_if_required
|
62
|
+
raise @system_exit if @system_exit
|
63
|
+
end
|
64
|
+
|
65
|
+
def capture_system_exit
|
66
|
+
@system_exit = $!
|
67
|
+
end
|
68
|
+
end
|
data/spec/coverage.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
formatters = [ SimpleCov::Formatter::HTMLFormatter ]
|
3
|
+
|
4
|
+
if ENV['TRAVIS']
|
5
|
+
require 'coveralls'
|
6
|
+
formatters << Coveralls::SimpleCov::Formatter
|
7
|
+
end
|
8
|
+
|
9
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
|
10
|
+
SimpleCov.start
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'cucumber/core/ast/data_table'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Core
|
6
|
+
module Ast
|
7
|
+
describe DataTable do
|
8
|
+
let(:location) { Location.new('foo.feature', 9..12) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
@table = DataTable.new([
|
12
|
+
%w{one four seven},
|
13
|
+
%w{4444 55555 666666}
|
14
|
+
], location)
|
15
|
+
def @table.cells_rows; super; end
|
16
|
+
def @table.columns; super; end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have rows" do
|
20
|
+
expect( @table.cells_rows[0].map{|cell| cell.value} ).to eq %w{one four seven}
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should have columns" do
|
24
|
+
expect( @table.columns[1].map{|cell| cell.value} ).to eq %w{four 55555}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should have headers" do
|
28
|
+
expect( @table.headers ).to eq %w{one four seven}
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have same cell objects in rows and columns" do
|
32
|
+
# 666666
|
33
|
+
expect( @table.cells_rows[1].__send__(:[], 2) ).to eq @table.columns[2].__send__(:[], 1)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should know about max width of a row" do
|
37
|
+
expect( @table.columns[1].__send__(:width) ).to eq 5
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be convertible to an array of hashes" do
|
41
|
+
expect( @table.hashes ).to eq [
|
42
|
+
{'one' => '4444', 'four' => '55555', 'seven' => '666666'}
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should accept symbols as keys for the hashes" do
|
47
|
+
expect( @table.hashes.first[:one] ).to eq '4444'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return the row values in order" do
|
51
|
+
expect( @table.rows.first ).to eq %w{4444 55555 666666}
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "equality" do
|
55
|
+
it "is equal to another table with the same data" do
|
56
|
+
expect( DataTable.new([[1,2],[3,4]], location) ).to eq DataTable.new([[1,2],[3,4]], location)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "is not equal to another table with different data" do
|
60
|
+
expect( DataTable.new([[1,2],[3,4]], location) ).not_to eq DataTable.new([[1,2]], location)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "is not equal to a non table" do
|
64
|
+
expect( DataTable.new([[1,2],[3,4]], location) ).not_to eq Object.new
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#map" do
|
69
|
+
let(:table) { DataTable.new([ %w{foo bar}, %w{1 2} ], location) }
|
70
|
+
|
71
|
+
it 'yields the contents of each cell to the block' do
|
72
|
+
|
73
|
+
expect { |b| table.map(&b) }.to yield_successive_args('foo', 'bar', '1', '2')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns a new table with the cells modified by the block' do
|
77
|
+
expect( table.map { |cell| "*#{cell}*" } ).to eq DataTable.new([%w{*foo* *bar*}, %w{*1* *2*}], location)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#transpose" do
|
82
|
+
before(:each) do
|
83
|
+
@table = DataTable.new([
|
84
|
+
%w{one 1111},
|
85
|
+
%w{two 22222}
|
86
|
+
], location)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should be convertible in to an array where each row is a hash" do
|
90
|
+
expect( @table.transpose.hashes[0] ).to eq({'one' => '1111', 'two' => '22222'})
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#rows_hash" do
|
95
|
+
|
96
|
+
it "should return a hash of the rows" do
|
97
|
+
table = DataTable.new([
|
98
|
+
%w{one 1111},
|
99
|
+
%w{two 22222}
|
100
|
+
], location)
|
101
|
+
expect( table.rows_hash ).to eq({'one' => '1111', 'two' => '22222'})
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should fail if the table doesn't have two columns" do
|
105
|
+
faulty_table = DataTable.new([
|
106
|
+
%w{one 1111 abc},
|
107
|
+
%w{two 22222 def}
|
108
|
+
], location)
|
109
|
+
expect { faulty_table.rows_hash }.to raise_error('The table must have exactly 2 columns')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#new" do
|
114
|
+
it "should allow Array of Hash" do
|
115
|
+
t1 = DataTable.new([{'name' => 'aslak', 'male' => 'true'}], location)
|
116
|
+
expect( t1.hashes ).to eq [{'name' => 'aslak', 'male' => 'true'}]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should convert to sexp" do
|
121
|
+
sexp_value =
|
122
|
+
[:table,
|
123
|
+
[:row, -1,
|
124
|
+
[:cell, "one"],
|
125
|
+
[:cell, "four"],
|
126
|
+
[:cell, "seven"]
|
127
|
+
],
|
128
|
+
[:row, -1,
|
129
|
+
[:cell, "4444"],
|
130
|
+
[:cell, "55555"],
|
131
|
+
[:cell, "666666"]
|
132
|
+
]
|
133
|
+
]
|
134
|
+
expect( @table.to_sexp ).to eq sexp_value
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'cucumber/core/ast/location'
|
2
|
+
require 'cucumber/core/ast/doc_string'
|
3
|
+
|
4
|
+
module Cucumber
|
5
|
+
module Core
|
6
|
+
module Ast
|
7
|
+
describe DocString do
|
8
|
+
let(:location) { double }
|
9
|
+
let(:doc_string) { DocString.new(content, content_type, location) }
|
10
|
+
|
11
|
+
context '#map' do
|
12
|
+
let(:content) { 'original content' }
|
13
|
+
let(:content_type) { double }
|
14
|
+
|
15
|
+
it 'yields with the content' do
|
16
|
+
expect { |b| doc_string.map(&b) }.to yield_with_args(content)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns a new docstring with new content' do
|
20
|
+
expect( doc_string.map { 'foo' }.content ).to eq 'foo'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises an error if no block is given' do
|
24
|
+
expect { doc_string.map }.to raise_error ArgumentError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'equality' do
|
29
|
+
let(:content) { 'foo' }
|
30
|
+
let(:content_type) { 'text/plain' }
|
31
|
+
|
32
|
+
it 'is equal to another DocString with the same content and content_type' do
|
33
|
+
expect( doc_string ).to eq DocString.new(content, content_type, location)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is not equal to another DocString with different content' do
|
37
|
+
expect( doc_string ).not_to eq DocString.new('bar', content_type, location)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is not equal to another DocString with different content_type' do
|
41
|
+
expect( doc_string ).not_to eq DocString.new(content, 'text/html', location)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'is equal to a string with the same content' do
|
45
|
+
expect( doc_string ).to eq 'foo'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'raises an error when compared with something odd' do
|
49
|
+
expect { doc_string == 5 }.to raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'quacking like a String' do
|
54
|
+
let(:content) { 'content' }
|
55
|
+
let(:content_type) { 'text/plain' }
|
56
|
+
|
57
|
+
it 'delegates #encoding to the content string' do
|
58
|
+
content.force_encoding('us-ascii')
|
59
|
+
expect( doc_string.encoding ).to eq Encoding.find('US-ASCII')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'allows implicit convertion to a String' do
|
63
|
+
expect( 'expected content' ).to include(doc_string)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'allows explicit convertion to a String' do
|
67
|
+
expect( doc_string.to_s ).to eq 'content'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'delegates #gsub to the content string' do
|
71
|
+
expect( doc_string.gsub(/n/, '_') ).to eq 'co_te_t'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'cucumber/core/ast/examples_table'
|
2
|
+
|
3
|
+
module Cucumber::Core::Ast
|
4
|
+
describe ExamplesTable do
|
5
|
+
let(:location) { double(:to_s => 'file.feature:8') }
|
6
|
+
|
7
|
+
describe ExamplesTable::Header do
|
8
|
+
let(:header) { ExamplesTable::Header.new(%w{foo bar baz}, location) }
|
9
|
+
|
10
|
+
describe 'location' do
|
11
|
+
it 'knows the file and line number' do
|
12
|
+
expect( header.file_colon_line ).to eq 'file.feature:8'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'building a row' do
|
17
|
+
it 'includes the header values as keys' do
|
18
|
+
expect( header.build_row(%w{1 2 3}, 1, location) ).to eq ExamplesTable::Row.new({'foo' => '1', 'bar' => '2', 'baz' => '3'}, 1, location)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
describe ExamplesTable::Row do
|
23
|
+
|
24
|
+
describe 'location' do
|
25
|
+
it 'knows the file and line number' do
|
26
|
+
row = ExamplesTable::Row.new({}, 1, location)
|
27
|
+
expect( row.file_colon_line ).to eq 'file.feature:8'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "expanding a string" do
|
32
|
+
context "when an argument matches" do
|
33
|
+
it "replaces the argument with the value from the row" do
|
34
|
+
row = ExamplesTable::Row.new({'arg' => 'replacement'}, 1, location)
|
35
|
+
text = 'this <arg> a test'
|
36
|
+
expect( row.expand(text) ).to eq 'this replacement a test'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "when the replacement value is nil" do
|
41
|
+
it "uses an empty string for the replacement" do
|
42
|
+
row = ExamplesTable::Row.new({'color' => nil}, 1, location)
|
43
|
+
text = 'a <color> cucumber'
|
44
|
+
expect( row.expand(text) ).to eq 'a cucumber'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when an argument does not match" do
|
49
|
+
it "ignores the arguments that do not match" do
|
50
|
+
row = ExamplesTable::Row.new({'x' => '1', 'y' => '2'}, 1, location)
|
51
|
+
text = 'foo <x> bar <z>'
|
52
|
+
expect( row.expand(text) ).to eq 'foo 1 bar <z>'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'accesing the values' do
|
58
|
+
it 'returns the actual row values' do
|
59
|
+
row = ExamplesTable::Row.new({'x' => '1', 'y' => '2'}, 1, location)
|
60
|
+
expect( row.values ).to eq ['1', '2']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'equality' do
|
65
|
+
let(:data) { {} }
|
66
|
+
let(:number) { double }
|
67
|
+
let(:location) { double }
|
68
|
+
let(:original) { ExamplesTable::Row.new(data, number, location) }
|
69
|
+
|
70
|
+
it 'is equal to another instance with the same data, number and location' do
|
71
|
+
expect( original ).to eq ExamplesTable::Row.new(data, number, location)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'is not equal to another instance with different data, number or location' do
|
75
|
+
expect( original ).not_to eq ExamplesTable::Row.new({'x' => 'y'}, number, location)
|
76
|
+
expect( original ).not_to eq ExamplesTable::Row.new(data, double, location)
|
77
|
+
expect( original ).not_to eq ExamplesTable::Row.new(data, number, double)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'is not equal to another type of object' do
|
81
|
+
expect( original ).not_to eq double(data: data, number: number, location: location)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|