capybara-lockstep 1.1.1 → 1.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: 271ed22154f61a70a3961eb2bd2a8a7d29430dfb37107b9f1176257e8efab320
4
- data.tar.gz: 6ba0d7a5c7e8bff779ee2a9ac2d6d32829374041dff41d7bddd9cbd19627a468
3
+ metadata.gz: ec0d3874212ec29a338fa16ef831e76c0b8dc65b4a200f3a305b7ce41bd76957
4
+ data.tar.gz: b5d48346fb7224b2154512534757ef96638f1df9cc62f9d0f911aac8d6a5f846
5
5
  SHA512:
6
- metadata.gz: 24f098f75fc9093065cf4210eed7fab3b8f1ea69d49d6e328f195f92affe41c262fa92daecbf1ee8247dba8bcad1d3d5c0941766bd3889416be6aedf79f3e354
7
- data.tar.gz: ea3debf0f2e4178dbc200c84a98aae87a614914de889769d10ed1cc2e311f83e93de460e2b1a4c163b756dc331838164bfe7ae8f77f676833df93a984a2fda49
6
+ metadata.gz: 4d44c17fa50ca0fd386b34ecea302148989f4d48d63c48820bcfff75bcd51c63cfbacea0053e63fc1c27f6347fbe252f14be83372aed123a6a1c2574328fe42e
7
+ data.tar.gz: 9e7f5cfcbb6b3750277bc55c37484e17859aa4d96f1b8f7b6c1865f4467ae00fad5311e11ea0bfc73d5b8ec5c925df5c8ec6bb2473da6b431296736c89972794
data/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@ 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
+ ## 1.2.0 - 2022-09-12
6
+
7
+ ### Synchronization around history navigation
8
+
9
+ We now synchronize before and after history navigation using the following Capybara methods:
10
+
11
+ - `page.refresh`
12
+ - `page.go_back`
13
+ - `page.go_forward`
14
+
15
+ We also synchronize before `current_url` in case running a JavaScript task wants to update the URL when done.
16
+
17
+ ### Support for tests with multiple tabs or frames
18
+
19
+ 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).
20
+ We now synchronize before and after the following Capybara methods:
21
+
22
+ - `switch_to_frame`
23
+ - `within_frame`
24
+ - `switch_to_window`
25
+ - `within_window`
26
+
27
+ ### Improved logging
28
+
29
+ - Only log when we're actually synchronizing
30
+ - Log the reason why we're synchronizing (e.g. before node access)
31
+ - Log which browser work we're waiting for (e.g. XHR request, image load)
32
+
33
+ ### Various changes
34
+
35
+ - Synchronize before accessing `page.html`.
36
+
37
+
5
38
  ## 1.1.1 - 2022-03-16
6
39
 
7
40
  - Activate rubygems MFA
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.0)
5
5
  activesupport (>= 3.2)
6
6
  capybara (>= 2.0)
7
7
  ruby2_keywords
@@ -10,32 +10,32 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- activesupport (6.1.3.1)
13
+ activesupport (7.0.1)
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
- zeitwerk (~> 2.3)
19
- addressable (2.7.0)
18
+ addressable (2.8.0)
20
19
  public_suffix (>= 2.0.2, < 5.0)
21
20
  byebug (11.1.3)
22
- capybara (3.35.3)
21
+ capybara (3.36.0)
23
22
  addressable
23
+ matrix
24
24
  mini_mime (>= 0.1.3)
25
25
  nokogiri (~> 1.8)
26
26
  rack (>= 1.6.0)
27
27
  rack-test (>= 0.6.3)
28
28
  regexp_parser (>= 1.5, < 3.0)
29
29
  xpath (~> 3.2)
30
- childprocess (3.0.0)
30
+ childprocess (4.1.0)
31
31
  chrome_remote (0.3.0)
32
32
  websocket-driver (~> 0.6)
33
- concurrent-ruby (1.1.8)
33
+ concurrent-ruby (1.1.10)
34
34
  daemons (1.3.1)
35
35
  diff-lcs (1.3)
36
36
  eventmachine (1.2.7)
37
37
  gemika (0.6.0)
38
- i18n (1.8.10)
38
+ i18n (1.10.0)
39
39
  concurrent-ruby (~> 1.0)
40
40
  jasmine (3.6.0)
41
41
  jasmine-core (~> 3.6.0)
@@ -43,18 +43,20 @@ GEM
43
43
  rack (>= 1.2.1)
44
44
  rake
45
45
  jasmine-core (3.6.0)
46
- mini_mime (1.1.0)
47
- minitest (5.14.4)
48
- nokogiri (1.11.3-x86_64-linux)
46
+ matrix (0.4.2)
47
+ mini_mime (1.1.2)
48
+ minitest (5.15.0)
49
+ nokogiri (1.13.0-x86_64-linux)
49
50
  racc (~> 1.4)
50
51
  phantomjs (2.1.1.0)
51
52
  public_suffix (4.0.6)
52
- racc (1.5.2)
53
+ racc (1.6.0)
53
54
  rack (2.2.3)
54
55
  rack-test (1.1.0)
55
56
  rack (>= 1.0, < 3)
56
57
  rake (13.0.1)
57
- regexp_parser (2.1.1)
58
+ regexp_parser (2.2.0)
59
+ rexml (3.2.5)
58
60
  rspec (3.7.0)
59
61
  rspec-core (~> 3.7.0)
60
62
  rspec-expectations (~> 3.7.0)
@@ -68,10 +70,11 @@ GEM
68
70
  diff-lcs (>= 1.2.0, < 2.0)
69
71
  rspec-support (~> 3.7.0)
70
72
  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)
73
+ ruby2_keywords (0.0.5)
74
+ rubyzip (2.3.2)
75
+ selenium-webdriver (4.1.0)
76
+ childprocess (>= 0.5, < 5.0)
77
+ rexml (~> 3.2, >= 3.2.5)
75
78
  rubyzip (>= 1.2.2)
76
79
  thin (1.8.0)
77
80
  daemons (~> 1.0, >= 1.0.9)
@@ -84,7 +87,6 @@ GEM
84
87
  websocket-extensions (0.1.5)
85
88
  xpath (3.2.0)
86
89
  nokogiri (~> 1.8)
87
- zeitwerk (2.4.2)
88
90
 
89
91
  PLATFORMS
90
92
  ruby
@@ -100,4 +102,4 @@ DEPENDENCIES
100
102
  thin
101
103
 
102
104
  BUNDLED WITH
103
- 2.2.15
105
+ 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
 
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Lockstep
3
- VERSION = "1.1.1"
3
+ VERSION = "1.2.0"
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.0
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