cucumber_factory 2.1.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: baa05dc176d4705c737b2ad4f7257f01f0da4d0e07aae0513acc8248c7577550
4
- data.tar.gz: 68ff25b1099b7eabb458e60e9cb95e8edb8ec7051befdd2c493353b7c23ef0f1
3
+ metadata.gz: 95567d71e09afa59f6db5bb2722ce78eb234b473987025ca3f66b6e181cdac1e
4
+ data.tar.gz: 7b1e046852657282cb2d5b65b714d12bb65c848196c0d6c70c13f3ce231fc684
5
5
  SHA512:
6
- metadata.gz: 4ca06816ddcc668384556e40eef5466bb231dc9c4c458bb277f7296fd715d32e73d70af414d2e8c2cc8a9e5bd6157d59ec99c9e93ca737c5a2b015bfb1c82970
7
- data.tar.gz: 293eabcf22252aca840ca43800d24f9e75a5a4cbf20aa6efbcfa52051782e2f3c5c6286166fc44c56decaad247972d7d487cc0fe08f7661527512ce2c80da7d9
6
+ metadata.gz: '083f2675e4b6592ab9ee117b5080fefdeaa06f27abba833f3145b176834f37baf3e40f4ef74e90b9d91558f1034b50c7c6250d2675caeda98d52b8a794d27142'
7
+ data.tar.gz: 392e4121dc98584c1928f2e028964780f079a4a0f74d437fb6595dc6d2b9ecbbb090e9eb97f0361f9fa1059c633fb8a9de8f357d986ad0a3bd5e193e79c20bae
@@ -15,6 +15,23 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
15
15
 
16
16
  -
17
17
 
18
+ ## 2.2.0 - 2020-09-23
19
+
20
+ ### Compatible changes
21
+
22
+ - A step was added that allows modifying existing records with a similar syntax to creating new records:
23
+ ```cucumber
24
+ (Given "Bob" is a user)
25
+ And "Bob" has the email "foo@bar.com" and is subscribed
26
+ ```
27
+ - This step will also work with doc strings or tables:
28
+ ```cucumber
29
+ (Given "Bob" is a user)
30
+ And the user above has these attributes:
31
+ | name | Bob |
32
+ | email | foo@bar.com |
33
+ ```
34
+
18
35
  ## 2.1.1 - 2020-05-20
19
36
 
20
37
  ### Compatible changes
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cucumber_factory (2.1.1)
4
+ cucumber_factory (2.2.0)
5
5
  activerecord
6
6
  activesupport
7
7
  cucumber
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cucumber_factory (2.1.1)
4
+ cucumber_factory (2.2.0)
5
5
  activerecord
6
6
  activesupport
7
7
  cucumber
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cucumber_factory (2.1.1)
4
+ cucumber_factory (2.2.0)
5
5
  activerecord
6
6
  activesupport
7
7
  cucumber
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cucumber_factory (2.1.1)
4
+ cucumber_factory (2.2.0)
5
5
  activerecord
6
6
  activesupport
7
7
  cucumber
data/README.md CHANGED
@@ -24,6 +24,13 @@ Quoted strings and numbers denote attribute values:
24
24
  Given there is a movie with the title "Sunshine" and the year 2007
25
25
  ```
26
26
 
27
+ To update an existing record, specify the record and the changes:
28
+ ```
29
+ Given the movie above has the title "Sunset" and the year 2008
30
+ Given the movie "Sunrise" has the year 2009
31
+ ```
32
+ A record can be specified by the `above` keyword, which uses the last created record of this class, or by any string that was used during its creation.
33
+
27
34
  Setting boolean attributes
28
35
  --------------------------
29
36
 
@@ -42,6 +49,13 @@ Given there is a movie which is awesome, popular and successful but not science
42
49
  And there is a director with the income "500000" but with the account balance "-30000"
43
50
  ```
44
51
 
52
+ To update boolean attributes use the keyword `is`:
53
+
54
+ ```cucumber
55
+ Given the movie above is awesome but not popular
56
+ Given the movie above has the year 1979 but is not science fiction
57
+ ```
58
+
45
59
 
46
60
  Setting many attributes with a table
47
61
  ------------------------------------
@@ -62,6 +76,14 @@ Given there is a movie with these attributes:
62
76
  | comedy | false |
