chop 0.36.6 → 0.38.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: a4d477e3610880a15f5ab315ad6c2f3a212f28f443147e363edc79508c7316b8
4
- data.tar.gz: c0cdf43b5a9415e21216d1ed4babddee3a141d9651bf593d6ffac484b1d1140c
3
+ metadata.gz: 0daeb281cda901500a0d95a2aee5ce2362322a930c36a395440093b6dcd41e5b
4
+ data.tar.gz: 73f5b4b2bc8ded47e68210efa01785842d8444770c68430a049aeb32e8f6d98f
5
5
  SHA512:
6
- metadata.gz: e3c86c9f999bdb1c9b2cbb8c721affa85008ae8cd65947311d182bdd1c5923d0aeedd14f4e2e75a6820c211a7e477a599e18628c2230c26dc6ef75530728d6ec
7
- data.tar.gz: e51e524727d4a813a0e19756fdc9c82a389e2e30404eac8de13fd7f512fd4edf218ca96787b5c9a63e56e2dc2557115add2851cb2d408adfc0c6399de35e4185
6
+ metadata.gz: 375fba97039e7e2ad8f63ea1c111e66d0499193e458311c909497851115a8759f2ac1259df90a0fe5886748d73c75b040a48261170196d72295b694e2604d467
7
+ data.tar.gz: c95e301a217166638a1c6129334b59abc62f1b8950dd6317e7b9c2cf8c7223bceffba18db6aa91308682ac98c6b74d02aedba60ba59bc04db7a8a12cc2b355d6
data/README.md CHANGED
@@ -26,6 +26,7 @@ All these methods accept blocks for customization.
26
26
  ### Configuration methods for `Chop`:
27
27
 
28
28
  * `.register_creation_strategy`: Provide a key and a block to register alternate strategies for `.create!`, e.g. FactoryGirl.
29
+ * `.atomic_diff=`: When set to `true`, `#diff!` captures the root element's HTML in a single read and parses it locally with Nokogiri, avoiding multiple CDP round-trips that can cause stale element errors during DOM morphs (e.g. Turbo). Off by default. Can be overridden per-call with the `atomic:` keyword argument.
29
30
 
30
31
  ### Block methods for `#create!`:
31
32
 
@@ -55,7 +56,10 @@ Lifecycle hooks:
55
56
 
56
57
  Transform the table of Capybara nodes before converting them to text and passing to `diff!`.
57
58
 
58
- Overide Capybara finders:
59
+ Options (keyword arguments):
60
+ * `atomic: true/false`: Override the global `Chop.atomic_diff` setting for this call. Example: `table.diff! "table", atomic: true`
61
+
62
+ Override Capybara finders:
59
63
  * `#rows`: overrides existing default row finder.
60
64
  * `#cells`: overrides existing default cell finder.
61
65
  * `#text`: overrides existing default text finder.
@@ -163,7 +167,7 @@ end
163
167
 
164
168
  Given "the following stories exist:" do |table|
165
169
  table.create! factory_girl: "conversation_table/story" do
166
- belongs_to :industry, ConversationTable::Industry
170
+ belongs_to :industry # class is inferred from association reflection
167
171
 
168
172
  rename :image => :image_file
169
173
  file :image_file
data/chop.gemspec CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency "cucumber"
24
24
  spec.add_dependency "capybara"
25
25
 
26
+ spec.add_development_dependency "sqlite3", "~> 1.4"
26
27
  spec.add_development_dependency "appraisal"
27
28
  spec.add_development_dependency "rake"
28
29
  spec.add_development_dependency "rspec"
data/lib/chop/config.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  module Chop
2
2
  module Config
3
+ attr_accessor :atomic_diff
4
+
3
5
  def register_creation_strategy *args, &block
4
6
  Chop::Create.register_creation_strategy *args, &block
5
7
  end
6
8
  end
9
+
10
+ extend Config
7
11
  end
data/lib/chop/create.rb CHANGED
@@ -159,7 +159,7 @@ module Chop
159
159
 
160
160
  def has_many key, klass=nil, delimiter: ", ", field: :name, find_by: nil
161
161
  field = find_by if find_by
162
- klass ||= key.to_s.classify.constantize
162
+ klass ||= class_from_association(key)
163
163
  self.field key do |names|
