capybara-playwright-driver 0.3.0 → 0.3.1

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: 6af9f6424fc2c8e2dc870b3f93c7c3a5b78d72b2b1919216a4ab6ea10d410315
4
- data.tar.gz: 90c223a49a82c1c39599847de8fd1818ab901c6eca2918630e8e6f0d25a3d701
3
+ metadata.gz: 931fea1c143e8de209fb6a360e072b0b5dfd724e7f9d240d8a4b0a36b39b443e
4
+ data.tar.gz: 53fa84147fadb311502981d7a51311d147a140f6983cd8d66567cea79918d332
5
5
  SHA512:
6
- metadata.gz: 83de549842d4de5b036f57e3ebd3d009a94ae27c70c9724aa282bd0ecdafd1e2faacd7ce990ff0e837877606cc56453b9827199ed2afaff7a73c0cb97c4dea42
7
- data.tar.gz: 314d3197acfd2098343d8edbb030cc87f424bf46db4d6ee0c4441eba5db3f4c75f772584769cd7e18028ef2547a8fb235452a23dd6dab1a0f8259c90fe6fd667
6
+ metadata.gz: 1e3e16c8377aa75d7fb5716e875b0291d50332ef9e447c52205b0dab27cfd8de95d32388112a3f1a280137569eba270f77b236f826dcc7117d374ff43b18943c
7
+ data.tar.gz: cc15d0f5a5ddfa9d024d87e800f9e073df34220318b68465e9e77ff4bfc8ac3ce3a3cd7ed7e393b9b884de85fbf0ac4eae65b4e7ee68740cb4c68eafa17524c9
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'bundler'
32
32
  spec.add_development_dependency 'launchy', '>= 2.0.4'
33
33
  spec.add_development_dependency 'pry-byebug'
34
+ spec.add_development_dependency 'rack-test_server'
34
35
  spec.add_development_dependency 'rake', '~> 13.0.3'
35
36
  spec.add_development_dependency 'rspec', '~> 3.11.0'
36
37
  spec.add_development_dependency 'rubocop-rspec'
@@ -49,15 +49,14 @@ module Capybara
49
49
  end
50
50
 
51
51
  def current_url
52
- assert_page_alive
53
-
54
- @playwright_page.url
52
+ assert_page_alive {
53
+ @playwright_page.url
54
+ }
55
55
  end
56
56
 
57
57
  def visit(path)
58
- assert_page_alive
59
-
60
- url =
58
+ assert_page_alive {
59
+ url =
61
60
  if Capybara.app_host
62
61
  URI(Capybara.app_host).merge(path)
63
62
  elsif Capybara.default_host
@@ -66,103 +65,104 @@ module Capybara
66
65
  path
67
66
  end
68
67
 
69
- @playwright_page.capybara_current_frame.goto(url)
68
+ @playwright_page.capybara_current_frame.goto(url)
69
+ }
70
70
  end
71
71
 
72
72
  def refresh
73
- assert_page_alive
74
-
75
- @playwright_page.capybara_current_frame.evaluate('() => { location.reload(true) }')
73
+ assert_page_alive {
74
+ @playwright_page.capybara_current_frame.evaluate('() => { location.reload(true) }')
75
+ }
76
76
  end
77
77
 
78
78
  def find_xpath(query, **options)
79
- assert_page_alive
80
-
81
- @playwright_page.capybara_current_frame.query_selector_all("xpath=#{query}").map do |el|
82
- Node.new(@driver, @playwright_page, el)
83
- end
79
+ assert_page_alive {
80
+ @playwright_page.capybara_current_frame.query_selector_all("xpath=#{query}").map do |el|
81
+ Node.new(@driver, @playwright_page, el)
82
+ end
83
+ }
84
84
  end
85
85
 
86
86
  def find_css(query, **options)
