cucumber_factory 2.1.1 → 2.2.0

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