164
164
  names.split(delimiter).map do |name|
165
165
  klass.find_by!(field => name)
@@ -169,7 +169,7 @@ module Chop
169
169
 
170
170
  def has_one key, klass=nil, field: :name, find_by: nil
171
171
  field = find_by if find_by
172
- klass ||= key.to_s.classify.constantize
172
+ klass ||= class_from_association(key)
173
173
  self.field key do |name|
174
174
  klass.find_by!(field => name) if name.present?
175
175
  end
@@ -206,6 +206,12 @@ module Chop
206
206
  keys.replace keys.first.values
207
207
  end
208
208
  end
209
+
210
+ def class_from_association(key)
211
+ if self[:klass].respond_to?(:reflect_on_association)
212
+ self[:klass].reflect_on_association(key)&.klass
213
+ end || key.to_s.classify.constantize
214
+ end
209
215
  end
210
216
  end
211
217
 
data/lib/chop/diff.rb CHANGED
@@ -2,15 +2,18 @@ require "active_support/core_ext/string/inflections"
2
2
  require "active_support/core_ext/object/blank"
3
3
  require "active_support/core_ext/class/attribute"
4
4
  require "active_support/hash_with_indifferent_access"
5
+ require "chop/config"
5
6
  require "chop/regex_templates"
6
7
 
7
8
  module Chop
8
9
  class Diff < Struct.new(:selector, :table, :session, :timeout, :block)
9
- def self.diff! selector, table, session: Capybara.current_session, timeout: Capybara.default_max_wait_time, errors: [], **kwargs, &block
10
+ def self.diff! selector, table, session: Capybara.current_session, timeout: Capybara.default_max_wait_time, atomic: Chop.atomic_diff, errors: [], **kwargs, &block
10
11
  errors += session.driver.invalid_element_errors
11
12
  errors += [Cucumber::MultilineArgument::DataTable::Different]
12
13
  session.document.synchronize timeout, errors: errors do
13
- new(selector, table, session, timeout, block).diff! **kwargs
14
+ instance = new(selector, table, session, timeout, block)
15
+ instance.instance_variable_set(:@atomic, atomic)
16
+ instance.diff! **kwargs
14
17
  end
15
18
  end
16
19
 
@@ -145,7 +148,10 @@ module Chop
145
148
  end
146
149
 
147
150
  rows.map do |row|
148
- row.map { |cell| text_finder.call(cell) }
151
+ row.map do |cell|
152
+ text = text_finder.call(cell)
153
+ @atomic && text.is_a?(String) ? normalize_atomic_text(text) : text
154
+ end
149
155
  end
150
156
  end
151
157
 
@@ -172,13 +178,31 @@ module Chop
172
178
  end
173
179
  end
174
180
 
181
+ # Normalize text from a Nokogiri snapshot to match what Capybara drivers
182
+ # return from visible_text. Replicates Capybara::Node::WhitespaceNormalizer#normalize_spacing.
183
+ def normalize_atomic_text(text)
184
+ text
185
+ .delete("\u200b\u200e\u200f")
186
+ .tr(" \n\f\t\v\u2028\u2029", " ")
187
+ .squeeze(" ")
188
+ .sub(/\A[[:space:]&&[^\u00a0]]+/, "")
189
+ .sub(/[[:space:]&&[^\u00a0]]+\z/, "")
190
+ .tr("\u00a0", " ")
191
+ end
192
+
175
193
  def root
176
194
  @root ||= begin
177
- if selector.is_a?(Capybara::Node::Element)
195
+ element = if selector.is_a?(Capybara::Node::Element)
178
196
  selector
179
197
  else
180
198
  session.find(selector, wait: timeout)
181
199
  end
200
+ if @atomic
201
+ html = element["outerHTML"] || element.native.to_html
202
+ Node(html)
203
+ else
204
+ element
205
+ end
182
206
  rescue Capybara::ElementNotFound
183
207
  raise unless @allow_not_found
184
208
  Node("")
data/lib/chop/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chop
2
- VERSION = "0.36.6"
2
+ VERSION = "0.38.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.36.6
4
+ version: 0.38.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Geisel
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-03 00:00:00.000000000 Z
11
+ date: 2026-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: appraisal
57
71
  requirement: !ruby/object:Gem::Requirement