capybara-playwright-driver 0.2.1 → 0.3.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 +4 -4
- data/capybara-playwright.gemspec +1 -0
- data/lib/capybara/playwright/browser.rb +112 -89
- data/lib/capybara/playwright/node.rb +141 -118
- 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,55 +854,66 @@ 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)
|
891
903
|
@element.dispatch_event(event)
|
892
904
|
end
|
893
905
|
|
906
|
+
def shadow_root
|
907
|
+
# Playwright does not distinguish shadow DOM.
|
908
|
+
# https://playwright.dev/docs/selectors#selecting-elements-in-shadow-dom
|
909
|
+
# Just do with Host element as shadow root Element.
|
910
|
+
#
|
911
|
+
# Node.new(@driver, @page, @element.evaluate_handle('el => el.shadowRoot'))
|
912
|
+
#
|
913
|
+
# does not work well because of the Playwright Error 'Element is not attached to the DOM'
|
914
|
+
ShadowRootNode.new(@driver, @page, @element)
|
915
|
+
end
|
916
|
+
|
894
917
|
def inspect
|
895
918
|
%(#<#{self.class} tag="#{tag_name}" path="#{path}">)
|
896
919
|
end
|
@@ -902,19 +925,19 @@ module Capybara
|
|
902
925
|
end
|
903
926
|
|
904
927
|
def find_xpath(query, **options)
|
905
|
-
assert_element_not_stale
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
928
|
+
assert_element_not_stale {
|
929
|
+
@element.query_selector_all("xpath=#{query}").map do |el|
|
930
|
+
Node.new(@driver, @page, el)
|
931
|
+
end
|
932
|
+
}
|
910
933
|
end
|
911
934
|
|
912
935
|
def find_css(query, **options)
|
913
|
-
assert_element_not_stale
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
936
|
+
assert_element_not_stale {
|
937
|
+
@element.query_selector_all(query).map do |el|
|
938
|
+
Node.new(@driver, @page, el)
|
939
|
+
end
|
940
|
+
}
|
918
941
|
end
|
919
942
|
end
|
920
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.
|
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
|