63
77
  ```
64
78
 
79
+ ```cucumber
80
+ Given the movie above has these attributes:
81
+ """
82
+ name: Sunshine
83
+ comedy: false
84
+ """
85
+ ```
86
+
65
87
  Setting associations
66
88
  --------------------
67
89
 
@@ -13,6 +13,7 @@ require 'cucumber_priority'
13
13
 
14
14
  # Gem
15
15
  require 'cucumber_factory/build_strategy'
16
+ require 'cucumber_factory/update_strategy'
16
17
  require 'cucumber_factory/factory'
17
18
  require 'cucumber_factory/switcher'
18
19
 
@@ -22,6 +22,10 @@ module CucumberFactory
22
22
  [strategy, transient_attributes]
23
23
  end
24
24
 
25
+ def parse_model_class(model_prose)
26
+ underscored_model_name(model_prose).camelize.constantize
27
+ end
28
+
25
29
  private
26
30
 
27
31
  def variants_from_prose(variant_prose)
@@ -85,7 +89,7 @@ module CucumberFactory
85
89
  end
86
90
 
87
91
  def alternative_strategy(model_prose, variants)
88
- model_class = underscored_model_name(model_prose).camelize.constantize
92
+ model_class = parse_model_class(model_prose)
89
93
  machinist_strategy(model_class, variants) ||
90
94
  active_record_strategy(model_class) ||
91
95
  ruby_object_strategy(model_class)
@@ -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,6 +42,12 @@ 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
53
  :pattern => /^#{NAMED_RECORD_PATTERN}#{ATTRIBUTES_PATTERN}#{TEXT_ATTRIBUTES_PATTERN}?$/,
@@ -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,8 +118,24 @@ 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, transient_attributes = 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})/
@@ -140,33 +174,19 @@ module CucumberFactory
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
180
  def attribute_value(world, model_class, transient_attributes, attribute, value)
149
181
  associated, association_class = resolve_association(attribute, model_class, transient_attributes)
150
182
 
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, transient_attributes, attribute, v) }
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) }
154
186
  elsif associated
155
- if matches_fully?(value, VALUE_LAST_RECORD)
156
- raise(Error, "Cannot set last #{model_class}##{attribute} for polymorphic associations") unless association_class.present?
157
-
158
- value = CucumberFactory::Switcher.find_last(association_class) || raise(Error, "There is no last #{attribute}")
159
- elsif matches_fully?(value, VALUE_STRING)
160
- value = unquote(value)
161
- value = get_named_record(world, value) || transform_value(world, value)
162
- elsif matches_fully?(value, VALUE_INTEGER)
163
- value = value.to_s
164
- value = get_named_record(world, value) || transform_value(world, value)
165
- else
166
- raise Error, "Cannot set association #{model_class}##{attribute} to #{value}."
167
- end
187
+ resolve_associated_value(world, model_class, association_class, attribute, value)
168
188
  else
169
- value = resolve_scalar_value(world, model_class, attribute, value)
189
+ resolve_scalar_value(world, model_class, attribute, value)
170
190
  end
171
191
  value
172
192
  end
@@ -192,6 +212,22 @@ module CucumberFactory
192
212
  [associated, association_class]
193
213
  end
194
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
+
195
231
  def resolve_scalar_value(world, model_class, attribute, value)
196
232
  if matches_fully?(value, VALUE_STRING)
197
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.1.1'
2
+ VERSION = '2.2.0'
3
3
  end
@@ -111,7 +111,7 @@ describe 'steps provided by cucumber_factory' do
111
111
  payment.id.should == 2
112
112
  end
113
113
 
114
- it "should allow to name records and set a belongs_to association to that record by refering to that name" do
114
+ it "should allow to name records and set a belongs_to association to that record by referring to that name" do
115
115
  invoke_cucumber_step('"Some Prequel" is a movie with the title "Before Sunrise"')
116
116
  invoke_cucumber_step('there is a movie with the title "Limitless"')
117
117
  invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Some Prequel"')
@@ -120,7 +120,7 @@ describe 'steps provided by cucumber_factory' do
120
120
  movie.prequel.should == prequel
121
121
  end
122
122
 
123
- it "should allow to set a belongs_to association to a previously created record by refering to any string attribute of that record" do
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
124
  invoke_cucumber_step('there is a movie with the title "Before Sunrise"')
125
125
  invoke_cucumber_step('there is a movie with the title "Limitless"')
126
126
  invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel "Before Sunrise"')
@@ -129,7 +129,17 @@ describe 'steps provided by cucumber_factory' do
129
129
  movie.prequel.should == prequel
130
130
  end
131
131
 
132
- it "should allow to set a belongs_to association to a previously created record by refering to their explicitely set primary keys" do
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
133
143
  invoke_cucumber_step('there is a movie with the ID 123')
134
144
  invoke_cucumber_step('there is a movie with the title "Before Sunset" and the prequel 123')
135
145
  movie = Movie.find_by_title!('Before Sunset')
@@ -200,6 +210,15 @@ describe 'steps provided by cucumber_factory' do
200
210
  user.deleted.should == true
201
211
  end
202
212
 
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
221
+
203
222
  it "should allow to set positive boolean attributes with 'which' after the attribute list" do
204
223
  user = User.new
205
224
  User.stub(:new => user)
@@ -259,7 +278,7 @@ describe 'steps provided by cucumber_factory' do
259
278
  user.deleted.should == true
260
279
  end
261
280
 
262
- it "should allow to set a has_many association by refering to multiple named records in square brackets" do
281
+ it "should allow to set a has_many association by referring to multiple named records in square brackets" do
263
282
  invoke_cucumber_step('there is a movie with the title "Sunshine"')
264
283
  invoke_cucumber_step('there is a movie with the title "Limitless"')
265
284
  invoke_cucumber_step('there is a user with the reviewed movies ["Sunshine" and "Limitless"]')
@@ -536,5 +555,54 @@ tags: ["foo", "bar"]
536
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 ".')
537
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 ".".')
538
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
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
539
607
  end
540
608
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cucumber_factory
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-20 00:00:00.000000000 Z
11
+ date: 2020-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber
@@ -97,6 +97,7 @@ files:
97
97
  - lib/cucumber_factory/build_strategy.rb
98
98
  - lib/cucumber_factory/factory.rb
99
99
  - lib/cucumber_factory/switcher.rb
100
+ - lib/cucumber_factory/update_strategy.rb
100
101
  - lib/cucumber_factory/version.rb
101
102
  - spec/cucumber_factory/factory/build_strategy_spec.rb
102
103
  - spec/cucumber_factory/steps_spec.rb