capybara-lockstep 1.1.1 → 1.2.1

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: 271ed22154f61a70a3961eb2bd2a8a7d29430dfb37107b9f1176257e8efab320
4
- data.tar.gz: 6ba0d7a5c7e8bff779ee2a9ac2d6d32829374041dff41d7bddd9cbd19627a468
3
+ metadata.gz: e9207a7671ab6f25999a138df922afb783ec3267257eadfab1b179f3c26eb88d
4
+ data.tar.gz: 2ddfe3a9a10143894afdc2eb2517ddeeaf76311e3365693606359282004a598d
5
5
  SHA512:
6
- metadata.gz: 24f098f75fc9093065cf4210eed7fab3b8f1ea69d49d6e328f195f92affe41c262fa92daecbf1ee8247dba8bcad1d3d5c0941766bd3889416be6aedf79f3e354
7
- data.tar.gz: ea3debf0f2e4178dbc200c84a98aae87a614914de889769d10ed1cc2e311f83e93de460e2b1a4c163b756dc331838164bfe7ae8f77f676833df93a984a2fda49
6
+ metadata.gz: dffa87e401d189b8afea6bb72b49bb14a6f9865d306fc7a838e50b4fb4dfc715fe8930502b9e3e2b4139c214b45eefe189c0ddb8e3e867d30d3021331ba76ea3
7
+ data.tar.gz: b311ee487b65ae42dc5dcc114cdd2aca2ff39ed49953fe43959ecf8578b3691740ebd2019aeae95fc87334cc5b87835f18b1f816ed2ad0a234e6475b549baad8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@ All notable changes to this project will be documented in this file.
2
2
 
3
3
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4
4
 
