cucumber_factory 2.0.0 → 2.2.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.
@@ -2,11 +2,14 @@ module CucumberFactory
2
2
  module Factory
3
3
  class Error < StandardError; end
4
4
 
5
- ATTRIBUTES_PATTERN = '( with the .+?)?( (?:which|who|that) is .+?)?'
5
+ ATTRIBUTES_PATTERN = '( with the .+?)?( (?:which|who|that) is .+?)?' # ... with the year 1979 which is science fiction
6
6
  TEXT_ATTRIBUTES_PATTERN = ' (?:with|and) these attributes:'
7
+ UPDATE_ATTR_PATTERN = '(?: (?:has|belongs to)( the .+?))?(?:(?: and| but|,)*( is .+?))?' # ... belongs to the collection "Fantasy" and is trending
8
+ TEXT_UPDATE_ATTR_PATTERN = '(?: and|,)* has these attributes:'
7
9
 
8
- RECORD_PATTERN = 'there is an? (.+?)( \(.+?\))?'
9
- NAMED_RECORD_PATTERN = '(?:"([^\"]*)"|\'([^\']*)\') is an? (.+?)( \(.+?\))?'
10
+ RECORD_PATTERN = 'there is an? (.+?)( \(.+?\))?' # Given there is a movie (comedy)
11
+ NAMED_RECORD_PATTERN = '(?:"([^\"]*)"|\'([^\']*)\') is an? (.+?)( \(.+?\))?' # Given "LotR" is a movie
12
+ RECORD_UPDATE_PATTERN = 'the (.+?) (above|".+?"|\'.+?\')' # Given the movie "LotR" ...
10
13
 
11
14
  NAMED_RECORDS_VARIABLE = :'@named_cucumber_factory_records'
12
15
 
@@ -39,9 +42,15 @@ module CucumberFactory
39
42
  :block => lambda { |a1, a2, a3, a4| CucumberFactory::Factory.send(:parse_creation, self, a1, a2, a3, a4) }
40
43
  }
41
44
 
45
+ UPDATE_STEP_DESCRIPTOR = {
46
+ :kind => :And,
47
+ :pattern => /^#{RECORD_UPDATE_PATTERN}#{UPDATE_ATTR_PATTERN}$/,
48
+ :block => lambda { |a1, a2, a3, a4| CucumberFactory::Factory.send(:parse_update, self, a1, a2, a3, a4) }
49
+ }
50
+
42
51
  NAMED_CREATION_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES = {
43
52
  :kind => :Given,
44
- :pattern => /^"#{NAMED_RECORD_PATTERN}#{ATTRIBUTES_PATTERN}#{TEXT_ATTRIBUTES_PATTERN}?$/,
53
+ :pattern => /^#{NAMED_RECORD_PATTERN}#{ATTRIBUTES_PATTERN}#{TEXT_ATTRIBUTES_PATTERN}?$/,
45
54
  :block => lambda { |a1, a2, a3, a4, a5, a6, a7| CucumberFactory::Factory.send(:parse_named_creation, self, a1, a2, a3, a4, a5, a6, a7) },
46
55
  :priority => true
47
56
  }
@@ -53,6 +62,13 @@ module CucumberFactory
53
62
  :priority => true
54
63
  }
55
64
 
65
+ UPDATE_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES = {
66
+ :kind => :And,
67
+ :pattern => /^#{RECORD_UPDATE_PATTERN}#{UPDATE_ATTR_PATTERN}#{TEXT_UPDATE_ATTR_PATTERN}$/,
68
+ :block => lambda { |a1, a2, a3, a4, a5| CucumberFactory::Factory.send(:parse_update, self, a1, a2, a3, a4, a5) },
69
+ :priority => true
70
+ }
71
+
56
72
  class << self
57
73
 
58
74
  def add_steps(main)
@@ -61,6 +77,8 @@ module CucumberFactory
61
77
  add_step(main, CREATION_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES)
62
78
  add_step(main, NAMED_CREATION_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES)
63
79
  add_step(main, CLEAR_NAMED_RECORDS_STEP_DESCRIPTOR)
80
+ add_step(main, UPDATE_STEP_DESCRIPTOR)
81
+ add_step(main, UPDATE_STEP_DESCRIPTOR_WITH_TEXT_ATTRIBUTES)
64
82
  end
65
83
 
66
84
  private
@@ -100,15 +118,31 @@ module CucumberFactory
100
118
  end
101
119
 
102
120
  def parse_creation(world, raw_model, raw_variant, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil)
103
- build_strategy = BuildStrategy.from_prose(raw_model, raw_variant)
121
+ build_strategy, transient_attributes = CucumberFactory::BuildStrategy.from_prose(raw_model, raw_variant)
104
122
  model_class = build_strategy.model_class
123
+ attributes = parse_attributes(world, model_class, raw_attributes, raw_boolean_attributes, raw_multiline_attributes, transient_attributes)
124
+ record = build_strategy.create_record(attributes)
125
+ remember_record_names(world, record, attributes)
126
+ record
127
+ end
128
+
129
+ def parse_update(world, raw_model, raw_name, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil)
130
+ model_class = CucumberFactory::BuildStrategy.parse_model_class(raw_model)
131
+ attributes = parse_attributes(world, model_class, raw_attributes, raw_boolean_attributes, raw_multiline_attributes)
132
+ record = resolve_associated_value(world, model_class, model_class, model_class, raw_name)
133
+ CucumberFactory::UpdateStrategy.new(record).assign_attributes(attributes)
134
+ remember_record_names(world, record, attributes)
135
+ record
136
+ end
137
+
138
+ def parse_attributes(world, model_class, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil, transient_attributes = [])
105
139
  attributes = {}
106
140
  if raw_attributes.try(:strip).present?
