cucumber-core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|