5
+
6
+ ## 1.2.1 - 2022-09-12
7
+
8
+ - Synchronize with pages constructed from non-empty [data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs)
9
+
10
+ ## 1.2.0 - 2022-09-12
11
+
12
+ ### Synchronization around history navigation
13
+
14
+ We now synchronize before and after history navigation using the following Capybara methods:
15
+
16
+ - `page.refresh`
17
+ - `page.go_back`
18
+ - `page.go_forward`
19
+
20
+ We also synchronize before `current_url` in case running a JavaScript task wants to update the URL when done.
21
+
22
+ ### Support for tests with multiple tabs or frames
23
+
24
+ capybara-lockstep now supports test that work with [multiple frames](https://makandracards.com/makandra/34015-use-capybara-commands-inside-an-iframe) or [multiple tabs or windows](https://github.com/teamcapybara/capybara#working-with-windows).
25
+ We now synchronize before and after the following Capybara methods:
26
+
27
+ - `switch_to_frame`
28
+ - `within_frame`
29
+ - `switch_to_window`
30
+ - `within_window`
31
+
32
+ ### Improved logging
33
+
34
+ - Only log when we're actually synchronizing
35
+ - Log the reason why we're synchronizing (e.g. before node access)
36
+ - Log which browser work we're waiting for (e.g. XHR request, image load)
37
+
38
+ ### Various changes
39
+
40
+ - Synchronize before accessing `page.html`.
41
+
42
+
5
43
  ## 1.1.1 - 2022-03-16
6
44
 
7
45
  - Activate rubygems MFA
data/Gemfile CHANGED
@@ -14,3 +14,5 @@ gem 'chrome_remote'
14
14
 
15
15
  gem 'byebug'
16
16
  gem 'gemika'
17
+
18
+ gem 'activesupport', '~> 6.0'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- capybara-lockstep (1.1.1)
4
+ capybara-lockstep (1.2.1)
5
5
  activesupport (>= 3.2)
6
6
  capybara (>= 2.0)
7
7
  ruby2_keywords
@@ -10,32 +10,33 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- activesupport (6.1.3.1)
13
+ activesupport (6.1.7)
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
15
  i18n (>= 1.6, < 2)
16
16
  minitest (>= 5.1)
17
17
  tzinfo (~> 2.0)
18
18
  zeitwerk (~> 2.3)
19
- addressable (2.7.0)
20
- public_suffix (>= 2.0.2, < 5.0)
19
+ addressable (2.8.1)
20
+ public_suffix (>= 2.0.2, < 6.0)
21
21
  byebug (11.1.3)
22
- capybara (3.35.3)
22
+ capybara (3.37.1)
23
23
  addressable
24
+ matrix
24
25
  mini_mime (>= 0.1.3)
25
26
  nokogiri (~> 1.8)
26
27
  rack (>= 1.6.0)
27
28
  rack-test (>= 0.6.3)
28
29
  regexp_parser (>= 1.5, < 3.0)
29
30
  xpath (~> 3.2)
30
- childprocess (3.0.0)
31
+ childprocess (4.1.0)
31
32
  chrome_remote (0.3.0)
32
33
  websocket-driver (~> 0.6)
33
- concurrent-ruby (1.1.8)
34
+ concurrent-ruby (1.1.10)
34
35
  daemons (1.3.1)
35
36
  diff-lcs (1.3)
36
37
  eventmachine (1.2.7)
37
38
  gemika (0.6.0)
38
- i18n (1.8.10)
39
+ i18n (1.12.0)
39
40
  concurrent-ruby (~> 1.0)
40
41
  jasmine (3.6.0)
41
42
  jasmine-core (~> 3.6.0)
@@ -43,18 +44,20 @@ GEM
43
44
  rack (>= 1.2.1)
44
45
  rake
45
46
  jasmine-core (3.6.0)
46
- mini_mime (1.1.0)
47
- minitest (5.14.4)
48
- nokogiri (1.11.3-x86_64-linux)
47
+ matrix (0.4.2)
48
+ mini_mime (1.1.2)
49
+ minitest (5.16.3)
50
+ nokogiri (1.13.8-x86_64-linux)
49
51
  racc (~> 1.4)
50
52
  phantomjs (2.1.1.0)
51
- public_suffix (4.0.6)
52
- racc (1.5.2)
53
+ public_suffix (5.0.0)
54
+ racc (1.6.0)
53
55
  rack (2.2.3)
54
- rack-test (1.1.0)
55
- rack (>= 1.0, < 3)
56
+ rack-test (2.0.2)
57
+ rack (>= 1.3)
56
58
  rake (13.0.1)
57
- regexp_parser (2.1.1)
59
+ regexp_parser (2.5.0)
60
+ rexml (3.2.5)
58
61
  rspec (3.7.0)
59
62
  rspec-core (~> 3.7.0)
60
63
  rspec-expectations (~> 3.7.0)
@@ -68,28 +71,32 @@ GEM
68
71
  diff-lcs (>= 1.2.0, < 2.0)
69
72
  rspec-support (~> 3.7.0)
70
73
  rspec-support (3.7.0)
71
- ruby2_keywords (0.0.4)
72
- rubyzip (2.3.0)
73
- selenium-webdriver (3.142.7)
74
- childprocess (>= 0.5, < 4.0)
75
- rubyzip (>= 1.2.2)
74
+ ruby2_keywords (0.0.5)
75
+ rubyzip (2.3.2)
76
+ selenium-webdriver (4.4.0)
77
+ childprocess (>= 0.5, < 5.0)
78
+ rexml (~> 3.2, >= 3.2.5)
79
+ rubyzip (>= 1.2.2, < 3.0)
80
+ websocket (~> 1.0)
76
81
  thin (1.8.0)
77
82
  daemons (~> 1.0, >= 1.0.9)
78
83
  eventmachine (~> 1.0, >= 1.0.4)
79
84
  rack (>= 1, < 3)
80
- tzinfo (2.0.4)
85
+ tzinfo (2.0.5)
81
86
  concurrent-ruby (~> 1.0)
87
+ websocket (1.2.9)
82
88
  websocket-driver (0.7.3)
83
89
  websocket-extensions (>= 0.1.0)
84
90
  websocket-extensions (0.1.5)
85
91
  xpath (3.2.0)
86
92
  nokogiri (~> 1.8)
87
- zeitwerk (2.4.2)
93
+ zeitwerk (2.6.0)
88
94
 
89
95
  PLATFORMS
90
96
  ruby
91
97
 
92
98
  DEPENDENCIES
99
+ activesupport (~> 6.0)
93
100
  byebug
94
101
  capybara-lockstep!
95
102
  chrome_remote
@@ -100,4 +107,4 @@ DEPENDENCIES
100
107
  thin
101
108
 
102
109
  BUNDLED WITH
103
- 2.2.15
110
+ 2.2.32
data/README.md CHANGED
@@ -309,7 +309,7 @@ end
309
309
 
310
310
  In the `:manual` mode you may still force synchronization by calling `Capybara::Lockstep.synchronize` manually.
311
311
 
312
- To completely disable synchronization:
312
+ To completely disable synchronization, even when `Capybara::Lockstep.synchronize` is called:
313
313
 
314
314
  ```ruby
315
315
  Capybara::Lockstep.mode = :off
@@ -1,34 +1,99 @@
1
1
  require 'ruby2_keywords'
2
2
 
3
+ module Capybara
4
+ module Lockstep
5
+ module UnsychronizeAfter
6
+ def unsychronize_after(meth)
7
+ mod = Module.new do
8
+ define_method meth do |*args, &block|
9
+ super(*args, &block)
10
+ ensure
11
+ Lockstep.synchronized = false
12
+ end
13
+
14
+ ruby2_keywords meth
15
+ end
16
+
17
+ prepend(mod)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ module Capybara
24
+ module Lockstep
25
+ module SynchronizeBefore
26
+ def synchronize_before(meth, lazy:)
27
+ mod = Module.new do
28
+ define_method meth do |*args, &block|
29
+ Lockstep.auto_synchronize(lazy: lazy, log: "Synchronizing before ##{meth}")
30
+ super(*args, &block)
31
+ end
32
+
33
+ ruby2_keywords meth
34
+ end
35
+
36
+ prepend(mod)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ Capybara::Session.class_eval do
43
+ extend Capybara::Lockstep::SynchronizeBefore
44
+ extend Capybara::Lockstep::UnsychronizeAfter
45
+
46
+ synchronize_before :html, lazy: true # wait until running JavaScript has updated the DOM
47
+
48
+ synchronize_before :current_url, lazy: true # wait until running JavaScript has updated the URL
49
+
50
+ synchronize_before :refresh, lazy: false # wait until running JavaScript has updated the URL
51
+ unsychronize_after :refresh # new document is no longer synchronized
52
+
53
+ synchronize_before :go_back, lazy: false # wait until running JavaScript has updated the URL
54
+ unsychronize_after :go_back # new document is no longer synchronized
55
+
56
+ synchronize_before :go_forward, lazy: false # wait until running JavaScript has updated the URL
57
+ unsychronize_after :go_forward # new document is no longer synchronized
58
+
59
+ synchronize_before :switch_to_frame, lazy: true # wait until the current frame is done processing
60
+ unsychronize_after :switch_to_frame # now that we've switched into the new frame, we don't know the document's synchronization state.
61
+
62
+ synchronize_before :switch_to_window, lazy: true # wait until the current frame is done processing
63
+ unsychronize_after :switch_to_window # now that we've switched to the new window, we don't know the document's synchronization state.
64
+ end
65
+
3
66
  module Capybara
4
67
  module Lockstep
5
68
  module VisitWithWaiting
6
- ruby2_keywords def visit(*args, &block)
69
+ def visit(*args, &block)
7
70
  url = args[0]
8
71
  # Some of our apps have a Cucumber step that changes drivers mid-scenario.
9
72
  # It works by creating a new Capybara session and re-visits the URL from the
10
73
  # previous session. If this happens before a URL is ever loaded,
11
74
  # it re-visits the URL "data:", which will never "finish" initializing.
12
75
  # Also when opening a new tab via Capybara, the initial URL is about:blank.
13
- visiting_remote_url = !(url.start_with?('data:') || url.start_with?('about:'))
76
+ visiting_real_url = !(url.start_with?('data:') || url.start_with?('about:'))
14
77
 
15
- if visiting_remote_url
78
+ if visiting_real_url
16
79
  # We're about to leave this screen, killing all in-flight requests.
17
80
  # Give pending form submissions etc. a chance to finish before we tear down
18
81
  # the browser environment.
19
82
  #
20
83
  # We force a non-lazy synchronization so we pick up all client-side changes
21
84
  # that have not been caused by Capybara commands.
22
- Lockstep.synchronize(lazy: false)
85
+ Lockstep.auto_synchronize(lazy: false, log: "Synchronizing before visiting #{url}")
23
86
  end
24
87
 
25
88
  super(*args, &block).tap do
26
- if visiting_remote_url
89
+ if visiting_real_url
27
90
  # We haven't yet synchronized the new screen.
28
91
  Lockstep.synchronized = false
29
92
  end
30
93
  end
31
94
  end
95
+
96
+ ruby2_keywords :visit
32
97
  end
33
98
  end
34
99
  end
@@ -58,14 +123,15 @@ module Capybara
58
123
  Lockstep.auto_synchronize(lazy: !script_may_navigate_away, log: "Synchronizing before script: #{script}")
59
124
  end
60
125
 
61
- super(script, *args, &block).tap do
62
- if !Lockstep.synchronizing?
63
- # We haven't yet synchronized with whatever changes the JavaScript
64
- # did on the frontend.
65
- Lockstep.synchronized = false
66
- end
126
+ super(script, *args, &block)
127
+ ensure
128
+ if !Lockstep.synchronizing?
129
+ # We haven't yet synchronized with whatever changes the JavaScript
130
+ # did on the frontend.
131
+ Lockstep.synchronized = false
67
132
  end
68
133
  end
134
+
69
135
  ruby2_keywords meth
70
136
  end
71
137
  prepend(mod)
@@ -84,24 +150,6 @@ Capybara::Session.class_eval do
84
150
  # internally and we don't want to synchronize multiple times.
85
151
  end
86
152
 
87
- module Capybara
88
- module Lockstep
89
- module UnsychronizeAfter
90
- def unsychronize_after(meth)
91
- mod = Module.new do
92
- define_method meth do |*args, &block|
93
- super(*args, &block).tap do
94
- Lockstep.synchronized = false
95
- end
96
- end
97
- ruby2_keywords meth
98
- end
99
- prepend(mod)
100
- end
101
- end
102
- end
103
- end
104
-
105
153
  # Capybara 3 has driver-specific Node classes which sometimes
106
154
  # super to Capybara::Selenium::Node, but not always.
107
155
  node_classes = [
@@ -141,14 +189,16 @@ end
141
189
  module Capybara
142
190
  module Lockstep
143
191
  module SynchronizeWithCatchUp
144
- ruby2_keywords def synchronize(*args, &block)
192
+ def synchronize(*args, &block)
145
193
  # This method is called by Capybara before most interactions with
146
194
  # the browser. It is a different method than Capybara::Lockstep.synchronize!
147
195
  # We use the { lazy } option to only synchronize when we're out of sync.
148
- Capybara::Lockstep.auto_synchronize(lazy: true)
196
+ Lockstep.auto_synchronize(lazy: true, log: 'Synchronizing before node access')
149
197
 
150
198
  super(*args, &block)
151
199
  end
200
+
201
+ ruby2_keywords :synchronize
152
202
  end
153
203
  end
154
204
  end
@@ -3,12 +3,14 @@ window.CapybaraLockstep = (function() {
3
3
  let debug
4
4
  let jobCount
5
5
  let idleCallbacks
6
+ let finishedWorkTags
6
7
  let waitTasks
7
8
  reset()
8
9
 
9
10
  function reset() {
10
11
  jobCount = 0
11
12
  idleCallbacks = []
13
+ finishedWorkTags = []
12
14
  waitTasks = 0
13
15
  debug = false
14
16
  }
@@ -74,12 +76,17 @@ window.CapybaraLockstep = (function() {
74
76
  jobCount--
75
77
 
76
78
  if (tag) {
79
+ finishedWorkTags.push(tag)
77
80
  logPositive('Finished work: %s [%d jobs]', tag, jobCount)
78
81
  }
79
82
 
80
- let idleCallback
81
- while (isIdle() && (idleCallback = idleCallbacks.shift())) {
82
- idleCallback('Finished waiting for browser')
83
+ if (isIdle()) {
84
+ let idleCallback
85
+ while ((idleCallback = idleCallbacks.shift())) {
86
+ idleCallback("Finished waiting for " + finishedWorkTags.join(', '))
87
+ }
88
+
89
+ finishedWorkTags = []
83
90
  }
84
91
  }
85
92
 
@@ -1,7 +1,7 @@
1
1
  module Capybara
2
2
  module Lockstep
3
3
  ERROR_SNIPPET_MISSING = 'Cannot synchronize: capybara-lockstep JavaScript snippet is missing'
4
- ERROR_PAGE_MISSING = 'Cannot synchronize before initial Capybara visit'
4
+ ERROR_PAGE_MISSING = 'Cannot synchronize with empty page'
5
5
  ERROR_ALERT_OPEN = 'Cannot synchronize while an alert is open'
6
6
  ERROR_NAVIGATED_AWAY = "Browser navigated away while synchronizing"
7
7
 
@@ -13,7 +13,17 @@ module Capybara
13
13
  alias synchronizing? synchronizing
14
14
 
15
15
  def synchronized?
16
+ # The synchronized flag is per-session (page == Capybara.current_session).
17
+ # This enables tests that use more than one browser, e.g. to test multi-user interaction:
18
+ # https://makandracards.com/makandra/474480-how-to-make-a-cucumber-test-work-with-multiple-browser-sessions
19
+ #
20
+ # Ideally the synchronized flag would also be per-tab, per-frame and per-document.
21
+ # We haven't found a way to patch this into Capybara, as there does not seem to be
22
+ # a persistent object representing a document. Capybara::Node::Document just seems to
23
+ # be a proxy accessing whatever is the current document. The way we work around this
24
+ # is that we synchronize before switching tabs or frames.
16
25
  value = page.instance_variable_get(:@lockstep_synchronized)
26
+
17
27
  # We consider a new Capybara session to be synchronized.
18
28
  # This will be set to false after our first visit().
19
29
  value.nil? ? true : value
@@ -24,19 +34,30 @@ module Capybara
24
34
  end
25
35
 
26
36
  def synchronize(lazy: false, log: nil)
37
+ # The { lazy } option is a performance optimization that will prevent capybara-lockstep
38
+ # from synchronizing multiple times in expressions like `page.find('.foo').find('.bar')`.
39
+ # The { lazy } option has nothing todo with :auto mode.
40
+ #
41
+ # With { lazy: true } we only synchronize when the Ruby-side thinks we're out of sync.
42
+ # This saves us an expensive execute_script() roundtrip that goes to the browser and back.
43
+ # However the knowledge of the Ruby-side is limited: We only assume that we're out of sync
44
+ # after a page load or after a Capybara command. There may be additional client-side work
45
+ # that the Ruby-side is not aware of, e.g. an AJAX call scheduled by a timeout.
46
+ #
47
+ # With { lazy: false } we force synchronization with the browser, whether the Ruby-side
48
+ # thinks we're in sync or not. This always makes an execute_script() rountrip, but picks up
49
+ # non-lazy synchronization so we pick up client-side work that have not been caused
50
+ # by Capybara commands.
27
51
  if (lazy && synchronized?) || synchronizing? || mode == :off
28
52
  return
29
53
  end
30
54
 
31
- # Allow passing a log message that is only logged
32
- # when we're actually synchronizing.
33
- if log
34
- self.log(log)
35
- end
36
-
37
- synchronize_now
55
+ synchronize_now(log: log)
38
56
  end
39
57
 
58
+ # Automatic synchronization from within the capybara-lockstep should always call #auto_synchronize.
59
+ # This only synchronizes IFF in :auto mode, i.e. the user has not explicitly disabled automatic syncing.
60
+ # The :auto mode has nothing to do with the { lazy } option.
40
61
  def auto_synchronize(**options)
41
62
  if mode == :auto
42
63
  synchronize(**options)
@@ -45,11 +66,11 @@ module Capybara
45
66
 
46
67
  private
47
68
 
48
- def synchronize_now
69
+ def synchronize_now(log: 'Synchronizing')
49
70
  self.synchronizing = true
50
71
  self.synchronized = false
51
72
 
52
- log 'Synchronizing'
73
+ self.log(log)
53
74
 
54
75
  start_time = current_seconds
55
76
 
@@ -64,8 +85,8 @@ module Capybara
64
85
  done(#{ERROR_SNIPPET_MISSING.to_json})
65
86
  }
66
87
  }
67
- let protocol = location.protocol
68
- if (protocol === 'data:' || protocol == 'about:') {
88
+ const emptyDataURL = /^data:[^,]*,?$/
89
+ if (emptyDataURL.test(location.href) || location.protocol === 'about:') {
69
90
  done(#{ERROR_PAGE_MISSING.to_json})
70
91
  } else if (document.readyState === 'complete') {
71
92
  // WebDriver always waits for the `load` event after a visit(),
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Lockstep
3
- VERSION = "1.1.1"
3
+ VERSION = "1.2.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-lockstep
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-16 00:00:00.000000000 Z
11
+ date: 2022-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -119,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  - !ruby/object:Gem::Version
120
120
  version: '0'
121
121
  requirements: []
122
- rubygems_version: 3.1.4
122
+ rubygems_version: 3.2.6
123
123
  signing_key:
124
124
  specification_version: 4
125
125
  summary: Synchronize Capybara commands with client-side JavaScript and AJAX requests