87
- assert_page_alive
88
-
89
- @playwright_page.capybara_current_frame.query_selector_all(query).map do |el|
90
- Node.new(@driver, @playwright_page, el)
91
- end
87
+ assert_page_alive {
88
+ @playwright_page.capybara_current_frame.query_selector_all(query).map do |el|
89
+ Node.new(@driver, @playwright_page, el)
90
+ end
91
+ }
92
92
  end
93
93
 
94
94
  def response_headers
95
- assert_page_alive
96
-
97
- @playwright_page.capybara_response_headers
95
+ assert_page_alive {
96
+ @playwright_page.capybara_response_headers
97
+ }
98
98
  end
99
99
 
100
100
  def status_code
101
- assert_page_alive
102
-
103
- @playwright_page.capybara_status_code
101
+ assert_page_alive {
102
+ @playwright_page.capybara_status_code
103
+ }
104
104
  end
105
105
 
106
106
  def html
107
- assert_page_alive
108
-
109
- js = <<~JAVASCRIPT
110
- () => {
111
- let html = '';
112
- if (document.doctype) html += new XMLSerializer().serializeToString(document.doctype);
113
- if (document.documentElement) html += document.documentElement.outerHTML;
114
- return html;
107
+ assert_page_alive {
108
+ js = <<~JAVASCRIPT
109
+ () => {
110
+ let html = '';
111
+ if (document.doctype) html += new XMLSerializer().serializeToString(document.doctype);
112
+ if (document.documentElement) html += document.documentElement.outerHTML;
113
+ return html;
114
+ }
115
+ JAVASCRIPT
116
+ @playwright_page.capybara_current_frame.evaluate(js)
115
117
  }
116
- JAVASCRIPT
117
- @playwright_page.capybara_current_frame.evaluate(js)
118
118
  end
119
119
 
120
120
  def title
121
- assert_page_alive
122
-
123
- @playwright_page.title
121
+ assert_page_alive {
122
+ @playwright_page.title
123
+ }
124
124
  end
125
125
 
126
126
  def go_back
127
- assert_page_alive
128
-
129
- @playwright_page.go_back
127
+ assert_page_alive {
128
+ @playwright_page.go_back
129
+ }
130
130
  end
131
131
 
132
132
  def go_forward
133
- assert_page_alive
134
-
135
- @playwright_page.go_forward
133
+ assert_page_alive {
134
+ @playwright_page.go_forward
135
+ }
136
136
  end
137
137
 
138
138
  def execute_script(script, *args)
139
- assert_page_alive
140
-
141
- @playwright_page.capybara_current_frame.evaluate("function (arguments) { #{script} }", arg: unwrap_node(args))
139
+ assert_page_alive {
140
+ @playwright_page.capybara_current_frame.evaluate("function (arguments) { #{script} }", arg: unwrap_node(args))
141
+ }
142
142
  nil
143
143
  end
144
144
 
145
145
  def evaluate_script(script, *args)
146
- assert_page_alive
147
-
148
- result = @playwright_page.capybara_current_frame.evaluate_handle("function (arguments) { return #{script} }", arg: unwrap_node(args))
149
- wrap_node(result)
146
+ assert_page_alive {
147
+ result = @playwright_page.capybara_current_frame.evaluate_handle("function (arguments) { return #{script} }", arg: unwrap_node(args))
148
+ wrap_node(result)
149
+ }
150
150
  end
151
151
 
152
152
  def evaluate_async_script(script, *args)
153
- assert_page_alive
154
-
155
- js = <<~JAVASCRIPT
156
- function(_arguments){
157
- let args = Array.prototype.slice.call(_arguments);
158
- return new Promise((resolve, reject) => {
159
- args.push(resolve);
160
- (function(){ #{script} }).apply(this, args);
161
- });
153
+ assert_page_alive {
154
+ js = <<~JAVASCRIPT
155
+ function(_arguments){
156
+ let args = Array.prototype.slice.call(_arguments);
157
+ return new Promise((resolve, reject) => {
158
+ args.push(resolve);
159
+ (function(){ #{script} }).apply(this, args);
160
+ });
161
+ }
162
+ JAVASCRIPT
163
+ result = @playwright_page.capybara_current_frame.evaluate_handle(js, arg: unwrap_node(args))
164
+ wrap_node(result)
162
165
  }
163
- JAVASCRIPT
164
- result = @playwright_page.capybara_current_frame.evaluate_handle(js, arg: unwrap_node(args))
165
- wrap_node(result)
166
166
  end
167
167
 
168
168
  def active_element
@@ -191,9 +191,9 @@ module Capybara
191
191
  end
192
192
 
193
193
  def save_screenshot(path, **options)
194
- assert_page_alive
195
-
196
- @playwright_page.screenshot(path: path)
194
+ assert_page_alive {
195
+ @playwright_page.screenshot(path: path)
196
+ }
197
197
  end
198
198
 
199
199
  def send_keys(*args)
@@ -201,24 +201,47 @@ module Capybara
201
201
  end
202
202
 
203
203
  def switch_to_frame(frame)
204
- assert_page_alive
205
-
206
- case frame
207
- when :top
208
- @playwright_page.capybara_reset_frames
209
- when :parent
210
- @playwright_page.capybara_pop_frame
211
- else
212
- playwright_frame = frame.native.content_frame
213
- raise ArgumentError.new("Not a frame element: #{frame}") unless playwright_frame
214
- @playwright_page.capybara_push_frame(playwright_frame)
215
- end
204
+ assert_page_alive {
205
+ case frame
206
+ when :top
207
+ @playwright_page.capybara_reset_frames
208
+ when :parent
209
+ @playwright_page.capybara_pop_frame
210
+ else
211
+ playwright_frame = frame.native.content_frame
212
+ raise ArgumentError.new("Not a frame element: #{frame}") unless playwright_frame
213
+ @playwright_page.capybara_push_frame(playwright_frame)
214
+ end
215
+ }
216
216
  end
217
217
 
218
- private def assert_page_alive
218
+ # Capybara doesn't retry at this case since it doesn't use `synchronize { ... } for driver/browser methods.`
219
+ # We have to retry ourselves.
220
+ private def assert_page_alive(retry_count: 5, &block)
219
221
  if !@playwright_page || @playwright_page.closed?
220
222
  raise NoSuchWindowError
221
223
  end
224
+
225
+ if retry_count <= 0
226
+ return block.call
227
+ end
228
+
229
+ begin
230
+ return block.call
231
+ rescue ::Playwright::Error => err
232
+ case err.message
233
+ when /Element is not attached to the DOM/,
234
+ /Execution context was destroyed, most likely because of a navigation/,
235
+ /Cannot find context with specified id/,
236
+ /Unable to adopt element handle from a different document/
237
+ # ignore error for retry
238
+ puts "[WARNING] #{err.message}"
239
+ else
240
+ raise
241
+ end
242
+ end
243
+
244
+ assert_page_alive(retry_count: retry_count - 1, &block)
222
245
  end
223
246
 
224
247
  private def pages
@@ -303,15 +326,15 @@ module Capybara
303
326
  end
304
327
 
305
328
  def accept_modal(dialog_type, **options, &block)
306
- assert_page_alive
307
-
308
- @playwright_page.capybara_accept_modal(dialog_type, **options, &block)
329
+ assert_page_alive {
330
+ @playwright_page.capybara_accept_modal(dialog_type, **options, &block)
331
+ }
309
332
  end
310
333
 
311
334
  def dismiss_modal(dialog_type, **options, &block)
312
- assert_page_alive
313
-
314
- @playwright_page.capybara_dismiss_modal(dialog_type, **options, &block)
335
+ assert_page_alive {
336
+ @playwright_page.capybara_dismiss_modal(dialog_type, **options, &block)
337
+ }
315
338
  end
316
339
 
317
340
  private def unwrap_node(args)
@@ -361,9 +384,9 @@ module Capybara
361
384
  end
362
385
 
363
386
  def with_playwright_page(&block)
364
- assert_page_alive
365
-
366
- block.call(@playwright_page)
387
+ assert_page_alive {
388
+ block.call(@playwright_page)
389
+ }
367
390
  end
368
391
  end
369
392
  end
@@ -74,15 +74,19 @@ module Capybara
74
74
  @element
75
75
  end
76
76
 
77
- private def assert_element_not_stale
77
+ private def assert_element_not_stale(&block)
78
78
  # Playwright checks the staled state only when
79
79
  # actionable methods. (click, select_option, hover, ...)
80
80
  # Capybara expects stale checking also when getting inner text, and so on.
81
81
  @element.enabled?
82
+
83
+ block.call
82
84
  rescue ::Playwright::Error => err
83
85
  case err.message
84
86
  when /Element is not attached to the DOM/
85
87
  raise StaleReferenceError.new(err)
88
+ when /Execution context was destroyed, most likely because of a navigation/
89
+ raise StaleReferenceError.new(err)
86
90
  when /Cannot find context with specified id/
87
91
  raise StaleReferenceError.new(err)
88
92
  when /Unable to adopt element handle from a different document/ # for WebKit.
@@ -102,42 +106,42 @@ module Capybara
102
106
  class StaleReferenceError < StandardError ; end
103
107
 
104
108
  def all_text
105
- assert_element_not_stale
106
-
107
- text = @element.text_content
108
- text.to_s.gsub(/[\u200b\u200e\u200f]/, '')
109
- .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
110
- .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
111
- .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
112
- .tr("\u00a0", ' ')
109
+ assert_element_not_stale {
110
+ text = @element.text_content
111
+ text.to_s.gsub(/[\u200b\u200e\u200f]/, '')
112
+ .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
113
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
114
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
115
+ .tr("\u00a0", ' ')
116
+ }
113
117
  end
114
118
 
115
119
  def visible_text
116
- assert_element_not_stale
117
-
118
- return '' unless visible?
119
-
120
- text = @element.evaluate(<<~JAVASCRIPT)
121
- function(el){
122
- if (el.nodeName == 'TEXTAREA'){
123
- return el.textContent;
124
- } else if (el instanceof SVGElement) {
125
- return el.textContent;
126
- } else {
127
- return el.innerText;
120
+ assert_element_not_stale {
121
+ return '' unless visible?
122
+
123
+ text = @element.evaluate(<<~JAVASCRIPT)
124
+ function(el){
125
+ if (el.nodeName == 'TEXTAREA'){
126
+ return el.textContent;
127
+ } else if (el instanceof SVGElement) {
128
+ return el.textContent;
129
+ } else {
130
+ return el.innerText;
131
+ }
128
132
  }
129
- }
130
- JAVASCRIPT
131
- text.to_s.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
132
- .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
133
- .gsub(/\n+/, "\n")
134
- .tr("\u00a0", ' ')
133
+ JAVASCRIPT
134
+ text.to_s.gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
135
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
136
+ .gsub(/\n+/, "\n")
137
+ .tr("\u00a0", ' ')
138
+ }
135
139
  end
136
140
 
137
141
  def [](name)
138
- assert_element_not_stale
139
-
140
- property(name) || attribute(name)
142
+ assert_element_not_stale {
143
+ property(name) || attribute(name)
144
+ }
141
145
  end
142
146
 
143
147
  private def property(name)
@@ -150,17 +154,17 @@ module Capybara
150
154
  end
151
155
 
152
156
  def value
153
- assert_element_not_stale
154
-
155
- # ref: https://github.com/teamcapybara/capybara/blob/f7ab0b5cd5da86185816c2d5c30d58145fe654ed/lib/capybara/selenium/node.rb#L31
156
- # ref: https://github.com/twalpole/apparition/blob/11aca464b38b77585191b7e302be2e062bdd369d/lib/capybara/apparition/node.rb#L728
157
- if tag_name == 'select' && @element.evaluate('el => el.multiple')
158
- @element.query_selector_all('option:checked').map do |option|
159
- option.evaluate('el => el.value')
157
+ assert_element_not_stale {
158
+ # ref: https://github.com/teamcapybara/capybara/blob/f7ab0b5cd5da86185816c2d5c30d58145fe654ed/lib/capybara/selenium/node.rb#L31
159
+ # ref: https://github.com/twalpole/apparition/blob/11aca464b38b77585191b7e302be2e062bdd369d/lib/capybara/apparition/node.rb#L728
160
+ if tag_name == 'select' && @element.evaluate('el => el.multiple')
161
+ @element.query_selector_all('option:checked').map do |option|
162
+ option.evaluate('el => el.value')
163
+ end
164
+ else
165
+ @element.evaluate('el => el.value')
160
166
  end
161
- else
162
- @element.evaluate('el => el.value')
163
- end
167
+ }
164
168
  end
165
169
 
166
170
  def style(styles)
@@ -245,7 +249,15 @@ module Capybara
245
249
 
246
250
  class FileUpload < Settable
247
251
  def set(value, **options)
248
- @element.set_input_files(value, timeout: @timeout)
252
+ file =
253
+ if value.is_a?(File)
254
+ value.path
255
+ elsif value.is_a?(Enumerable)
256
+ value.map(&:to_s)
257
+ else
258
+ value.to_s
259
+ end
260
+ @element.set_input_files(file, timeout: @timeout)
249
261
  end
250
262
  end
251
263
 
@@ -772,37 +784,37 @@ module Capybara
772
784
  end
773
785
 
774
786
  def visible?
775
- assert_element_not_stale
776
-
777
- # if an area element, check visibility of relevant image
778
- @element.evaluate(<<~JAVASCRIPT)
779
- function(el) {
780
- if (el.tagName == 'AREA'){
781
- const map_name = document.evaluate('./ancestor::map/@name', el, null, XPathResult.STRING_TYPE, null).stringValue;
782
- el = document.querySelector(`img[usemap='#${map_name}']`);
783
- if (!el){
784
- return false;
785
- }
786
- }
787
- var forced_visible = false;
788
- while (el) {
789
- const style = window.getComputedStyle(el);
790
- if (style.visibility == 'visible')
791
- forced_visible = true;
792
- if ((style.display == 'none') ||
793
- ((style.visibility == 'hidden') && !forced_visible) ||
794
- (parseFloat(style.opacity) == 0)) {
787
+ assert_element_not_stale {
788
+ # if an area element, check visibility of relevant image
789
+ @element.evaluate(<<~JAVASCRIPT)
790
+ function(el) {
791
+ if (el.tagName == 'AREA'){
792
+ const map_name = document.evaluate('./ancestor::map/@name', el, null, XPathResult.STRING_TYPE, null).stringValue;
793
+ el = document.querySelector(`img[usemap='#${map_name}']`);
794
+ if (!el){
795
795
  return false;
796
+ }
796
797
  }
797
- var parent = el.parentElement;
798
- if (parent && (parent.tagName == 'DETAILS') && !parent.open && (el.tagName != 'SUMMARY')) {
799
- return false;
798
+ var forced_visible = false;
799
+ while (el) {
800
+ const style = window.getComputedStyle(el);
801
+ if (style.visibility == 'visible')
802
+ forced_visible = true;
803
+ if ((style.display == 'none') ||
804
+ ((style.visibility == 'hidden') && !forced_visible) ||
805
+ (parseFloat(style.opacity) == 0)) {
806
+ return false;
807
+ }
808
+ var parent = el.parentElement;
809
+ if (parent && (parent.tagName == 'DETAILS') && !parent.open && (el.tagName != 'SUMMARY')) {
810
+ return false;
811
+ }
812
+ el = parent;
800
813
  }
801
- el = parent;
814
+ return true;
802
815
  }
803
- return true;
816
+ JAVASCRIPT
804
817
  }
805
- JAVASCRIPT
806
818
  end
807
819
 
808
820
  def obscured?
@@ -810,15 +822,15 @@ module Capybara
810
822
  end
811
823
 
812
824
  def checked?
813
- assert_element_not_stale
814
-
815
- @element.evaluate('el => !!el.checked')
825
+ assert_element_not_stale {
826
+ @element.evaluate('el => !!el.checked')
827
+ }
816
828
  end
817
829
 
818
830
  def selected?
819
- assert_element_not_stale
820
-
821
- @element.evaluate('el => !!el.selected')
831
+ assert_element_not_stale {
832
+ @element.evaluate('el => !!el.selected')
833
+ }
822
834
  end
823
835
 
824
836
  def disabled?
@@ -842,49 +854,49 @@ module Capybara
842
854
  end
843
855
 
844
856
  def rect
845
- assert_element_not_stale
846
-
847
- @element.evaluate(<<~JAVASCRIPT)
848
- function(el){
849
- const rects = [...el.getClientRects()]
850
- const rect = rects.find(r => (r.height && r.width)) || el.getBoundingClientRect();
851
- return rect.toJSON();
857
+ assert_element_not_stale {
858
+ @element.evaluate(<<~JAVASCRIPT)
859
+ function(el){
860
+ const rects = [...el.getClientRects()]
861
+ const rect = rects.find(r => (r.height && r.width)) || el.getBoundingClientRect();
862
+ return rect.toJSON();
863
+ }
864
+ JAVASCRIPT
852
865
  }
853
- JAVASCRIPT
854
866
  end
855
867
 
856
868
  def path
857
- assert_element_not_stale
858
-
859
- @element.evaluate(<<~JAVASCRIPT)
860
- (el) => {
861
- var xml = document;
862
- var xpath = '';
863
- var pos, tempitem2;
864
- if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) {
865
- return "(: Shadow DOM element - no XPath :)";
866
- };
867
- while(el !== xml.documentElement) {
868
- pos = 0;
869
- tempitem2 = el;
870
- while(tempitem2) {
871
- if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name
872
- pos += 1;
869
+ assert_element_not_stale {
870
+ @element.evaluate(<<~JAVASCRIPT)
871
+ (el) => {
872
+ var xml = document;
873
+ var xpath = '';
874
+ var pos, tempitem2;
875
+ if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) {
876
+ return "(: Shadow DOM element - no XPath :)";
877
+ };
878
+ while(el !== xml.documentElement) {
879
+ pos = 0;
880
+ tempitem2 = el;
881
+ while(tempitem2) {
882
+ if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name
883
+ pos += 1;
884
+ }
885
+ tempitem2 = tempitem2.previousSibling;
873
886
  }
874
- tempitem2 = tempitem2.previousSibling;
875
- }
876
- if (el.namespaceURI != xml.documentElement.namespaceURI) {
877
- xpath = "*[local-name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;
878
- } else {
879
- xpath = el.nodeName.toUpperCase()+"["+pos+"]/"+xpath;
887
+ if (el.namespaceURI != xml.documentElement.namespaceURI) {
888
+ xpath = "*[local-name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;
889
+ } else {
890
+ xpath = el.nodeName.toUpperCase()+"["+pos+"]/"+xpath;
891
+ }
892
+ el = el.parentNode;
880
893
  }
881
- el = el.parentNode;
894
+ xpath = '/'+xml.documentElement.nodeName.toUpperCase()+'/'+xpath;
895
+ xpath = xpath.replace(/\\/$/, '');
896
+ return xpath;
882
897
  }
883
- xpath = '/'+xml.documentElement.nodeName.toUpperCase()+'/'+xpath;
884
- xpath = xpath.replace(/\\/$/, '');
885
- return xpath;
898
+ JAVASCRIPT
886
899
  }
887
- JAVASCRIPT
888
900
  end
889
901
 
890
902
  def trigger(event)
@@ -899,7 +911,7 @@ module Capybara
899
911
  # Node.new(@driver, @page, @element.evaluate_handle('el => el.shadowRoot'))
900
912
  #
901
913
  # does not work well because of the Playwright Error 'Element is not attached to the DOM'
902
- self
914
+ ShadowRootNode.new(@driver, @page, @element)
903
915
  end
904
916
 
905
917
  def inspect
@@ -913,19 +925,19 @@ module Capybara
913
925
  end
914
926
 
915
927
  def find_xpath(query, **options)
916
- assert_element_not_stale
917
-
918
- @element.query_selector_all("xpath=#{query}").map do |el|
919
- Node.new(@driver, @page, el)
920
- end
928
+ assert_element_not_stale {
929
+ @element.query_selector_all("xpath=#{query}").map do |el|
930
+ Node.new(@driver, @page, el)
931
+ end
932
+ }
921
933
  end
922
934
 
923
935
  def find_css(query, **options)
924
- assert_element_not_stale
925
-
926
- @element.query_selector_all(query).map do |el|
927
- Node.new(@driver, @page, el)
928
- end
936
+ assert_element_not_stale {
937
+ @element.query_selector_all(query).map do |el|
938
+ Node.new(@driver, @page, el)
939
+ end
940
+ }
929
941
  end
930
942
  end
931
943
  end
@@ -27,6 +27,7 @@ module Capybara
27
27
  record_video_dir: nil,
28
28
  record_video_size: nil,
29
29
  screen: nil,
30
+ serviceWorkers: nil,
30
31
  storageState: nil,
31
32
  timezoneId: nil,
32
33
  userAgent: nil,
@@ -0,0 +1,39 @@
1
+ require_relative './node'
2
+
3
+ module Capybara
4
+ module Playwright
5
+ class ShadowRootNode < Node
6
+ def initialize(driver, page, element)
7
+ super
8
+ @shadow_roow_element = element.evaluate_handle('el => el.shadowRoot')
9
+ end
10
+
11
+ def all_text
12
+ assert_element_not_stale
13
+
14
+ text = @shadow_roow_element.text_content
15
+ text.to_s.gsub(/[\u200b\u200e\u200f]/, '')
16
+ .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
17
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
18
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
19
+ .tr("\u00a0", ' ')
20
+ end
21
+
22
+ def visible_text
23
+ assert_element_not_stale
24
+
25
+ return '' unless visible?
26
+
27
+ # https://github.com/teamcapybara/capybara/blob/1c164b608fa6452418ec13795b293655f8a0102a/lib/capybara/rack_test/node.rb#L18
28
+ displayed_text = @shadow_roow_element.text_content.to_s.
29
+ gsub(/[\u200b\u200e\u200f]/, '').
30
+ gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
31
+ displayed_text.squeeze(' ')
32
+ .gsub(/[\ \n]*\n[\ \n]*/, "\n")
33
+ .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
34
+ .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
35
+ .tr("\u00a0", ' ')
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Playwright
5
- VERSION = '0.3.0'
5
+ VERSION = '0.3.1'
6
6
  end
7
7
  end
@@ -11,4 +11,5 @@ require 'capybara/playwright/driver'
11
11
  require 'capybara/playwright/node'
12
12
  require 'capybara/playwright/page'
13
13
  require 'capybara/playwright/page_options'
14
+ require 'capybara/playwright/shadow_root_node'
14
15
  require 'capybara/playwright/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-playwright-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-23 00:00:00.000000000 Z
11
+ date: 2022-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-test_server
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rake
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -191,6 +205,7 @@ files:
191
205
  - lib/capybara/playwright/node.rb
192
206
  - lib/capybara/playwright/page.rb
193
207
  - lib/capybara/playwright/page_options.rb
208
+ - lib/capybara/playwright/shadow_root_node.rb
194
209
  - lib/capybara/playwright/tmpdir_owner.rb
195
210
  - lib/capybara/playwright/version.rb
196
211
  homepage: https://github.com/YusukeIwaki/capybara-playwright-driver
@@ -212,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
227
  - !ruby/object:Gem::Version
213
228
  version: '0'
214
229
  requirements: []
215
- rubygems_version: 3.3.7
230
+ rubygems_version: 3.3.26
216
231
  signing_key:
217
232
  specification_version: 4
218
233
  summary: Playwright driver for Capybara