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.
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