cucumber-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +20 -0
  8. data/README.md +9 -0
  9. data/Rakefile +24 -0
  10. data/cucumber-core.gemspec +32 -0
  11. data/lib/cucumber/core.rb +37 -0
  12. data/lib/cucumber/core/ast.rb +13 -0
  13. data/lib/cucumber/core/ast/background.rb +33 -0
  14. data/lib/cucumber/core/ast/comment.rb +17 -0
  15. data/lib/cucumber/core/ast/data_table.rb +326 -0
  16. data/lib/cucumber/core/ast/describes_itself.rb +16 -0
  17. data/lib/cucumber/core/ast/doc_string.rb +83 -0
  18. data/lib/cucumber/core/ast/empty_background.rb +12 -0
  19. data/lib/cucumber/core/ast/examples_table.rb +95 -0
  20. data/lib/cucumber/core/ast/feature.rb +62 -0
  21. data/lib/cucumber/core/ast/location.rb +140 -0
  22. data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
  23. data/lib/cucumber/core/ast/names.rb +19 -0
  24. data/lib/cucumber/core/ast/outline_step.rb +51 -0
  25. data/lib/cucumber/core/ast/scenario.rb +43 -0
  26. data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
  27. data/lib/cucumber/core/ast/step.rb +38 -0
  28. data/lib/cucumber/core/ast/tag.rb +14 -0
  29. data/lib/cucumber/core/compiler.rb +136 -0
  30. data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
  31. data/lib/cucumber/core/gherkin/document.rb +20 -0
  32. data/lib/cucumber/core/gherkin/parser.rb +45 -0
  33. data/lib/cucumber/core/gherkin/writer.rb +220 -0
  34. data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
  35. data/lib/cucumber/core/platform.rb +30 -0
  36. data/lib/cucumber/core/test/case.rb +143 -0
  37. data/lib/cucumber/core/test/filters.rb +48 -0
  38. data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
  39. data/lib/cucumber/core/test/hook_compiler.rb +109 -0
  40. data/lib/cucumber/core/test/mapper.rb +56 -0
  41. data/lib/cucumber/core/test/mapping.rb +67 -0
  42. data/lib/cucumber/core/test/result.rb +191 -0
  43. data/lib/cucumber/core/test/runner.rb +149 -0
  44. data/lib/cucumber/core/test/step.rb +69 -0
  45. data/lib/cucumber/core/test/timer.rb +31 -0
  46. data/lib/cucumber/core/version.rb +9 -0
  47. data/lib/cucumber/initializer.rb +18 -0
  48. data/spec/capture_warnings.rb +68 -0
  49. data/spec/coverage.rb +10 -0
  50. data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
  51. data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
  52. data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
  53. data/spec/cucumber/core/ast/location_spec.rb +105 -0
  54. data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
  55. data/spec/cucumber/core/ast/step_spec.rb +44 -0
  56. data/spec/cucumber/core/compiler_spec.rb +249 -0
  57. data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
  58. data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
  59. data/spec/cucumber/core/test/case_spec.rb +416 -0
  60. data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
  61. data/spec/cucumber/core/test/mapper_spec.rb +68 -0
  62. data/spec/cucumber/core/test/mapping_spec.rb +103 -0
  63. data/spec/cucumber/core/test/result_spec.rb +178 -0
  64. data/spec/cucumber/core/test/runner_spec.rb +265 -0
  65. data/spec/cucumber/core/test/step_spec.rb +58 -0
  66. data/spec/cucumber/core/test/timer_spec.rb +13 -0
  67. data/spec/cucumber/core_spec.rb +419 -0
  68. data/spec/cucumber/initializer_spec.rb +49 -0
  69. 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,9 @@
1
+ module Cucumber
2
+ module Core
3
+ class Version
4
+ def self.to_s
5
+ "0.1.0"
6
+ end
7
+ end
8
+ end
9
+ 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
@@ -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