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 +4 -4
- data/capybara-playwright.gemspec +1 -0
- data/lib/capybara/playwright/browser.rb +112 -89
- data/lib/capybara/playwright/node.rb +131 -119
- data/lib/capybara/playwright/page_options.rb +1 -0
- data/lib/capybara/playwright/shadow_root_node.rb +39 -0
- data/lib/capybara/playwright/version.rb +1 -1
- data/lib/capybara/playwright.rb +1 -0
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 931fea1c143e8de209fb6a360e072b0b5dfd724e7f9d240d8a4b0a36b39b443e
|
4
|
+
data.tar.gz: 53fa84147fadb311502981d7a51311d147a140f6983cd8d66567cea79918d332
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e3e16c8377aa75d7fb5716e875b0291d50332ef9e447c52205b0dab27cfd8de95d32388112a3f1a280137569eba270f77b236f826dcc7117d374ff43b18943c
|
7
|
+
data.tar.gz: cc15d0f5a5ddfa9d024d87e800f9e073df34220318b68465e9e77ff4bfc8ac3ce3a3cd7ed7e393b9b884de85fbf0ac4eae65b4e7ee68740cb4c68eafa17524c9
|
data/capybara-playwright.gemspec
CHANGED
@@ -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
|
-
|
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
|
-
|
68
|
+
@playwright_page.capybara_current_frame.goto(url)
|
69
|
+
}
|
70
70
|
end
|
71
71
|
|
72
72
|
def refresh
|
73
|
-
assert_page_alive
|
74
|
-
|
75
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
149
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
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
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
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
|
798
|
-
|
799
|
-
|
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
|
-
|
814
|
+
return true;
|
802
815
|
}
|
803
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
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
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
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
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
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
|
-
|
894
|
+
xpath = '/'+xml.documentElement.nodeName.toUpperCase()+'/'+xpath;
|
895
|
+
xpath = xpath.replace(/\\/$/, '');
|
896
|
+
return xpath;
|
882
897
|
}
|
883
|
-
|
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
|
-
|
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
|
-
|
919
|
-
|
920
|
-
|
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
|
-
|
927
|
-
|
928
|
-
|
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
|
@@ -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
|
data/lib/capybara/playwright.rb
CHANGED
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.
|
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-
|
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.
|
230
|
+
rubygems_version: 3.3.26
|
216
231
|
signing_key:
|
217
232
|
specification_version: 4
|
218
233
|
summary: Playwright driver for Capybara
|