107
141
  raw_attribute_fragment_regex = /(?:the |and |with |but |,| )+(.*?) (#{VALUE_SCALAR}|#{VALUE_ARRAY}|#{VALUE_LAST_RECORD})/
108
142
  raw_attributes.scan(raw_attribute_fragment_regex).each do |fragment|
109
143
  attribute = attribute_name_from_prose(fragment[0])
110
144
  value = fragment[1]
111
- attributes[attribute] = attribute_value(world, model_class, attribute, value)
145
+ attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
112
146
  end
113
147
  unused_raw_attributes = raw_attributes.gsub(raw_attribute_fragment_regex, '')
114
148
  if unused_raw_attributes.present?
@@ -129,46 +163,71 @@ module CucumberFactory
129
163
  raw_attribute, value = fragment.split(': ')
130
164
  attribute = attribute_name_from_prose(raw_attribute)
131
165
  value = "\"#{value}\"" unless matches_fully?(value, VALUE_ARRAY)
132
- attributes[attribute] = attribute_value(world, model_class, attribute, value)
166
+ attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
133
167
  end
134
168
  # DataTable e.g. in raw [["first name", "Jane"], ["last name", "Jenny"]]
135
169
  else
136
170
  raw_multiline_attributes.raw.each do |raw_attribute, value|
137
171
  attribute = attribute_name_from_prose(raw_attribute)
138
172
  value = "\"#{value}\"" unless matches_fully?(value, VALUE_ARRAY)
139
- attributes[attribute] = attribute_value(world, model_class, attribute, value)
173
+ attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
140
174
  end
141
175
  end
142
176
  end
143
- record = build_strategy.create_record(attributes)
144
- remember_record_names(world, record, attributes)
145
- record
177
+ attributes
146
178
  end
147
179
 
148
- def attribute_value(world, model_class, attribute, value)
149
- association = model_class.respond_to?(:reflect_on_association) ? model_class.reflect_on_association(attribute) : nil
150
-
151
- if matches_fully?(value, VALUE_ARRAY)
152
- elements_str = unquote(value)
153
- value = elements_str.scan(VALUE_SCALAR).map { |v| attribute_value(world, model_class, attribute, v) }
154
- elsif association.present?
155
- if matches_fully?(value, VALUE_LAST_RECORD)
156
- value = CucumberFactory::Switcher.find_last(association.klass) or raise Error, "There is no last #{attribute}"
157
- elsif matches_fully?(value, VALUE_STRING)
158
- value = unquote(value)
159
- value = get_named_record(world, value) || transform_value(world, value)
160
- elsif matches_fully?(value, VALUE_INTEGER)
161
- value = value.to_s
162
- value = get_named_record(world, value) || transform_value(world, value)
163
- else
164
- raise Error, "Cannot set association #{model_class}##{attribute} to #{value}."
165
- end
180
+ def attribute_value(world, model_class, transient_attributes, attribute, value)
181
+ associated, association_class = resolve_association(attribute, model_class, transient_attributes)
182
+
183
+ value = if matches_fully?(value, VALUE_ARRAY)
184
+ array_values = unquote(value).scan(VALUE_SCALAR)
185
+ array_values.map { |v| attribute_value(world, model_class, transient_attributes, attribute, v) }
186
+ elsif associated
187
+ resolve_associated_value(world, model_class, association_class, attribute, value)
166
188
  else
167
- value = resolve_scalar_value(world, model_class, attribute, value)
189
+ resolve_scalar_value(world, model_class, attribute, value)
168
190
  end
169
191
  value
170
192
  end
171
193
 
194
+ def resolve_association(attribute, model_class, transient_attributes)
195
+ return unless model_class.respond_to?(:reflect_on_association)
196
+
197
+ association = model_class.reflect_on_association(attribute)
198
+ association_class = nil
199
+
200
+ if association
201
+ association_class = association.klass unless association.polymorphic?
202
+ associated = true
203
+ elsif transient_attributes.include?(attribute.to_sym)
204
+ klass_name = attribute.to_s.camelize
205
+ if Object.const_defined?(klass_name)
206
+ association_class = klass_name.constantize
207
+ associated = true
208
+ end
209
+ else
210
+ associated = false
211
+ end
212
+ [associated, association_class]
213
+ end
214
+
215
+ def resolve_associated_value(world, model_class, association_class, attribute, value)
216
+ if matches_fully?(value, VALUE_LAST_RECORD)
217
+ raise(Error, "Cannot set last #{model_class}##{attribute} for polymorphic associations") unless association_class.present?
218
+
219
+ CucumberFactory::Switcher.find_last(association_class) || raise(Error, "There is no last #{attribute}")
220
+ elsif matches_fully?(value, VALUE_STRING)
221
+ value = unquote(value)
222
+ get_named_record(world, value) || transform_value(world, value)
223
+ elsif matches_fully?(value, VALUE_INTEGER)
224
+ value = value.to_s
225
+ get_named_record(world, value) || transform_value(world, value)
226
+ else
227
+ raise Error, "Cannot set association #{model_class}##{attribute} to #{value}."
228
+ end
229
+ end
230
+
172
231
  def resolve_scalar_value(world, model_class, attribute, value)
173
232
  if matches_fully?(value, VALUE_STRING)
174
233
  value = unquote(value)
@@ -0,0 +1,30 @@
1
+ module CucumberFactory
2
+
3
+ class UpdateStrategy
4
+
5
+ def initialize(record)
6
+ @record = record
7
+ end
8
+
9
+ def assign_attributes(attributes)
10
+ active_record_strategy(attributes) ||
11
+ ruby_object_strategy(attributes)
12
+ end
13
+
14
+ private
15
+
16
+ def active_record_strategy(attributes)
17
+ return unless @record.respond_to?(:save!)
18
+
19
+ CucumberFactory::Switcher.assign_attributes(@record, attributes)
20
+ @record.save!
21
+ end
22
+
23
+ def ruby_object_strategy(attributes)
24
+ attributes.each do |name, value|
25
+ @record.send("#{name}=".to_sym, value)
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module CucumberFactory
2
- VERSION = '2.0.0'
2
+ VERSION = '2.2.0'
3
3
  end
@@ -2,47 +2,45 @@ require 'spec_helper'
2
2
 
3
3
  describe CucumberFactory::BuildStrategy do
4
4
 
5
- subject { CucumberFactory::BuildStrategy }
6
-
7
5
  # most of the behaviour is integration tested in steps_spec.rb
8
6
 
9
7
  describe '.from_prose' do
10
8
 
11
9
  context 'when describing a factory_bot factory' do
12
-
13
- it 'returns a strategy corresponding to the factories model' do
14
- FactoryBot.stub_factories :job_offer => JobOffer
15
- strategy = subject.from_prose('job offer', nil)
10
+ it 'returns a strategy and transient attributes corresponding to the factories model' do
11
+ strategy, transient_attributes = described_class.from_prose('job offer', nil)
16
12
 
17
13
  strategy.should be_a(described_class)
18
14
  strategy.model_class.should == JobOffer
15
+ transient_attributes.should == [:my_transient_attribute]
19
16
  end
20
17
 
21
18
  it 'uses the variant for the factory name if present' do
22
- FactoryBot.stub_factories :job_offer => JobOffer
23
- strategy = subject.from_prose('foo', '(job offer)')
19
+ strategy, transient_attributes = described_class.from_prose('job offer', '(tempting_job_offer)')
24
20
 
25
21
  strategy.should be_a(described_class)
26
22
  strategy.model_class.should == JobOffer
23
+ transient_attributes.should == [:my_transient_attribute, :other_transient_attribute]
27
24
  end
28
-
29
25
  end
30
26
 
31
27
  context 'when describing a non factory_bot model' do
28
+ before do
29
+ hide_const("FactoryBot")
30
+ end
32
31
 
33
32
  it "should return a strategy for the class matching a natural language expression" do
34
- subject.from_prose("movie", nil).model_class.should == Movie
35
- subject.from_prose("job offer", nil).model_class.should == JobOffer
33
+ described_class.from_prose("movie", nil).first.model_class.should == Movie
34
+ described_class.from_prose("job offer", nil).first.model_class.should == JobOffer
36
35
  end
37
36
 
38
37
  it "should ignore variants for the class name" do
39
- subject.from_prose("movie", "(job offer)").model_class.should == Movie
38
+ described_class.from_prose("movie", "(job offer)").first.model_class.should == Movie
40
39
  end
41
40
 
42
41
  it "should allow namespaced models" do
43
- subject.from_prose("people/actor", nil).model_class.should == People::Actor
42
+ described_class.from_prose("people/actor", nil).first.model_class.should == People::Actor
44
43
  end
45
-
46
44
  end
47
45
 
48
46
  end
@@ -1,468 +1,608 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'steps provided by cucumber_factory' do
3
+ TRANSFORMS_SUPPORTED = Cucumber::VERSION < '3'
4
4
 
5
+ describe 'steps provided by cucumber_factory' do
5
6
  before(:each) do
6
7
  prepare_cucumber_example
7
8
  end
8
9
 
9
- TRANSFORMS_SUPPORTED = Cucumber::VERSION < '3'
10
+ context 'FactoryBot' do
11
+ it "should create ActiveRecord models by calling #new and #save!" do
12
+ movie = Movie.new
13
+ Movie.should_receive(:new).with(no_args).and_return(movie)
14
+ movie.should_receive(:save!)
15
+ invoke_cucumber_step("there is a movie")
16
+ end
10
17
 
11
- it "should create ActiveRecord models by calling #new and #save!" do
12
- movie = Movie.new
13
- Movie.should_receive(:new).with(no_args).and_return(movie)
14
- movie.should_receive(:save!)
15
- invoke_cucumber_step("there is a movie")
16
- end
18
+ it "should create models that have a factory_bot factory by calling #FactoryBot.create(:model_name)" do
19
+ FactoryBot.should_receive(:create).with(:job_offer, { :title => "Awesome job" })
20
+ invoke_cucumber_step('there is a job offer with the title "Awesome job"')
21
+ end
17
22
 
18
- it "should create models that have a machinist blueprint by calling #make" do
19
- MachinistModel.should_receive(:make).with({ :attribute => "foo"})
20
- invoke_cucumber_step('there is a machinist model with the attribute "foo"')
21
- end
23
+ it "should create model variants that have a factory_bot factory by calling #FactoryBot.create(:variant)" do
24
+ FactoryBot.should_receive(:create).with(:job_offer, :tempting_job_offer, { :title => "Awesomafiablyfantasmic job" })
25
+ invoke_cucumber_step('there is a job offer (tempting job offer) with the title "Awesomafiablyfantasmic job"')
26
+ end
22
27
 
23
- it "should be able to step_match machinist blueprint variants" do
24
- MachinistModel.should_receive(:make).with(:variant, { :attribute => "foo"})
25
- invoke_cucumber_step('there is a machinist model (variant) with the attribute "foo"')
26
- end
28
+ it "should create model variants that have a factory_bot trait by calling #FactoryBot.create(:factory, :trait1, :trait2)" do
29
+ FactoryBot.should_receive(:create).with(:job_offer, :risky, :lucrative, { :title => "Awesomafiablyfantasmic job" })
30
+ invoke_cucumber_step('there is a job offer (risky, lucrative) with the title "Awesomafiablyfantasmic job"')
31
+ end
27
32
 
28
- it "should be able to step_match machinist blueprint variants containing spaces or uppercase characters in prose" do
29
- MachinistModel.should_receive(:make).with(:variant_mark_two, { :attribute => "foo"})
30
- invoke_cucumber_step('there is a machinist model (Variant Mark Two) with the attribute "foo"')
31
- end
33
+ it "should create model variants that have a factory_bot factory by using the model name as a factory name" do
34
+ FactoryBot.should_receive(:create).with(:job_offer, { :title => "Awesomafiablyfantasmic job" })
35
+ invoke_cucumber_step('there is a job offer with the title "Awesomafiablyfantasmic job"')
36
+ end
32
37
 
33
- it "should create models that have a factory_bot factory by calling #FactoryBot.create(:model_name)" do
34
- FactoryBot.stub_factories :job_offer => JobOffer
35
- FactoryBot.should_receive(:create).with(:job_offer, { :title => "Awesome job" })
36
- invoke_cucumber_step('there is a job offer with the title "Awesome job"')
37
- end
38
+ it "should instantiate classes with multiple words in their name" do
39
+ job_offer = JobOffer.new
40
+ JobOffer.should_receive(:new).with(no_args).and_return(job_offer)
41
+ invoke_cucumber_step("there is a job offer")
42
+ end
38
43
 
39
- it "should create model variants that have a factory_bot factory by calling #FactoryBot.create(:variant)" do
40
- FactoryBot.stub_factories :tempting_job_offer => JobOffer
41
- FactoryBot.should_receive(:create).with(:tempting_job_offer, { :title => "Awesomafiablyfantasmic job" })
42
- invoke_cucumber_step('there is a job offer (tempting job offer) with the title "Awesomafiablyfantasmic job"')
43
- end
44
+ it "should instantiate classes with uppercase characters in their name" do
45
+ user = User.new
46
+ User.should_receive(:new).and_return(user)
47
+ invoke_cucumber_step("there is a User")
48
+ end
44
49
 
45
- it "should create model variants that have a factory_bot trait by calling #FactoryBot.create(:factory, :trait1, :trait2)" do
46
- FactoryBot.stub_factories :tempting_job_offer => JobOffer
47
- FactoryBot.should_receive(:create).with(:tempting_job_offer, :risky, :lucrative, { :title => "Awesomafiablyfantasmic job" })
48
- invoke_cucumber_step('there is a tempting job offer (risky, lucrative) with the title "Awesomafiablyfantasmic job"')
49
- end
50
+ it "should allow either 'a' or 'an' for the article" do
51
+ opera = Opera.new
52
+ Opera.should_receive(:new).with(no_args).and_return(opera)
53
+ invoke_cucumber_step("there is an opera")
54
+ end
50
55
 
51
- it "should create model variants that have a factory_bot factory by using the model name as a factory name" do
52
- FactoryBot.stub_factories :tempting_job_offer => JobOffer
53
- FactoryBot.should_receive(:create).with(:tempting_job_offer, { :title => "Awesomafiablyfantasmic job" })
54
- invoke_cucumber_step('there is a tempting job offer with the title "Awesomafiablyfantasmic job"')
55
- end
56
+ it "should create records with attributes" do
57
+ movie = Movie.new
58
+ Movie.stub(:new => movie)
59
+ invoke_cucumber_step('there is a movie with the title "Sunshine" and the year "2007"')
60
+ movie.title.should == "Sunshine"
61
+ movie.year.should == 2007
62
+ end
56
63
 
57
- it "should instantiate plain ruby classes by calling #new" do
58
- PlainRubyClass.should_receive(:new).with({})
59
- invoke_cucumber_step("there is a plain ruby class")
60
- end
64
+ it "should allow to join attribute lists with 'and's, commas and 'but's" do
65
+ movie = Movie.new
66
+ Movie.stub(:new => movie)
67
+ invoke_cucumber_step('there is a movie with the title "Sunshine", the year "2007" but with the box office result "32000000"')
68
+ movie.title.should == "Sunshine"
69
+ movie.year.should == 2007
70
+ movie.box_office_result.should == 32000000
71
+ end
61
72
 
62
- it "should instantiate classes with multiple words in their name" do
63
- JobOffer.should_receive(:new).with({})
64
- invoke_cucumber_step("there is a job offer")
65
- end
73
+ if TRANSFORMS_SUPPORTED
74
+ it "should apply Cucumber transforms to attribute values" do
75
+ movie = Movie.new
76
+ Movie.stub(:new => movie)
77
+ @main.instance_eval do
78
+ Transform /^(value)$/ do |value|
79
+ 'transformed value'
80
+ end
81
+ end
82
+ invoke_cucumber_step('there is a movie with the title "value"')
83
+ movie.title.should == "transformed value"
84
+ end
85
+ end
66
86
 
67
- it "should instantiate classes with uppercase characters in their name" do
68
- user = User.new
69
- User.should_receive(:new).and_return(user)
70
- invoke_cucumber_step("there is a User")
71
- end
87
+ it "should create records with attributes containing spaces" do
88
+ movie = Movie.new
89
+ Movie.stub(:new => movie)
90
+ invoke_cucumber_step('there is a movie with the box office result "99999999"')
91
+ movie.box_office_result.should == 99999999
92
+ end
72
93
 
73
- it "should instantiate namespaced classes" do
74
- actor = People::Actor.new
75
- People::Actor.should_receive(:new).and_return(actor)
76
- invoke_cucumber_step("there is a people/actor")
77
- end
94
+ it "should create records with attributes containing uppercase characters" do
95
+ user = User.new
96
+ User.stub(:new => user)
97
+ invoke_cucumber_step('there is a User with the Name "Susanne"')
98
+ user.name.should == "Susanne"
99
+ end
78
100
 
79
- it "should allow either 'a' or 'an' for the article" do
80
- Opera.should_receive(:new).with({})
81
- invoke_cucumber_step("there is an opera")
82
- end
101
+ it "should override attr_accessible protection" do
102
+ invoke_cucumber_step('there is a payment with the amount "120" and the comment "Thanks for lending"')
103
+ payment = Payment.last
104
+ payment.amount.should == 120
105
+ payment.comment.should == 'Thanks for lending'
106
+ end
83
107
 
84
- it "should create records with attributes" do
85
- movie = Movie.new
86
- Movie.stub(:new => movie)
87
- invoke_cucumber_step('there is a movie with the title "Sunshine" and the year "2007"')
88
- movie.title.should == "Sunshine"
89
- movie.year.should == 2007
90
- end
108
+ it "should allow to set an explicit primary key" do
109
+ invoke_cucumber_step('there is a payment with the ID 2')
110
+ payment = Payment.last
111
+ payment.id.should == 2
112
+ end
91
113
 
92
- it "should allow to join attribute lists with 'and's, commas and 'but's" do
93
- movie = Movie.new
94
- Movie.stub(:new => movie)
95
- invoke_cucumber_step('there is a movie with the title "Sunshine", the year "2007" but with the box office result "32000000"')
96
- movie.title.should == "Sunshine"
97
- movie.year.should == 2007
98
- movie.box_office_result.should == 32000000
99
- end
114
+ it "should allow to name records and set a belongs_to association to that record by referring to that name" do
115
+ invoke_cucumber_step('"Some Prequel" is a movie with the title "Before Sunrise"')
116
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
117
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Some Prequel"')
118
+ movie = Movie.find_by_title!('Before Sunset')
119
+ prequel = Movie.find_by_title!('Before Sunrise')
120
+ movie.prequel.should == prequel
121
+ end
100
122
 
101
- if TRANSFORMS_SUPPORTED
102
- it "should apply Cucumber transforms to attribute values" do
103
- movie = Movie.new
104
- Movie.stub(:new => movie)
105
- @main.instance_eval do
106
- Transform /^(value)$/ do |value|
107
- 'transformed value'
123
+ it "should allow to set a belongs_to association to a previously created record by referring to any string attribute of that record" do
124
+ invoke_cucumber_step('there is a movie with the title "Before Sunrise"')
125
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
126
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Before Sunrise"')
127
+ movie = Movie.find_by_title!('Before Sunset')
128
+ prequel = Movie.find_by_title!('Before Sunrise')
129
+ movie.prequel.should == prequel
130
+ end
131
+
132
+ it "should allow to set a belongs_to association to a previously updated record by referring to any string attribute used when updating" do
133
+ invoke_cucumber_step('there is a movie')
134
+ invoke_cucumber_step('the movie above has the title "Before Sunrise"')
135
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
136
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Before Sunrise"')
137
+ movie = Movie.find_by_title!('Before Sunset')
138
+ prequel = Movie.find_by_title!('Before Sunrise')
139
+ movie.prequel.should == prequel
140
+ end
141
+
142
+ it "should allow to set a belongs_to association to a previously created record by referring to their explicitely set primary keys" do
143
+ invoke_cucumber_step('there is a movie with the ID 123')
144
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel 123')
145
+ movie = Movie.find_by_title!('Before Sunset')
146
+ prequel = Movie.find(123)
147
+ movie.prequel.should == prequel
148
+ end
149
+
150
+ it "should allow to set a belongs_to association to a previously created record by saying 'above'" do
151
+ invoke_cucumber_step('there is a user with the name "Jane"')
152
+ invoke_cucumber_step('there is a user with the name "John"')
153
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
154
+ invoke_cucumber_step('there is a movie with the title "Before Sunrise"')
155
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer above and the prequel above')
156
+ before_sunset = Movie.find_by_title!("Before Sunset")
157
+ before_sunset.prequel.title.should == "Before Sunrise"
158
+ before_sunset.reviewer.name.should == "John"
159
+ end
160
+
161
+ if TRANSFORMS_SUPPORTED
162
+ it "should fallback to using transforms when no named record is found" do
163
+ user = User.create!(:name => 'Me')
164
+ @main.instance_eval do
165
+ Transform(/^(me)$/) do |value|
166
+ user
167
+ end
108
168
  end
169
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer "me"')
170
+ before_sunset = Movie.find_by_title!("Before Sunset")
171
+ before_sunset.reviewer.should == user
109
172
  end
110
- invoke_cucumber_step('there is a movie with the title "value"')
111
- movie.title.should == "transformed value"
112
173
  end
113
- end
114
174
 
115
- it "should create records with attributes containing spaces" do
116
- movie = Movie.new
117
- Movie.stub(:new => movie)
118
- invoke_cucumber_step('there is a movie with the box office result "99999999"')
119
- movie.box_office_result.should == 99999999
120
- end
175
+ it "should give created_at precedence over id when saying 'above' if the primary key is not numeric" do
176
+ invoke_cucumber_step('there is a uuid user with the name "Jane" and the id "jane"')
177
+ invoke_cucumber_step('there is a uuid user with the name "John" and the id "john"')
178
+ UuidUser.find_by_name("John").update_attributes!(:created_at => 1.day.ago)
179
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the uuid reviewer above')
180
+ before_sunset = Movie.find_by_title!("Before Sunset")
181
+ before_sunset.uuid_reviewer.name.should == "Jane"
182
+ end
121
183
 
122
- it "should create records with attributes containing uppercase characters" do
123
- user = User.new
124
- User.stub(:new => user)
125
- invoke_cucumber_step('there is a User with the Name "Susanne"')
126
- user.name.should == "Susanne"
127
- end
184
+ it "should ignore created_at if the primary key is numeric" do
185
+ invoke_cucumber_step('there is a user with the name "Jane"')
186
+ invoke_cucumber_step('there is a user with the name "John"')
187
+ User.find_by_name("John").update_attributes!(:created_at => 1.day.ago)
188
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer above')
189
+ before_sunset = Movie.find_by_title!("Before Sunset")
190
+ before_sunset.reviewer.name.should == "John"
191
+ end
128
192
 
129
- it "should override attr_accessible protection" do
130
- invoke_cucumber_step('there is a payment with the amount "120" and the comment "Thanks for lending"')
131
- payment = Payment.last
132
- payment.amount.should == 120
133
- payment.comment.should == 'Thanks for lending'
134
- end
193
+ it "should raise a proper error if there is no previous record when saying 'above'" do
194
+ lambda do
195
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer above and the prequel above')
196
+ end.should raise_error(/There is no last reviewer/i)
197
+ end
135
198
 
136
- it "should allow to set an explicit primary key" do
137
- invoke_cucumber_step('there is a payment with the ID 2')
138
- payment = Payment.last
139
- payment.id.should == 2
140
- end
199
+ it "should reload an object assigned to a belongs_to before assigning" do
200
+ invoke_cucumber_step('"Jane" is a user who is deleted')
201
+ User.last.update_attributes(:deleted => false)
202
+ proc { invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer "Jane"') }.should_not raise_error
203
+ end
141
204
 
142
- it "should allow to name records and set a belongs_to association to that record by refering to that name" do
143
- invoke_cucumber_step('"Some Prequel" is a movie with the title "Before Sunrise"')
144
- invoke_cucumber_step('there is a movie with the title "Limitless"')
145
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Some Prequel"')
146
- movie = Movie.find_by_title!('Before Sunset')
147
- prequel = Movie.find_by_title!('Before Sunrise')
148
- movie.prequel.should == prequel
149
- end
205
+ it "should allow to set positive boolean attributes with 'who' after the attribute list" do
206
+ user = User.new
207
+ User.stub(:new => user)
208
+ invoke_cucumber_step('there is a user with the name "Jane" who is deleted')
209
+ user.name.should == "Jane"
210
+ user.deleted.should == true
211
+ end
150
212
 
151
- it "should allow to set a belongs_to association to a previously created record by refering to any string attribute of that record" do
152
- invoke_cucumber_step('there is a movie with the title "Before Sunrise"')
153
- invoke_cucumber_step('there is a movie with the title "Limitless"')
154
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Before Sunrise"')
155
- movie = Movie.find_by_title!('Before Sunset')
156
- prequel = Movie.find_by_title!('Before Sunrise')
157
- movie.prequel.should == prequel
158
- end
213
+ it "should allow to name records and set a belongs_to association to that record by referring to that name" do
214
+ invoke_cucumber_step('"Some Prequel" is a movie with the title "Before Sunrise"')
215
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
216
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Some Prequel"')
217
+ movie = Movie.find_by_title!('Before Sunset')
218
+ prequel = Movie.find_by_title!('Before Sunrise')
219
+ movie.prequel.should == prequel
220
+ end
159
221
 
160
- it "should allow to set a belongs_to association to a previously created record by refering to their explicitely set primary keys" do
161
- invoke_cucumber_step('there is a movie with the ID 123')
162
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel 123')
163
- movie = Movie.find_by_title!('Before Sunset')
164
- prequel = Movie.find(123)
165
- movie.prequel.should == prequel
166
- end
222
+ it "should allow to set positive boolean attributes with 'which' after the attribute list" do
223
+ user = User.new
224
+ User.stub(:new => user)
225
+ invoke_cucumber_step('there is a user with the name "Jane" which is deleted')
226
+ user.name.should == "Jane"
227
+ user.deleted.should == true
228
+ end
167
229
 
168
- it "should allow to set a belongs_to association to a previously created record by saying 'above'" do
169
- invoke_cucumber_step('there is a user with the name "Jane"')
170
- invoke_cucumber_step('there is a user with the name "John"')
171
- invoke_cucumber_step('there is a movie with the title "Limitless"')
172
- invoke_cucumber_step('there is a movie with the title "Before Sunrise"')
173
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer above and the prequel above')
174
- before_sunset = Movie.find_by_title!("Before Sunset")
175
- before_sunset.prequel.title.should == "Before Sunrise"
176
- before_sunset.reviewer.name.should == "John"
177
- end
230
+ it "should allow to set positive boolean attributes with 'that' after the attribute list" do
231
+ user = User.new
232
+ User.stub(:new => user)
233
+ invoke_cucumber_step('there is a user with the name "Jane" that is deleted')
234
+ user.name.should == "Jane"
235
+ user.deleted.should == true
236
+ end
178
237
 
179
- if TRANSFORMS_SUPPORTED
180
- it "should fallback to using transforms when no named record is found" do
181
- user = User.create!(:name => 'Me')
182
- @main.instance_eval do
183
- Transform(/^(me)$/) do |value|
184
- user
185
- end
186
- end
187
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer "me"')
188
- before_sunset = Movie.find_by_title!("Before Sunset")
189
- before_sunset.reviewer.should == user
238
+ it "should allow to set boolean attributes without regular attributes preceding them" do
239
+ user = User.new
240
+ User.stub(:new => user)
241
+ invoke_cucumber_step('there is a user who is deleted')
242
+ user.deleted.should == true
190
243
  end
191
- end
192
244
 
193
- it "should give created_at precedence over id when saying 'above' if the primary key is not numeric" do
194
- invoke_cucumber_step('there is a uuid user with the name "Jane" and the id "jane"')
195
- invoke_cucumber_step('there is a uuid user with the name "John" and the id "john"')
196
- UuidUser.find_by_name("John").update_attributes!(:created_at => 1.day.ago)
197
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the uuid reviewer above')
198
- before_sunset = Movie.find_by_title!("Before Sunset")
199
- before_sunset.uuid_reviewer.name.should == "Jane"
200
- end
245
+ it "should allow to set negative boolean attribute" do
246
+ user = User.new
247
+ User.stub(:new => user)
248
+ invoke_cucumber_step('there is a user who is not deleted')
249
+ user.deleted.should == false
250
+ end
201
251
 
202
- it "should ignore created_at if the primary key is numeric" do
203
- invoke_cucumber_step('there is a user with the name "Jane"')
204
- invoke_cucumber_step('there is a user with the name "John"')
205
- User.find_by_name("John").update_attributes!(:created_at => 1.day.ago)
206
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer above')
207
- before_sunset = Movie.find_by_title!("Before Sunset")
208
- before_sunset.reviewer.name.should == "John"
209
- end
252
+ it "should allow to set multiple boolean attributes" do
253
+ user = User.new
254
+ User.stub(:new => user)
255
+ invoke_cucumber_step('there is a user who is locked and not deleted and subscribed')
256
+ user.locked.should == true
257
+ user.deleted.should == false
258
+ user.subscribed.should == true
259
+ end
210
260
 
211
- it "should raise a proper error if there is no previous record when saying 'above'" do
212
- lambda do
213
- invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer above and the prequel above')
214
- end.should raise_error(/There is no last reviewer/i)
215
- end
261
+ it "should allow to set boolean attributes that are named from multiple words" do
262
+ user = User.new
263
+ User.stub(:new => user)
264
+ invoke_cucumber_step('there is a user who is locked and not scared and scared by spiders and deleted')
265
+ user.locked.should == true
266
+ user.scared.should == false
267
+ user.scared_by_spiders.should == true
268
+ user.deleted.should == true
269
+ end
216
270
 
217
- it "should reload an object assigned to a belongs_to before assigning" do
218
- invoke_cucumber_step('"Jane" is a user who is deleted')
219
- User.last.update_attributes(:deleted => false)
220
- proc { invoke_cucumber_step('there is a movie with the title "Before Sunset" and the reviewer "Jane"') }.should_not raise_error
221
- end
271
+ it "should allow to join boolean attribute lists with 'and's, commas and 'but's" do
272
+ user = User.new
273
+ User.stub(:new => user)
274
+ invoke_cucumber_step('there is a user who is locked, scared, but scared by spiders and deleted')
275
+ user.locked.should == true
276
+ user.scared.should == true
277
+ user.scared_by_spiders.should == true
278
+ user.deleted.should == true
279
+ end
222
280
 
223
- it "should allow to set positive boolean attributes with 'who' after the attribute list" do
224
- user = User.new
225
- User.stub(:new => user)
226
- invoke_cucumber_step('there is a user with the name "Jane" who is deleted')
227
- user.name.should == "Jane"
228
- user.deleted.should == true
229
- end
281
+ it "should allow to set a has_many association by referring to multiple named records in square brackets" do
282
+ invoke_cucumber_step('there is a movie with the title "Sunshine"')
283
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
284
+ invoke_cucumber_step('there is a user with the reviewed movies ["Sunshine" and "Limitless"]')
285
+ user = User.last
286
+ reviewed_movie_titles = user.reviewed_movies.map(&:title)
287
+ reviewed_movie_titles.should =~ ['Sunshine', 'Limitless']
288
+ end
230
289
 
231
- it "should allow to set positive boolean attributes with 'which' after the attribute list" do
232
- user = User.new
233
- User.stub(:new => user)
234
- invoke_cucumber_step('there is a user with the name "Jane" which is deleted')
235
- user.name.should == "Jane"
236
- user.deleted.should == true
237
- end
290
+ it 'allow associations for transient attributes if they are named after the associated model' do
291
+ invoke_cucumber_step('there is a movie with the title "Sunshine"')
292
+ invoke_cucumber_step('there is a user with the movie "Sunshine"')
293
+ user = User.last
294
+ user.reviewed_movies.count.should == 1
295
+ user.reviewed_movies.first.title.should == 'Sunshine'
296
+ end
238
297
 
239
- it "should allow to set positive boolean attributes with 'that' after the attribute list" do
240
- user = User.new
241
- User.stub(:new => user)
242
- invoke_cucumber_step('there is a user with the name "Jane" that is deleted')
243
- user.name.should == "Jane"
244
- user.deleted.should == true
245
- end
298
+ it "should allow to set attributes via doc string" do
299
+ user = User.new
300
+ User.stub(:new => user)
246
301
 
247
- it "should allow to set boolean attributes without regular attributes preceding them" do
248
- user = User.new
249
- User.stub(:new => user)
250
- invoke_cucumber_step('there is a user who is deleted')
251
- user.deleted.should == true
252
- end
302
+ invoke_cucumber_step('there is a user with these attributes:', <<-DOC_STRING)
303
+ name: Jane
304
+ locked: true
305
+ DOC_STRING
253
306
 
254
- it "should allow to set negative boolean attribute" do
255
- user = User.new
256
- User.stub(:new => user)
257
- invoke_cucumber_step('there is a user who is not deleted')
258
- user.deleted.should == false
259
- end
307
+ user.name.should == "Jane"
308
+ user.locked.should == true
309
+ end
260
310
 
261
- it "should allow to set multiple boolean attributes" do
262
- user = User.new
263
- User.stub(:new => user)
264
- invoke_cucumber_step('there is a user who is locked and not deleted and subscribed')
265
- user.locked.should == true
266
- user.deleted.should == false
267
- user.subscribed.should == true
268
- end
311
+ it "should allow to set attributes via additional doc string" do
312
+ user = User.new
313
+ User.stub(:new => user)
269
314
 
270
- it "should allow to set boolean attributes that are named from multiple words" do
271
- user = User.new
272
- User.stub(:new => user)
273
- invoke_cucumber_step('there is a user who is locked and not scared and scared by spiders and deleted')
274
- user.locked.should == true
275
- user.scared.should == false
276
- user.scared_by_spiders.should == true
277
- user.deleted.should == true
278
- end
315
+ invoke_cucumber_step('there is a user with the email "test@invalid.com" and these attributes:', <<-DOC_STRING)
316
+ name: Jane
317
+ DOC_STRING
279
318
 
280
- it "should allow to join boolean attribute lists with 'and's, commas and 'but's" do
281
- user = User.new
282
- User.stub(:new => user)
283
- invoke_cucumber_step('there is a user who is locked, scared, but scared by spiders and deleted')
284
- user.locked.should == true
285
- user.scared.should == true
286
- user.scared_by_spiders.should == true
287
- user.deleted.should == true
288
- end
319
+ user.name.should == "Jane"
320
+ user.email.should == "test@invalid.com"
321
+ end
289
322
 
290
- it "should allow to set integer attributes without surrounding quotes" do
291
- invoke_cucumber_step('there is a plain Ruby class with the amount 123 and the total 456')
292
- obj = PlainRubyClass.last
293
- obj.attributes[:amount].should == 123
294
- obj.attributes[:total].should == 456
295
- end
323
+ it 'should allow named records when setting attributes via doc string' do
324
+ invoke_cucumber_step('"Some Prequel" is a movie with these attributes:', <<-DOC_STRING)
325
+ title: Before Sunrise
326
+ DOC_STRING
327
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
328
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Some Prequel"')
329
+ movie = Movie.find_by_title!('Before Sunset')
330
+ prequel = Movie.find_by_title!('Before Sunrise')
331
+ movie.prequel.should == prequel
332
+ end
296
333
 
297
- it "should allow to set decimal attributes without surrounding quotes" do
298
- invoke_cucumber_step('there is a plain Ruby class with the amount 1.23 and the total 45.6')
299
- obj = PlainRubyClass.last
300
- obj.attributes[:amount].should be_a(BigDecimal)
301
- obj.attributes[:amount].to_s.should == "1.23"
302
- obj.attributes[:total].should be_a(BigDecimal)
303
- obj.attributes[:total].to_s.should == "45.6"
304
- end
334
+ it "should allow to set attributes via data table" do
335
+ user = User.new
336
+ User.stub(:new => user)
305
337
 
306
- it "should allow set an array of strings with square brackets" do
307
- invoke_cucumber_step('there is a plain Ruby class with the tags ["foo", "bar"] and the list ["bam", "baz"]')
308
- obj = PlainRubyClass.last
309
- obj.attributes[:tags].should == ['foo', 'bar']
310
- obj.attributes[:list].should == ['bam', 'baz']
311
- end
338
+ invoke_cucumber_step('there is a user with these attributes:', nil, <<-DATA_TABLE)
339
+ | name | Jane |
340
+ | locked | true |
341
+ DATA_TABLE
312
342
 
313
- it "should allow set an array of numbers with square brackets" do
314
- invoke_cucumber_step('there is a plain Ruby class with the integers [1, 2] and the decimals [3.4, 4.5]')
315
- obj = PlainRubyClass.last
316
- obj.attributes[:integers].should == [1, 2]
317
- obj.attributes[:decimals].should == [BigDecimal('3.4'), BigDecimal('4.5')]
318
- end
343
+ user.name.should == "Jane"
344
+ user.locked.should == true
345
+ end
319
346
 
320
- it 'should allow to set an empty array' do
321
- invoke_cucumber_step('there is a plain Ruby class with the tags []')
322
- obj = PlainRubyClass.last
323
- obj.attributes[:tags].should == []
324
- end
347
+ it "should allow to set attributes via additional data table" do
348
+ user = User.new
349
+ User.stub(:new => user)
325
350
 
326
- it 'should allow to separate array values with either a comma or "and"' do
327
- invoke_cucumber_step('there is a plain Ruby class with the tags ["foo", "bar" and "baz"] and the list ["bam", "baz" and "qux"]')
328
- obj = PlainRubyClass.last
329
- obj.attributes[:tags].should == ['foo', 'bar', 'baz']
330
- obj.attributes[:list].should == ['bam', 'baz', 'qux']
331
- end
351
+ invoke_cucumber_step('there is a user with the email "test@invalid.com" and these attributes:', nil, <<-DATA_TABLE)
352
+ | name | Jane |
353
+ DATA_TABLE
332
354
 
333
- it 'should allow to separate array values with an Oxford comma' do
334
- invoke_cucumber_step('there is a plain Ruby class with the tags ["foo", "bar", and "baz"] and the list ["bam", "baz", and "qux"]')
335
- obj = PlainRubyClass.last
336
- obj.attributes[:tags].should == ['foo', 'bar', 'baz']
337
- obj.attributes[:list].should == ['bam', 'baz', 'qux']
338
- end
355
+ user.name.should == "Jane"
356
+ user.email.should == "test@invalid.com"
357
+ end
339
358
 
340
- it "should allow to set a has_many association by refering to multiple named records in square brackets" do
341
- invoke_cucumber_step('there is a movie with the title "Sunshine"')
342
- invoke_cucumber_step('there is a movie with the title "Limitless"')
343
- invoke_cucumber_step('there is a user with the reviewed movies ["Sunshine" and "Limitless"]')
344
- user = User.last
345
- reviewed_movie_titles = user.reviewed_movies.map(&:title)
346
- reviewed_movie_titles.should =~ ['Sunshine', 'Limitless']
347
- end
359
+ it 'should allow named records when setting attributes via data table' do
360
+ invoke_cucumber_step('"Some Prequel" is a movie with these attributes:', nil, <<-DATA_TABLE)
361
+ | title | Before Sunrise |
362
+ DATA_TABLE
363
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
364
+ invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Some Prequel"')
365
+ movie = Movie.find_by_title!('Before Sunset')
366
+ prequel = Movie.find_by_title!('Before Sunrise')
367
+ movie.prequel.should == prequel
368
+ end
348
369
 
349
- it "should allow attribute names starting with 'the'" do
350
- PlainRubyClass.should_receive(:new).with({:theme => 'Sci-fi'})
351
- invoke_cucumber_step('there is a plain ruby class with the theme "Sci-fi"')
352
- end
370
+ it "should allow mixed single quotes for model names" do
371
+ invoke_cucumber_step("'Some Prequel' is a movie with the title \"Before Sunrise\"")
372
+ invoke_cucumber_step('there is a movie with the title "Limitless"')
373
+ invoke_cucumber_step('there is a movie with the title \'Before Sunset\' and the prequel "Some Prequel"')
374
+ movie = Movie.find_by_title!('Before Sunset')
375
+ prequel = Movie.find_by_title!('Before Sunrise')
376
+ movie.prequel.should == prequel
377
+ end
353
378
 
354
- it "should allow attribute names starting with 'and'" do
355
- PlainRubyClass.should_receive(:new).with({:android => 'Paranoid'})
356
- invoke_cucumber_step('there is a plain ruby class with the android "Paranoid"')
357
- end
379
+ it 'supports named associations with polymorphic associations' do
380
+ invoke_cucumber_step('"my opera" is an opera')
381
+ invoke_cucumber_step('there is a movie with the premiere site "my opera"')
382
+ end
358
383
 
359
- it "should allow attribute names starting with 'with'" do
360
- PlainRubyClass.should_receive(:new).with({:withdrawal => 'bank_account'})
361
- invoke_cucumber_step('there is a plain ruby class with the withdrawal "bank_account"')
362
- end
384
+ it 'does not support last record references with polymorphic associations as the target class cannot be guessed' do
385
+ invoke_cucumber_step('there is an opera')
386
+ expect {
387
+ invoke_cucumber_step('there is a movie with the premiere site above')
388
+ }.to raise_error(CucumberFactory::Factory::Error, 'Cannot set last Movie#premiere_site for polymorphic associations')
389
+ end
363
390
 
364
- it "should allow attribute names starting with 'but'" do
365
- PlainRubyClass.should_receive(:new).with({:butt => 'pear-shaped'})
366
- invoke_cucumber_step('there is a plain ruby class with the butt "pear-shaped"')
367
- end
391
+ it 'works with nested factories (BUGFIX)' do
392
+ invoke_cucumber_step('there is a subgenre movie')
393
+ end
368
394
 
369
- it "should allow to set attributes via doc string" do
370
- user = User.new
371
- User.stub(:new => user)
395
+ describe 'references to named records are tracked correctly' do
396
+ # CucumberFactory keeps track of all created records by their attributes.
397
+ # For example, if you create a user with the name "Peter" you can reference him later on as "Peter".
372
398
 
373
- invoke_cucumber_step('there is a user with these attributes:', <<-DOC_STRING)
374
- name: Jane
375
- locked: true
376
- DOC_STRING
399
+ it 'does not overwrite existing references when associating new records with another reference (BUGFIX)' do
400
+ invoke_cucumber_step('"reviewer" is a user with the id "1"')
401
+ movie1 = invoke_cucumber_step('there is a movie with the reviewer "reviewer" and the id "2"') # <- should not overwrite the 'reviewer' reference
402
+ movie2 = invoke_cucumber_step('there is a movie with the reviewer "reviewer" and the id "3"')
403
+
404
+ expect(movie1.reviewer_id).to eq(1)
405
+ expect(movie2.reviewer_id).to eq(1)
406
+ end
377
407
 
378
- user.name.should == "Jane"
379
- user.locked.should == true
408
+ it 'does not set new references when setting primary keys of an association (BUGFIX)' do
409
+ movie1 = invoke_cucumber_step('there is a movie with the id "123" and the user id "456"') # <- should not set a '456' reference
410
+ movie2 = invoke_cucumber_step('there is a movie with the user id "456"')
411
+
412
+ expect(movie1.reviewer_id).to eq(456)
413
+ expect(movie2.reviewer_id).to eq(456)
414
+ end
415
+ end
380
416
  end
381
417
 
382
- it "should allow to set attributes via additional doc string" do
383
- user = User.new
384
- User.stub(:new => user)
418
+ context 'without FactoryBot' do
419
+ before do
420
+ hide_const("FactoryBot")
421
+ end
385
422
 
386
- invoke_cucumber_step('there is a user with the email "test@invalid.com" and these attributes:', <<-DOC_STRING)
387
- name: Jane
388
- DOC_STRING
423
+ it "should instantiate plain ruby classes by calling #new" do
424
+ PlainRubyClass.should_receive(:new).with({})
425
+ invoke_cucumber_step("there is a plain ruby class")
426
+ end
389
427
 
390
- user.name.should == "Jane"
391
- user.email.should == "test@invalid.com"
392
- end
428
+ it "should instantiate namespaced classes" do
429
+ actor = People::Actor.new
430
+ People::Actor.should_receive(:new).and_return(actor)
431
+ invoke_cucumber_step("there is a people/actor")
432
+ end
393
433
 
394
- it "should allow to set array attributes via doc string" do
395
- invoke_cucumber_step('there is a plain Ruby class with these attributes:', <<-DOC_STRING)
396
- tags: ["foo", "bar"]
397
- DOC_STRING
434
+ it "should allow to set integer attributes without surrounding quotes" do
435
+ invoke_cucumber_step('there is a plain Ruby class with the amount 123 and the total 456')
436
+ obj = PlainRubyClass.last
437
+ obj.attributes[:amount].should == 123
438
+ obj.attributes[:total].should == 456
439
+ end
398
440
 
399
- obj = PlainRubyClass.last
400
- obj.attributes[:tags].should == ['foo', 'bar']
401
- end
441
+ it "should allow to set decimal attributes without surrounding quotes" do
442
+ invoke_cucumber_step('there is a plain Ruby class with the amount 1.23 and the total 45.6')
443
+ obj = PlainRubyClass.last
444
+ obj.attributes[:amount].should be_a(BigDecimal)
445
+ obj.attributes[:amount].to_s.should == "1.23"
446
+ obj.attributes[:total].should be_a(BigDecimal)
447
+ obj.attributes[:total].to_s.should == "45.6"
448
+ end
402
449
 
403
- it "should allow to set attributes via data table" do
404
- user = User.new
405
- User.stub(:new => user)
450
+ it "should allow set an array of strings with square brackets" do
451
+ invoke_cucumber_step('there is a plain Ruby class with the tags ["foo", "bar"] and the list ["bam", "baz"]')
452
+ obj = PlainRubyClass.last
453
+ obj.attributes[:tags].should == ['foo', 'bar']
454
+ obj.attributes[:list].should == ['bam', 'baz']
455
+ end
406
456
 
407
- invoke_cucumber_step('there is a user with these attributes:', nil, <<-DATA_TABLE)
408
- | name | Jane |
409
- | locked | true |
410
- DATA_TABLE
457
+ it "should allow set an array of numbers with square brackets" do
458
+ invoke_cucumber_step('there is a plain Ruby class with the integers [1, 2] and the decimals [3.4, 4.5]')
459
+ obj = PlainRubyClass.last
460
+ obj.attributes[:integers].should == [1, 2]
461
+ obj.attributes[:decimals].should == [BigDecimal('3.4'), BigDecimal('4.5')]
462
+ end
411
463
 
412
- user.name.should == "Jane"
413
- user.locked.should == true
414
- end
464
+ it 'should allow to set an empty array' do
465
+ invoke_cucumber_step('there is a plain Ruby class with the tags []')
466
+ obj = PlainRubyClass.last
467
+ obj.attributes[:tags].should == []
468
+ end
415
469
 
416
- it "should allow to set attributes via additional data table" do
417
- user = User.new
418
- User.stub(:new => user)
470
+ it 'should allow to separate array values with either a comma or "and"' do
471
+ invoke_cucumber_step('there is a plain Ruby class with the tags ["foo", "bar" and "baz"] and the list ["bam", "baz" and "qux"]')
472
+ obj = PlainRubyClass.last
473
+ obj.attributes[:tags].should == ['foo', 'bar', 'baz']
474
+ obj.attributes[:list].should == ['bam', 'baz', 'qux']
475
+ end
419
476
 
420
- invoke_cucumber_step('there is a user with the email "test@invalid.com" and these attributes:', nil, <<-DATA_TABLE)
421
- | name | Jane |
422
- DATA_TABLE
477
+ it 'should allow to separate array values with an Oxford comma' do
478
+ invoke_cucumber_step('there is a plain Ruby class with the tags ["foo", "bar", and "baz"] and the list ["bam", "baz", and "qux"]')
479
+ obj = PlainRubyClass.last
480
+ obj.attributes[:tags].should == ['foo', 'bar', 'baz']
481
+ obj.attributes[:list].should == ['bam', 'baz', 'qux']
482
+ end
423
483
 
424
- user.name.should == "Jane"
425
- user.email.should == "test@invalid.com"
426
- end
484
+ it "should allow attribute names starting with 'the'" do
485
+ PlainRubyClass.should_receive(:new).with({:theme => 'Sci-fi'})
486
+ invoke_cucumber_step('there is a plain ruby class with the theme "Sci-fi"')
487
+ end
488
+
489
+ it "should allow attribute names starting with 'and'" do
490
+ PlainRubyClass.should_receive(:new).with({:android => 'Paranoid'})
491
+ invoke_cucumber_step('there is a plain ruby class with the android "Paranoid"')
492
+ end
493
+
494
+ it "should allow attribute names starting with 'with'" do
495
+ PlainRubyClass.should_receive(:new).with({:withdrawal => 'bank_account'})
496
+ invoke_cucumber_step('there is a plain ruby class with the withdrawal "bank_account"')
497
+ end
427
498
 
428
- it "should allow to set array attributes via data table" do
429
- invoke_cucumber_step('there is a plain Ruby class with these attributes:', nil, <<-DATA_TABLE)
499
+ it "should allow attribute names starting with 'but'" do
500
+ PlainRubyClass.should_receive(:new).with({:butt => 'pear-shaped'})
501
+ invoke_cucumber_step('there is a plain ruby class with the butt "pear-shaped"')
502
+ end
503
+
504
+ it "should allow to set array attributes via doc string" do
505
+ invoke_cucumber_step('there is a plain Ruby class with these attributes:', <<-DOC_STRING)
506
+ tags: ["foo", "bar"]
507
+ DOC_STRING
508
+
509
+ obj = PlainRubyClass.last
510
+ obj.attributes[:tags].should == ['foo', 'bar']
511
+ end
512
+
513
+ it "should allow to set array attributes via data table" do
514
+ invoke_cucumber_step('there is a plain Ruby class with these attributes:', nil, <<-DATA_TABLE)
430
515
  | tags | ["foo", "bar"] |
431
- DATA_TABLE
516
+ DATA_TABLE
432
517
 
433
- obj = PlainRubyClass.last
434
- obj.attributes[:tags].should == ['foo', 'bar']
435
- end
518
+ obj = PlainRubyClass.last
519
+ obj.attributes[:tags].should == ['foo', 'bar']
520
+ end
436
521
 
437
- it "should allow single quote for attribute values" do
438
- MachinistModel.should_receive(:make).with({ :attribute => "foo"})
439
- invoke_cucumber_step("there is a machinist model with the attribute 'foo'")
440
- end
522
+ it "should create models that have a machinist blueprint by calling #make" do
523
+ MachinistModel.should_receive(:make).with({ :attribute => "foo"})
524
+ invoke_cucumber_step('there is a machinist model with the attribute "foo"')
525
+ end
441
526
 
442
- it "should allow mixed single and double quotes for different attribute values" do
443
- MachinistModel.should_receive(:make).with({ :attribute => "foo", :other_attribute => "bar" })
444
- invoke_cucumber_step("there is a machinist model with the attribute 'foo' and the other attribute \"bar\"")
445
- end
527
+ it "should be able to step_match machinist blueprint variants" do
528
+ MachinistModel.should_receive(:make).with(:variant, { :attribute => "foo"})
529
+ invoke_cucumber_step('there is a machinist model (variant) with the attribute "foo"')
530
+ end
446
531
 
447
- it "should allow mixed single quotes for model names" do
448
- invoke_cucumber_step("'Some Prequel' is a movie with the title \"Before Sunrise\"")
449
- invoke_cucumber_step('there is a movie with the title "Limitless"')
450
- invoke_cucumber_step('there is a movie with the title \'Before Sunset\' and the prequel "Some Prequel"')
451
- movie = Movie.find_by_title!('Before Sunset')
452
- prequel = Movie.find_by_title!('Before Sunrise')
453
- movie.prequel.should == prequel
454
- end
532
+ it "should be able to step_match machinist blueprint variants containing spaces or uppercase characters in prose" do
533
+ MachinistModel.should_receive(:make).with(:variant_mark_two, { :attribute => "foo"})
534
+ invoke_cucumber_step('there is a machinist model (Variant Mark Two) with the attribute "foo"')
535
+ end
455
536
 
456
- it 'should not raise an error for a blank instance name' do
457
- MachinistModel.should_receive(:make).with({ :attribute => 'foo' })
458
- invoke_cucumber_step("'' is a machinist model with the attribute 'foo'")
459
- end
537
+ it "should allow single quote for attribute values" do
538
+ MachinistModel.should_receive(:make).with({ :attribute => "foo"})
539
+ invoke_cucumber_step("there is a machinist model with the attribute 'foo'")
540
+ end
460
541
 
461
- it 'should warn if there are unused fragments' do
462
- MachinistModel.should_not_receive(:make)
463
- lambda { invoke_cucumber_step("there is a machinist model with the attribute NOQUOTES") }.should raise_error(ArgumentError, 'Unable to parse attributes " with the attribute NOQUOTES".')
464
- lambda { invoke_cucumber_step("there is a machinist model with the attribute 'foo' and the ") }.should raise_error(ArgumentError, 'Unable to parse attributes " and the ".')
465
- lambda { invoke_cucumber_step("there is a machinist model with the attribute 'foo'. the other_attribute 'bar' and the third attribute 'baz'") }.should raise_error(ArgumentError, 'Unable to parse attributes ".".')
466
- end
542
+ it "should allow mixed single and double quotes for different attribute values" do
543
+ MachinistModel.should_receive(:make).with({ :attribute => "foo", :other_attribute => "bar" })
544
+ invoke_cucumber_step("there is a machinist model with the attribute 'foo' and the other attribute \"bar\"")
545
+ end
546
+
547
+ it 'should not raise an error for a blank instance name' do
548
+ MachinistModel.should_receive(:make).with({ :attribute => 'foo' })
549
+ invoke_cucumber_step("'' is a machinist model with the attribute 'foo'")
550
+ end
551
+
552
+ it 'should warn if there are unused fragments' do
553
+ MachinistModel.should_not_receive(:make)
554
+ lambda { invoke_cucumber_step("there is a machinist model with the attribute NOQUOTES") }.should raise_error(ArgumentError, 'Unable to parse attributes " with the attribute NOQUOTES".')
555
+ lambda { invoke_cucumber_step("there is a machinist model with the attribute 'foo' and the ") }.should raise_error(ArgumentError, 'Unable to parse attributes " and the ".')
556
+ lambda { invoke_cucumber_step("there is a machinist model with the attribute 'foo'. the other_attribute 'bar' and the third attribute 'baz'") }.should raise_error(ArgumentError, 'Unable to parse attributes ".".')
557
+ end
558
+
559
+ it "should allow to update the last record of a type" do
560
+ invoke_cucumber_step('there is a user with the name "Bar" and the email "foo@example.com" who is not subscribed')
561
+ invoke_cucumber_step('the user above has the name "Baz", the email "foobar@example.com", and is subscribed')
562
+ user = User.last
563
+ user.name.should == 'Baz'
564
+ user.email.should == 'foobar@example.com'
565
+ user.subscribed.should == true
566
+ end
467
567
 
568
+ it "should be able to update a record with a given name" do
569
+ invoke_cucumber_step('"Foo" is a user with the name "Bar"')
570
+ invoke_cucumber_step('the user "Foo" has the email "foo@example.com"')
571
+ invoke_cucumber_step("the user 'Foo' is subscribed")
572
+ user = User.last
573
+ user.name.should == 'Bar'
574
+ user.email.should == 'foo@example.com'
575
+ user.subscribed.should == true
576
+ end
577
+
578
+ it "should be able to update a record with a Cucumber table expression" do
579
+ invoke_cucumber_step('"Foo" is a user with the name "Bar"')
580
+ invoke_cucumber_step('the user above has these attributes:', nil, <<-DATA_TABLE)
581
+ | name | Jane |
582
+ | locked | true |
583
+ DATA_TABLE
584
+ user = User.last
585
+ user.name.should == "Jane"
586
+ user.locked.should == true
587
+ end
588
+
589
+ it "should allow to update a record via additional data table" do
590
+ invoke_cucumber_step('"Foo" is a user with the name "Bar"')
591
+ invoke_cucumber_step('the user above has the email "test@invalid.com", is subscribed, and has these attributes:', nil, <<-DATA_TABLE)
592
+ | name | Jane |
593
+ DATA_TABLE
594
+ user = User.last
595
+ user.name.should == "Jane"
596
+ user.email.should == "test@invalid.com"
597
+ user.subscribed.should == true
598
+ end
599
+
600
+ it "should allow to update an association" do
601
+ invoke_cucumber_step('there is a movie with the title "Before Sunrise"')
602
+ invoke_cucumber_step('there is a user with the name "John"')
603
+ invoke_cucumber_step('the movie above has the reviewer above')
604
+ movie = Movie.last
605
+ movie.reviewer.name.should == "John"
606
+ end
607
+ end
468
608
  end