apparition 0.2.0 → 0.3.0
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/lib/capybara/apparition.rb +0 -2
- data/lib/capybara/apparition/browser.rb +18 -129
- data/lib/capybara/apparition/browser/cookie.rb +3 -15
- data/lib/capybara/apparition/browser/header.rb +2 -2
- data/lib/capybara/apparition/browser/page_manager.rb +84 -0
- data/lib/capybara/apparition/browser/window.rb +7 -7
- data/lib/capybara/apparition/driver.rb +5 -2
- data/lib/capybara/apparition/node.rb +64 -49
- data/lib/capybara/apparition/node/drag.rb +4 -4
- data/lib/capybara/apparition/page.rb +33 -19
- data/lib/capybara/apparition/page/frame_manager.rb +1 -0
- data/lib/capybara/apparition/version.rb +1 -1
- metadata +47 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5675a80f435049166cf933b540e00b5569b0f516099597cdc071cc4c7767ff0
|
4
|
+
data.tar.gz: 8a63bf45f5376a4eb0a65954bd3863062f1aea67b030cb3ee2b96c7cfe1d108d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d0b64f71ec2e0a026bc5f0cea02588e550a5e27768b5368041bfbbd206e758654900cf22c35620d88f0618a76a12b874120513ae3d606e8ee481a5021bf3297
|
7
|
+
data.tar.gz: b279c8871baedd04acf1e717fddbf2898ae9796e0c1152fd17c0ccc160deaa10cb0cc86243037448883e10da3a90844351c60e097e0b6b5aebb8e87b0e796f65
|
data/lib/capybara/apparition.rb
CHANGED
@@ -9,6 +9,7 @@ require 'capybara/apparition/browser/window'
|
|
9
9
|
require 'capybara/apparition/browser/render'
|
10
10
|
require 'capybara/apparition/browser/cookie'
|
11
11
|
require 'capybara/apparition/browser/modal'
|
12
|
+
require 'capybara/apparition/browser/page_manager'
|
12
13
|
require 'capybara/apparition/browser/frame'
|
13
14
|
require 'capybara/apparition/browser/auth'
|
14
15
|
require 'json'
|
@@ -30,7 +31,7 @@ module Capybara::Apparition
|
|
30
31
|
def initialize(client, logger = nil)
|
31
32
|
@client = client
|
32
33
|
@current_page_handle = nil
|
33
|
-
@pages =
|
34
|
+
@pages = PageManager.new(self)
|
34
35
|
@context_id = nil
|
35
36
|
@js_errors = true
|
36
37
|
@ignore_https_errors = false
|
@@ -46,7 +47,7 @@ module Capybara::Apparition
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def restart
|
49
|
-
puts 'handle client restart'
|
50
|
+
# puts 'handle client restart'
|
50
51
|
# client.restart
|
51
52
|
|
52
53
|
self.debug = @debug if defined?(@debug)
|
@@ -79,35 +80,26 @@ module Capybara::Apparition
|
|
79
80
|
|
80
81
|
def reset
|
81
82
|
new_context_id = command('Target.createBrowserContext')['browserContextId']
|
82
|
-
# current_pages = @pages.keys
|
83
|
-
|
84
83
|
new_target_response = client.send_cmd('Target.createTarget', url: 'about:blank', browserContextId: new_context_id)
|
85
|
-
|
86
|
-
|
87
|
-
client.send_cmd('Target.disposeBrowserContext', browserContextId: page.browser_context_id).discard_result
|
88
|
-
rescue WrongWorld
|
89
|
-
puts 'Unknown browserContextId'
|
90
|
-
end
|
91
|
-
@pages.delete(id)
|
92
|
-
end
|
84
|
+
|
85
|
+
@pages.reset
|
93
86
|
|
94
87
|
new_target_id = new_target_response['targetId']
|
95
88
|
|
96
89
|
session_id = command('Target.attachToTarget', targetId: new_target_id)['sessionId']
|
97
90
|
session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_id)
|
98
91
|
|
99
|
-
@pages
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
@pages[new_target_id].send(:main_frame).loaded!
|
92
|
+
@pages.create(new_target_id, session, new_context_id,
|
93
|
+
ignore_https_errors: ignore_https_errors,
|
94
|
+
js_errors: js_errors, extensions: @extensions,
|
95
|
+
url_blacklist: @url_blacklist,
|
96
|
+
url_whitelist: @url_whitelist) .send(:main_frame).loaded!
|
105
97
|
|
106
98
|
timer = Capybara::Helpers.timer(expire_in: 10)
|
107
99
|
until @pages[new_target_id].usable?
|
108
100
|
if timer.expired?
|
109
101
|
puts 'Timedout waiting for reset'
|
110
|
-
raise TimeoutError
|
102
|
+
raise TimeoutError, 'reset'
|
111
103
|
end
|
112
104
|
sleep 0.01
|
113
105
|
end
|
@@ -117,42 +109,11 @@ module Capybara::Apparition
|
|
117
109
|
end
|
118
110
|
|
119
111
|
def refresh_pages(opener:)
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
session_result = client.send_cmd('Target.attachToTarget', targetId: target_id)
|
126
|
-
[target_id, session_result]
|
127
|
-
end
|
128
|
-
|
129
|
-
sessions = sessions.map do |(target_id, session_result)|
|
130
|
-
session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_result.result['sessionId'])
|
131
|
-
[target_id, session]
|
132
|
-
end
|
133
|
-
|
134
|
-
sessions.each do |(_target_id, session)|
|
135
|
-
session.async_commands 'Page.enable', 'Network.enable', 'Runtime.enable', 'Security.enable', 'DOM.enable'
|
136
|
-
end
|
137
|
-
|
138
|
-
# sessions.each do |(target_id, session_result)|
|
139
|
-
# session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_result.result['sessionId'])
|
140
|
-
sessions.each do |(target_id, session)|
|
141
|
-
page_options = { ignore_https_errors: ignore_https_errors, js_errors: js_errors,
|
142
|
-
url_blacklist: @url_blacklist, url_whitelist: @url_whitelist }
|
143
|
-
new_page = Page.create(self, session, target_id, opener.browser_context_id, page_options).inherit(opener)
|
144
|
-
@pages[target_id] = new_page
|
145
|
-
end
|
146
|
-
|
147
|
-
# new_pages.each do |page|
|
148
|
-
# target_id = page['targetId']
|
149
|
-
# session_id = command('Target.attachToTarget', targetId: target_id)['sessionId']
|
150
|
-
# session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_id)
|
151
|
-
# page_options = { ignore_https_errors: ignore_https_errors, js_errors: js_errors,
|
152
|
-
# url_blacklist: @url_blacklist, url_whitelist: @url_whitelist }
|
153
|
-
# new_page = Page.create(self, session, page['targetId'], opener.browser_context_id, page_options).inherit(opener)
|
154
|
-
# @pages[target_id] = new_page
|
155
|
-
# end
|
112
|
+
@pages.refresh(opener: opener,
|
113
|
+
ignore_https_errors: ignore_https_errors,
|
114
|
+
js_errors: js_errors,
|
115
|
+
url_blacklist: @url_blacklist,
|
116
|
+
url_whitelist: @url_whitelist)
|
156
117
|
end
|
157
118
|
|
158
119
|
def resize(width, height, screen: nil)
|
@@ -178,17 +139,11 @@ module Capybara::Apparition
|
|
178
139
|
end
|
179
140
|
|
180
141
|
def url_whitelist=(whitelist)
|
181
|
-
@url_whitelist = whitelist
|
182
|
-
@pages.each do |_id, page|
|
183
|
-
page.url_whitelist = whitelist
|
184
|
-
end
|
142
|
+
@url_whitelist = @pages.whitelist = whitelist
|
185
143
|
end
|
186
144
|
|
187
145
|
def url_blacklist=(blacklist)
|
188
|
-
@url_blacklist = blacklist
|
189
|
-
@pages.each do |_id, page|
|
190
|
-
page.url_blacklist = blacklist
|
191
|
-
end
|
146
|
+
@url_blacklist = @pages.blacklist = blacklist
|
192
147
|
end
|
193
148
|
|
194
149
|
attr_writer :debug
|
@@ -234,72 +189,6 @@ module Capybara::Apparition
|
|
234
189
|
@logger&.puts message if ENV['DEBUG']
|
235
190
|
end
|
236
191
|
|
237
|
-
# KEY_ALIASES = {
|
238
|
-
# command: :Meta,
|
239
|
-
# equals: :Equal,
|
240
|
-
# control: :Control,
|
241
|
-
# ctrl: :Control,
|
242
|
-
# multiply: 'numpad*',
|
243
|
-
# add: 'numpad+',
|
244
|
-
# divide: 'numpad/',
|
245
|
-
# subtract: 'numpad-',
|
246
|
-
# decimal: 'numpad.',
|
247
|
-
# left: 'ArrowLeft',
|
248
|
-
# right: 'ArrowRight',
|
249
|
-
# down: 'ArrowDown',
|
250
|
-
# up: 'ArrowUp'
|
251
|
-
# }.freeze
|
252
|
-
#
|
253
|
-
# def normalize_keys(keys)
|
254
|
-
# keys.map do |key_desc|
|
255
|
-
# case key_desc
|
256
|
-
# when Array
|
257
|
-
# # [:Shift, "s"] => { modifier: "shift", keys: "S" }
|
258
|
-
# # [:Shift, "string"] => { modifier: "shift", keys: "STRING" }
|
259
|
-
# # [:Ctrl, :Left] => { modifier: "ctrl", key: 'Left' }
|
260
|
-
# # [:Ctrl, :Shift, :Left] => { modifier: "ctrl,shift", key: 'Left' }
|
261
|
-
# # [:Ctrl, :Left, :Left] => { modifier: "ctrl", key: [:Left, :Left] }
|
262
|
-
# keys_chunks = key_desc.chunk do |k|
|
263
|
-
# k.is_a?(Symbol) && %w[shift ctrl control alt meta command].include?(k.to_s.downcase)
|
264
|
-
# end
|
265
|
-
# modifiers = modifiers_from_chunks(keys_chunks)
|
266
|
-
# letters = normalize_keys(_keys.next[1].map { |k| k.is_a?(String) ? k.upcase : k })
|
267
|
-
# { modifier: modifiers, keys: letters }
|
268
|
-
# when Symbol
|
269
|
-
# symbol_to_desc(key_desc)
|
270
|
-
# when String
|
271
|
-
# key_desc # Plain string, nothing to do
|
272
|
-
# end
|
273
|
-
# end
|
274
|
-
# end
|
275
|
-
#
|
276
|
-
# def modifiers_from_chunks(chunks)
|
277
|
-
# if chunks.peek[0]
|
278
|
-
# chunks.next[1].map do |k|
|
279
|
-
# k = k.to_s.downcase
|
280
|
-
# k = 'control' if k == 'ctrl'
|
281
|
-
# k = 'meta' if k == 'command'
|
282
|
-
# k
|
283
|
-
# end.join(',')
|
284
|
-
# else
|
285
|
-
# ''
|
286
|
-
# end
|
287
|
-
# end
|
288
|
-
#
|
289
|
-
# def symbol_to_desc(symbol)
|
290
|
-
# if symbol == :space
|
291
|
-
# res = ' '
|
292
|
-
# else
|
293
|
-
# key = KEY_ALIASES.fetch(symbol.downcase, symbol)
|
294
|
-
# if (match = key.to_s.match(/numpad(.)/))
|
295
|
-
# res = { keys: match[1], modifier: 'keypad' }
|
296
|
-
# elsif !/^[A-Z]/.match?(key)
|
297
|
-
# key = key.to_s.split('_').map(&:capitalize).join
|
298
|
-
# end
|
299
|
-
# end
|
300
|
-
# res || { key: key }
|
301
|
-
# end
|
302
|
-
|
303
192
|
def initialize_handlers
|
304
193
|
# @client.on 'Target.targetCreated' do |info|
|
305
194
|
# byebug
|
@@ -6,18 +6,9 @@ module Capybara::Apparition
|
|
6
6
|
class Browser
|
7
7
|
module Cookie
|
8
8
|
def cookies
|
9
|
-
CookieJar.new(
|
10
|
-
# current_page.command('Network.getCookies')['cookies'].map { |c| Cookie.new(c) }
|
11
|
-
self
|
12
|
-
)
|
13
|
-
end
|
14
|
-
|
15
|
-
def all_cookies
|
16
|
-
CookieJar.new(
|
17
|
-
# current_page.command('Network.getAllCookies')['cookies'].map { |c| Cookie.new(c) }
|
18
|
-
self
|
19
|
-
)
|
9
|
+
CookieJar.new(self)
|
20
10
|
end
|
11
|
+
alias :all_cookies :cookies
|
21
12
|
|
22
13
|
def get_raw_cookies
|
23
14
|
current_page.command('Network.getAllCookies')['cookies'].map do |c|
|
@@ -26,10 +17,7 @@ module Capybara::Apparition
|
|
26
17
|
end
|
27
18
|
|
28
19
|
def set_cookie(cookie)
|
29
|
-
if cookie[:expires]
|
30
|
-
# cookie[:expires] = cookie[:expires].to_i * 1000
|
31
|
-
cookie[:expires] = cookie[:expires].to_i
|
32
|
-
end
|
20
|
+
cookie[:expires] = cookie[:expires].to_i if cookie[:expires]
|
33
21
|
|
34
22
|
current_page.command('Network.setCookie', cookie)
|
35
23
|
end
|
@@ -8,7 +8,7 @@ module Capybara::Apparition
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def headers=(headers)
|
11
|
-
@pages.each do |
|
11
|
+
@pages.each do |page|
|
12
12
|
page.perm_headers = headers.dup
|
13
13
|
page.temp_headers = {}
|
14
14
|
page.temp_no_redirect_headers = {}
|
@@ -23,7 +23,7 @@ module Capybara::Apparition
|
|
23
23
|
|
24
24
|
def add_header(header, permanent: true, **_options)
|
25
25
|
if permanent == true
|
26
|
-
@pages.each do |
|
26
|
+
@pages.each do |page|
|
27
27
|
page.perm_headers.merge! header
|
28
28
|
page.update_headers
|
29
29
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capybara::Apparition
|
4
|
+
class Browser
|
5
|
+
class PageManager
|
6
|
+
def initialize(browser)
|
7
|
+
@browser = browser
|
8
|
+
@pages = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def ids
|
12
|
+
@pages.keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](id)
|
16
|
+
@pages[id]
|
17
|
+
end
|
18
|
+
|
19
|
+
def each(&block)
|
20
|
+
@pages.each_value(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset
|
24
|
+
@pages.each do |id, page|
|
25
|
+
begin
|
26
|
+
@browser.client.send_cmd(
|
27
|
+
'Target.disposeBrowserContext',
|
28
|
+
browserContextId: page.browser_context_id
|
29
|
+
).discard_result
|
30
|
+
rescue WrongWorld
|
31
|
+
puts 'Unknown browserContextId'
|
32
|
+
end
|
33
|
+
@pages.delete(id)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def create(id, session, ctx_id, **options)
|
38
|
+
@pages[id] = Page.create(@browser, session, id, ctx_id, **options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(id)
|
42
|
+
@pages.delete(id)
|
43
|
+
end
|
44
|
+
|
45
|
+
def refresh(opener:, **page_options)
|
46
|
+
new_pages = @browser.command('Target.getTargets')['targetInfos'].select do |ti|
|
47
|
+
(ti['openerId'] == opener.target_id) && (ti['type'] == 'page') && (ti['attached'] == false)
|
48
|
+
end
|
49
|
+
|
50
|
+
sessions = new_pages.map do |page|
|
51
|
+
target_id = page['targetId']
|
52
|
+
session_result = @browser.client.send_cmd('Target.attachToTarget', targetId: target_id)
|
53
|
+
[target_id, session_result]
|
54
|
+
end
|
55
|
+
|
56
|
+
sessions = sessions.map do |(target_id, session_result)|
|
57
|
+
session = Capybara::Apparition::DevToolsProtocol::Session.new(
|
58
|
+
@browser,
|
59
|
+
@browser.client,
|
60
|
+
session_result.result['sessionId']
|
61
|
+
)
|
62
|
+
[target_id, session]
|
63
|
+
end
|
64
|
+
|
65
|
+
sessions.each do |(_id, session)|
|
66
|
+
session.async_commands 'Page.enable', 'Network.enable', 'Runtime.enable', 'Security.enable', 'DOM.enable'
|
67
|
+
end
|
68
|
+
|
69
|
+
sessions.each do |(target_id, session)|
|
70
|
+
new_page = Page.create(@browser, session, target_id, opener.browser_context_id, page_options).inherit(opener)
|
71
|
+
@pages[target_id] = new_page
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def whitelist=(list)
|
76
|
+
@pages.each_value { |page| page.url_whitelist = list }
|
77
|
+
end
|
78
|
+
|
79
|
+
def blacklist=(list)
|
80
|
+
@pages.each_value { |page| page.url_blacklist = list }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -8,7 +8,7 @@ module Capybara::Apparition
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def window_handles
|
11
|
-
@pages.
|
11
|
+
@pages.ids
|
12
12
|
end
|
13
13
|
|
14
14
|
def switch_to_window(handle)
|
@@ -29,12 +29,12 @@ module Capybara::Apparition
|
|
29
29
|
target_id = command('Target.createTarget', url: 'about:blank', browserContextId: context_id)['targetId']
|
30
30
|
session_id = command('Target.attachToTarget', targetId: target_id)['sessionId']
|
31
31
|
session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_id)
|
32
|
-
@pages
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
@pages.create(target_id, session, context_id,
|
33
|
+
ignore_https_errors: ignore_https_errors,
|
34
|
+
js_errors: js_errors,
|
35
|
+
url_whitelist: @url_whitelist,
|
36
|
+
extensions: @extensions,
|
37
|
+
url_blacklist: @url_blacklist).inherit(current_page(allow_nil: true))
|
38
38
|
@pages[target_id].send(:main_frame).loaded!
|
39
39
|
target_id
|
40
40
|
end
|
@@ -100,7 +100,9 @@ module Capybara::Apparition
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def find(method, selector)
|
103
|
-
browser.find(method, selector).map
|
103
|
+
browser.find(method, selector).map do |page_id, id, attrs|
|
104
|
+
Capybara::Apparition::Node.new(self, page_id, id, attrs)
|
105
|
+
end
|
104
106
|
end
|
105
107
|
|
106
108
|
def find_xpath(selector)
|
@@ -521,7 +523,8 @@ module Capybara::Apparition
|
|
521
523
|
object_cache[arg]
|
522
524
|
when Hash
|
523
525
|
if (arg['subtype'] == 'node') && arg['objectId']
|
524
|
-
|
526
|
+
tag_name = arg['description'].split(/[\.#]/, 2)[0]
|
527
|
+
Capybara::Apparition::Node.new(self, browser.current_page, arg['objectId'], tag_name: tag_name)
|
525
528
|
else
|
526
529
|
object_cache[arg] = {}
|
527
530
|
arg.each { |k, v| object_cache[arg][k] = unwrap_script_result(v, object_cache) }
|
@@ -11,8 +11,8 @@ module Capybara::Apparition
|
|
11
11
|
|
12
12
|
attr_reader :page_id
|
13
13
|
|
14
|
-
def initialize(driver, page, remote_object)
|
15
|
-
super(driver, self)
|
14
|
+
def initialize(driver, page, remote_object, initial_cache)
|
15
|
+
super(driver, self, initial_cache)
|
16
16
|
@page = page
|
17
17
|
@remote_object = remote_object
|
18
18
|
end
|
@@ -30,7 +30,8 @@ module Capybara::Apparition
|
|
30
30
|
def find(method, selector)
|
31
31
|
js = method == :css ? FIND_CSS_JS : FIND_XPATH_JS
|
32
32
|
evaluate_on(js, value: selector).map do |r_o|
|
33
|
-
|
33
|
+
tag_name = r_o['description'].split(/[\.#]/, 2)[0]
|
34
|
+
Capybara::Apparition::Node.new(driver, @page, r_o['objectId'], tag_name: tag_name)
|
34
35
|
end
|
35
36
|
rescue ::Capybara::Apparition::BrowserError => e
|
36
37
|
raise unless /is not a valid (XPath expression|selector)/.match? e.name
|
@@ -165,8 +166,19 @@ module Capybara::Apparition
|
|
165
166
|
evaluate_on VISIBLE_JS
|
166
167
|
end
|
167
168
|
|
168
|
-
def obscured?(
|
169
|
-
|
169
|
+
def obscured?(**)
|
170
|
+
pos = visible_center(allow_scroll: false)
|
171
|
+
return true if pos.nil?
|
172
|
+
|
173
|
+
hit_node = @page.element_from_point(pos)
|
174
|
+
return true if hit_node.nil?
|
175
|
+
|
176
|
+
begin
|
177
|
+
return evaluate_on('el => !this.contains(el)', objectId: hit_node['objectId'])
|
178
|
+
rescue WrongWorld # rubocop:disable Lint/HandleExceptions
|
179
|
+
end
|
180
|
+
|
181
|
+
true
|
170
182
|
end
|
171
183
|
|
172
184
|
def checked?
|
@@ -256,9 +268,9 @@ module Capybara::Apparition
|
|
256
268
|
false
|
257
269
|
end
|
258
270
|
|
259
|
-
def send_keys(*keys, delay: 0, **
|
271
|
+
def send_keys(*keys, delay: 0, **opts)
|
260
272
|
click unless evaluate_on CURRENT_NODE_SELECTED_JS
|
261
|
-
|
273
|
+
_send_keys(*keys, delay: delay, **opts)
|
262
274
|
end
|
263
275
|
alias_method :send_key, :send_keys
|
264
276
|
|
@@ -311,8 +323,8 @@ module Capybara::Apparition
|
|
311
323
|
end
|
312
324
|
end
|
313
325
|
|
314
|
-
def visible_center
|
315
|
-
rect = in_view_bounding_rect
|
326
|
+
def visible_center(allow_scroll: true)
|
327
|
+
rect = in_view_bounding_rect(allow_scroll: allow_scroll)
|
316
328
|
return nil if rect.nil?
|
317
329
|
|
318
330
|
frame_offset = @page.current_frame_offset
|
@@ -405,8 +417,38 @@ module Capybara::Apparition
|
|
405
417
|
|
406
418
|
private
|
407
419
|
|
408
|
-
def
|
409
|
-
|
420
|
+
def focus
|
421
|
+
@page.command('DOM.focus', objectId: id)
|
422
|
+
end
|
423
|
+
|
424
|
+
def keys_to_send(value, clear)
|
425
|
+
case clear
|
426
|
+
when :backspace
|
427
|
+
# Clear field by sending the correct number of backspace keys.
|
428
|
+
[:end] + ([:backspace] * self.value.to_s.length) + [value]
|
429
|
+
when :none
|
430
|
+
[value]
|
431
|
+
when Array
|
432
|
+
clear << value
|
433
|
+
else
|
434
|
+
# Clear field by JavaScript assignment of the value property.
|
435
|
+
# Script can change a readonly element which user input cannot, so
|
436
|
+
# don't execute if readonly.
|
437
|
+
driver.execute_script <<~JS, self
|
438
|
+
if (!arguments[0].readOnly) {
|
439
|
+
arguments[0].value = ''
|
440
|
+
}
|
441
|
+
JS
|
442
|
+
[value]
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def _send_keys(*keys, delay: 0, **_opts)
|
447
|
+
@page.keyboard.type(keys, delay: delay)
|
448
|
+
end
|
449
|
+
|
450
|
+
def in_view_bounding_rect(allow_scroll: true)
|
451
|
+
evaluate_on('() => this.scrollIntoViewIfNeeded()') if allow_scroll
|
410
452
|
result = evaluate_on GET_BOUNDING_CLIENT_RECT_JS
|
411
453
|
result = result['model'] if result && result['model']
|
412
454
|
result
|
@@ -437,22 +479,9 @@ module Capybara::Apparition
|
|
437
479
|
value = value.to_s
|
438
480
|
if value.empty? && clear.nil?
|
439
481
|
evaluate_on CLEAR_ELEMENT_JS
|
440
|
-
elsif clear == :backspace
|
441
|
-
# Clear field by sending the correct number of backspace keys.
|
442
|
-
backspaces = [:backspace] * self.value.to_s.length
|
443
|
-
send_keys(*([:end] + backspaces + [value]), delay: delay)
|
444
|
-
elsif clear.is_a? Array
|
445
|
-
send_keys(*clear, value, delay: delay)
|
446
482
|
else
|
447
|
-
|
448
|
-
|
449
|
-
# don't execute if readonly.
|
450
|
-
driver.execute_script <<~JS, self unless clear == :none
|
451
|
-
if (!arguments[0].readOnly) {
|
452
|
-
arguments[0].value = ''
|
453
|
-
}
|
454
|
-
JS
|
455
|
-
send_keys(value, delay: delay)
|
483
|
+
focus
|
484
|
+
_send_keys(*keys_to_send(value, clear), delay: delay)
|
456
485
|
end
|
457
486
|
end
|
458
487
|
|
@@ -511,7 +540,8 @@ module Capybara::Apparition
|
|
511
540
|
r_o = @page.element_from_point(x: x, y: y)
|
512
541
|
return nil unless r_o && r_o['objectId']
|
513
542
|
|
514
|
-
|
543
|
+
tag_name = r_o['description'].split(/[\.#]/, 2)[0]
|
544
|
+
hit_node = Capybara::Apparition::Node.new(driver, @page, r_o['objectId'], tag_name: tag_name)
|
515
545
|
result = begin
|
516
546
|
evaluate_on(<<~JS, objectId: hit_node.id)
|
517
547
|
(hit_node) => {
|
@@ -681,11 +711,14 @@ module Capybara::Apparition
|
|
681
711
|
sel = sel.parentNode;
|
682
712
|
}
|
683
713
|
let event_options = { bubbles: true, cancelable: true };
|
714
|
+
sel.dispatchEvent(new MouseEvent('mousedown', event_options));
|
684
715
|
sel.dispatchEvent(new FocusEvent('focus', event_options));
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
716
|
+
if (this.selected == false){
|
717
|
+
this.selected = true;
|
718
|
+
sel.dispatchEvent(new Event('change', event_options));
|
719
|
+
}
|
720
|
+
sel.dispatchEvent(new MouseEvent('mouseup', event_options));
|
721
|
+
sel.dispatchEvent(new MouseEvent('click', event_options));
|
689
722
|
sel.dispatchEvent(new FocusEvent('blur', event_options));
|
690
723
|
}
|
691
724
|
JS
|
@@ -733,24 +766,6 @@ module Capybara::Apparition
|
|
733
766
|
}
|
734
767
|
JS
|
735
768
|
|
736
|
-
OBSCURED_JS = <<~JS
|
737
|
-
function(x, y) {
|
738
|
-
var box = this.getBoundingClientRect();
|
739
|
-
if (x == null) x = box.width/2;
|
740
|
-
if (y == null) y = box.height/2 ;
|
741
|
-
|
742
|
-
var px = box.left + x,
|
743
|
-
py = box.top + y,
|
744
|
-
e = document.elementFromPoint(px, py);
|
745
|
-
|
746
|
-
if (!this.contains(e))
|
747
|
-
return true;
|
748
|
-
|
749
|
-
return { x: px, y: py };
|
750
|
-
}
|
751
|
-
JS
|
752
|
-
|
753
|
-
|
754
769
|
DELETE_TEXT_JS = <<~JS
|
755
770
|
function(){
|
756
771
|
range = document.createRange();
|
@@ -42,16 +42,17 @@ module Capybara::Apparition
|
|
42
42
|
def drop(*args)
|
43
43
|
if args[0].is_a? String
|
44
44
|
input = evaluate_on ATTACH_FILE
|
45
|
-
|
45
|
+
tag_name = input['description'].split(/[\.#]/, 2)[0]
|
46
|
+
input = Capybara::Apparition::Node.new(driver, @page, input['objectId'], tag_name: tag_name)
|
46
47
|
input.set(args)
|
47
|
-
evaluate_on DROP_FILE,
|
48
|
+
evaluate_on DROP_FILE, objectId: input.id
|
48
49
|
else
|
49
50
|
items = args.each_with_object([]) do |arg, arr|
|
50
51
|
arg.each_with_object(arr) do |(type, data), arr_|
|
51
52
|
arr_ << { type: type, data: data }
|
52
53
|
end
|
53
54
|
end
|
54
|
-
evaluate_on DROP_STRING,
|
55
|
+
evaluate_on DROP_STRING, value: items
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -103,7 +104,6 @@ module Capybara::Apparition
|
|
103
104
|
}
|
104
105
|
JS
|
105
106
|
|
106
|
-
|
107
107
|
private
|
108
108
|
|
109
109
|
def html5_drag_to(element)
|
@@ -145,10 +145,11 @@ module Capybara::Apparition
|
|
145
145
|
pixel_ratio = evaluate('window.devicePixelRatio')
|
146
146
|
scale = (@browser.zoom_factor || 1).to_f / pixel_ratio
|
147
147
|
if options[:format].to_s == 'pdf'
|
148
|
-
params = {}
|
149
|
-
|
150
|
-
|
151
|
-
|
148
|
+
params = { scale: scale }
|
149
|
+
if @browser.paper_size
|
150
|
+
params[:paperWidth] = @browser.paper_size[:width].to_f
|
151
|
+
params[:paperHeight] = @browser.paper_size[:height].to_f
|
152
|
+
end
|
152
153
|
command('Page.printToPDF', params)
|
153
154
|
else
|
154
155
|
clip_options = if options[:selector]
|
@@ -173,15 +174,14 @@ module Capybara::Apparition
|
|
173
174
|
frame_id = node['node']['frameId']
|
174
175
|
|
175
176
|
timer = Capybara::Helpers.timer(expire_in: 10)
|
176
|
-
while (frame = @frames
|
177
|
+
while (frame = @frames[frame_id]).nil? || frame.loading?
|
177
178
|
# Wait for the frame creation messages to be processed
|
178
179
|
if timer.expired?
|
179
|
-
puts 'Timed out waiting
|
180
|
+
puts 'Timed out waiting for frame to be ready'
|
180
181
|
raise TimeoutError.new('push_frame')
|
181
182
|
end
|
182
183
|
sleep 0.1
|
183
184
|
end
|
184
|
-
return unless frame
|
185
185
|
|
186
186
|
frame.element_id = frame_el.base.id
|
187
187
|
@frames.push_frame(frame.id)
|
@@ -196,8 +196,8 @@ module Capybara::Apparition
|
|
196
196
|
wait_for_loaded
|
197
197
|
js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
|
198
198
|
query = method == :css ? CSS_FIND_JS : XPATH_FIND_JS
|
199
|
-
result = _raw_evaluate(query
|
200
|
-
(result || []).map { |r_o| [self, r_o['objectId']] }
|
199
|
+
result = _raw_evaluate(format(query, selector: js_escaped_selector))
|
200
|
+
(result || []).map { |r_o| [self, r_o['objectId'], tag_name: r_o['description'].split(/[\.#]/, 2)[0]] }
|
201
201
|
rescue ::Capybara::Apparition::BrowserError => e
|
202
202
|
raise unless /is not a valid (XPath expression|selector)/.match? e.name
|
203
203
|
|
@@ -241,15 +241,29 @@ module Capybara::Apparition
|
|
241
241
|
attr_reader :status_code
|
242
242
|
|
243
243
|
def wait_for_loaded(allow_obsolete: false)
|
244
|
+
# We can't reliably detect if the page is loaded, so just ensure the context
|
245
|
+
# is usable
|
244
246
|
timer = Capybara::Helpers.timer(expire_in: 30)
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
247
|
+
page_function = '(function(){ return 1 == 1; })()'
|
248
|
+
begin
|
249
|
+
response = command('Runtime.evaluate',
|
250
|
+
expression: page_function,
|
251
|
+
contextId: current_frame.context_id,
|
252
|
+
returnByValue: false,
|
253
|
+
awaitPromise: true)
|
254
|
+
process_response(response)
|
255
|
+
current_frame.loaded!
|
256
|
+
rescue # rubocop:disable Style/RescueStandardError
|
257
|
+
return if allow_obsolete && current_frame.obsolete?
|
258
|
+
|
259
|
+
unless timer.expired?
|
260
|
+
sleep 0.05
|
261
|
+
retry
|
250
262
|
end
|
251
|
-
|
263
|
+
puts 'Timedout waiting for page to be loaded' if ENV['DEBUG']
|
264
|
+
raise TimeoutError.new('wait_for_loaded')
|
252
265
|
end
|
266
|
+
|
253
267
|
raise JavascriptError.new(js_error) if @js_error
|
254
268
|
end
|
255
269
|
|
@@ -702,8 +716,8 @@ module Capybara::Apparition
|
|
702
716
|
def process_response(response)
|
703
717
|
return nil if response.nil?
|
704
718
|
|
705
|
-
|
706
|
-
if
|
719
|
+
exception = response['exceptionDetails']&.dig('exception')
|
720
|
+
if exception
|
707
721
|
case exception['className']
|
708
722
|
when 'DOMException'
|
709
723
|
raise ::Capybara::Apparition::BrowserError.new('name' => exception['description'], 'args' => nil)
|
@@ -798,12 +812,12 @@ module Capybara::Apparition
|
|
798
812
|
JS
|
799
813
|
|
800
814
|
CSS_FIND_JS = <<~JS
|
801
|
-
Array.from(document.querySelectorAll("
|
815
|
+
Array.from(document.querySelectorAll("%<selector>s"));
|
802
816
|
JS
|
803
817
|
|
804
818
|
XPATH_FIND_JS = <<~JS
|
805
819
|
(function(){
|
806
|
-
const xpath = document.evaluate("
|
820
|
+
const xpath = document.evaluate("%<selector>s", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
807
821
|
let results = [];
|
808
822
|
for (let i=0; i < xpath.snapshotLength; i++){
|
809
823
|
results.push(xpath.snapshotItem(i))
|
metadata
CHANGED
@@ -1,36 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apparition
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Walpole
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: backports
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: capybara
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
17
|
- - "~>"
|
32
18
|
- !ruby/object:Gem::Version
|
33
|
-
version: '3.
|
19
|
+
version: '3.13'
|
34
20
|
- - "<"
|
35
21
|
- !ruby/object:Gem::Version
|
36
22
|
version: '4'
|
@@ -40,7 +26,7 @@ dependencies:
|
|
40
26
|
requirements:
|
41
27
|
- - "~>"
|
42
28
|
- !ruby/object:Gem::Version
|
43
|
-
version: '3.
|
29
|
+
version: '3.13'
|
44
30
|
- - "<"
|
45
31
|
- !ruby/object:Gem::Version
|
46
32
|
version: '4'
|
@@ -184,6 +170,48 @@ dependencies:
|
|
184
170
|
- - "~>"
|
185
171
|
- !ruby/object:Gem::Version
|
186
172
|
version: '3.6'
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: rubocop
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
type: :development
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: rubocop-performance
|
189
|
+
requirement: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
type: :development
|
195
|
+
prerelease: false
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: rubocop-rspec
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
type: :development
|
209
|
+
prerelease: false
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
187
215
|
- !ruby/object:Gem::Dependency
|
188
216
|
name: selenium-webdriver
|
189
217
|
requirement: !ruby/object:Gem::Requirement
|
@@ -229,6 +257,7 @@ files:
|
|
229
257
|
- lib/capybara/apparition/browser/frame.rb
|
230
258
|
- lib/capybara/apparition/browser/header.rb
|
231
259
|
- lib/capybara/apparition/browser/modal.rb
|
260
|
+
- lib/capybara/apparition/browser/page_manager.rb
|
232
261
|
- lib/capybara/apparition/browser/render.rb
|
233
262
|
- lib/capybara/apparition/browser/window.rb
|
234
263
|
- lib/capybara/apparition/configuration.rb
|