apparition 0.1.0 → 0.2.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/README.md +1 -1
- data/lib/capybara/apparition/browser.rb +102 -56
- data/lib/capybara/apparition/browser/header.rb +2 -2
- data/lib/capybara/apparition/browser/window.rb +29 -29
- data/lib/capybara/apparition/configuration.rb +100 -0
- data/lib/capybara/apparition/dev_tools_protocol/remote_object.rb +19 -7
- data/lib/capybara/apparition/dev_tools_protocol/session.rb +2 -3
- data/lib/capybara/apparition/driver.rb +95 -21
- data/lib/capybara/apparition/driver/chrome_client.rb +3 -3
- data/lib/capybara/apparition/driver/launcher.rb +27 -15
- data/lib/capybara/apparition/driver/response.rb +1 -1
- data/lib/capybara/apparition/errors.rb +3 -3
- data/lib/capybara/apparition/node.rb +44 -3
- data/lib/capybara/apparition/node/drag.rb +65 -0
- data/lib/capybara/apparition/page.rb +55 -37
- data/lib/capybara/apparition/page/frame.rb +3 -0
- data/lib/capybara/apparition/page/frame_manager.rb +1 -1
- data/lib/capybara/apparition/page/keyboard.rb +8 -1
- data/lib/capybara/apparition/utility.rb +1 -1
- data/lib/capybara/apparition/version.rb +1 -1
- metadata +5 -6
- data/lib/capybara/apparition/dev_tools_protocol/target.rb +0 -64
- data/lib/capybara/apparition/dev_tools_protocol/target_manager.rb +0 -48
@@ -39,6 +39,71 @@ module Capybara::Apparition
|
|
39
39
|
@page.mouse.up
|
40
40
|
end
|
41
41
|
|
42
|
+
def drop(*args)
|
43
|
+
if args[0].is_a? String
|
44
|
+
input = evaluate_on ATTACH_FILE
|
45
|
+
input = Capybara::Apparition::Node.new(driver, @page, input['objectId'])
|
46
|
+
input.set(args)
|
47
|
+
evaluate_on DROP_FILE, { objectId: input.id }
|
48
|
+
else
|
49
|
+
items = args.each_with_object([]) do |arg, arr|
|
50
|
+
arg.each_with_object(arr) do |(type, data), arr_|
|
51
|
+
arr_ << { type: type, data: data }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
evaluate_on DROP_STRING, { value: items }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
DROP_STRING = <<~JS
|
59
|
+
function(strings){
|
60
|
+
var dt = new DataTransfer(),
|
61
|
+
opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
62
|
+
for (var i=0; i < strings.length; i++){
|
63
|
+
if (dt.items) {
|
64
|
+
dt.items.add(strings[i]['data'], strings[i]['type']);
|
65
|
+
} else {
|
66
|
+
dt.setData(strings[i]['type'], strings[i]['data']);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
var dropEvent = new DragEvent('drop', opts);
|
70
|
+
this.dispatchEvent(dropEvent);
|
71
|
+
}
|
72
|
+
JS
|
73
|
+
|
74
|
+
DROP_FILE = <<~JS
|
75
|
+
function(input){
|
76
|
+
var files = input.files,
|
77
|
+
dt = new DataTransfer(),
|
78
|
+
opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
79
|
+
input.parentElement.removeChild(input);
|
80
|
+
if (dt.items){
|
81
|
+
for (var i=0; i<files.length; i++){
|
82
|
+
dt.items.add(files[i]);
|
83
|
+
}
|
84
|
+
} else {
|
85
|
+
Object.defineProperty(dt, "files", {
|
86
|
+
value: files,
|
87
|
+
writable: false
|
88
|
+
});
|
89
|
+
}
|
90
|
+
var dropEvent = new DragEvent('drop', opts);
|
91
|
+
this.dispatchEvent(dropEvent);
|
92
|
+
}
|
93
|
+
JS
|
94
|
+
|
95
|
+
ATTACH_FILE = <<~JS
|
96
|
+
function(){
|
97
|
+
var input = document.createElement('INPUT');
|
98
|
+
input.type = "file";
|
99
|
+
input.id = "_capybara_drop_file";
|
100
|
+
input.multiple = true;
|
101
|
+
document.body.appendChild(input);
|
102
|
+
return input;
|
103
|
+
}
|
104
|
+
JS
|
105
|
+
|
106
|
+
|
42
107
|
private
|
43
108
|
|
44
109
|
def html5_drag_to(element)
|
@@ -10,44 +10,43 @@ module Capybara::Apparition
|
|
10
10
|
attr_reader :modal_messages
|
11
11
|
attr_reader :mouse, :keyboard
|
12
12
|
attr_reader :viewport_size
|
13
|
+
attr_reader :browser_context_id
|
13
14
|
attr_accessor :perm_headers, :temp_headers, :temp_no_redirect_headers
|
14
15
|
attr_reader :network_traffic
|
16
|
+
attr_reader :target_id
|
15
17
|
|
16
|
-
def self.create(browser, session, id,
|
17
|
-
|
18
|
+
def self.create(browser, session, id, browser_context_id,
|
19
|
+
ignore_https_errors: false, **options)
|
20
|
+
session.async_command 'Page.enable'
|
18
21
|
|
19
22
|
# Provides a lot of info - but huge overhead
|
20
23
|
# session.command 'Page.setLifecycleEventsEnabled', enabled: true
|
21
24
|
|
22
|
-
page = Page.new(browser, session, id,
|
25
|
+
page = Page.new(browser, session, id, browser_context_id, options)
|
23
26
|
|
24
27
|
session.async_commands 'Network.enable', 'Runtime.enable', 'Security.enable', 'DOM.enable'
|
25
|
-
|
26
|
-
# session.command 'Runtime.enable'
|
27
|
-
# session.command 'Security.enable'
|
28
|
-
# session.command 'Security.setOverrideCertificateErrors', override: true if ignore_https_errors
|
29
|
-
session.command 'Security.setIgnoreCertificateErrors', ignore: !!ignore_https_errors
|
30
|
-
# session.command 'DOM.enable'
|
31
|
-
# session.command 'Log.enable'
|
28
|
+
session.async_command 'Security.setIgnoreCertificateErrors', ignore: !!ignore_https_errors
|
32
29
|
if Capybara.save_path
|
33
|
-
session.
|
30
|
+
session.async_command 'Page.setDownloadBehavior', behavior: 'allow', downloadPath: Capybara.save_path
|
34
31
|
end
|
35
32
|
page
|
36
33
|
end
|
37
34
|
|
38
|
-
def initialize(browser, session,
|
39
|
-
|
35
|
+
def initialize(browser, session, target_id, browser_context_id,
|
36
|
+
js_errors: false, url_blacklist: [], url_whitelist: [], extensions: [])
|
37
|
+
@target_id = target_id
|
38
|
+
@browser_context_id = browser_context_id
|
40
39
|
@browser = browser
|
41
40
|
@session = session
|
42
41
|
@keyboard = Keyboard.new(self)
|
43
42
|
@mouse = Mouse.new(self, @keyboard)
|
44
43
|
@modals = []
|
45
44
|
@modal_messages = []
|
46
|
-
@frames = Capybara::Apparition::FrameManager.new(
|
45
|
+
@frames = Capybara::Apparition::FrameManager.new(@target_id)
|
47
46
|
@response_headers = {}
|
48
47
|
@status_code = 0
|
49
|
-
@url_blacklist = []
|
50
|
-
@url_whitelist = []
|
48
|
+
@url_blacklist = url_blacklist || []
|
49
|
+
@url_whitelist = url_whitelist || []
|
51
50
|
@credentials = nil
|
52
51
|
@auth_attempts = []
|
53
52
|
@proxy_credentials = nil
|
@@ -67,6 +66,10 @@ module Capybara::Apparition
|
|
67
66
|
|
68
67
|
register_js_error_handler # if js_errors
|
69
68
|
|
69
|
+
extensions.each do |name|
|
70
|
+
add_extension(name)
|
71
|
+
end
|
72
|
+
|
70
73
|
setup_network_interception if browser.proxy_auth
|
71
74
|
end
|
72
75
|
|
@@ -84,6 +87,12 @@ module Capybara::Apparition
|
|
84
87
|
@perm_headers = {}
|
85
88
|
end
|
86
89
|
|
90
|
+
def add_extension(filename)
|
91
|
+
command('Page.addScriptToEvaluateOnNewDocument', source: File.read(filename))
|
92
|
+
rescue Errno::ENOENT
|
93
|
+
raise ::Capybara::Apparition::BrowserError.new('name' => "Unable to load extension: #{filename}", 'args' => nil)
|
94
|
+
end
|
95
|
+
|
87
96
|
def add_modal(modal_response)
|
88
97
|
@last_modal_message = nil
|
89
98
|
@modals.push(modal_response)
|
@@ -190,9 +199,9 @@ module Capybara::Apparition
|
|
190
199
|
result = _raw_evaluate(query % js_escaped_selector)
|
191
200
|
(result || []).map { |r_o| [self, r_o['objectId']] }
|
192
201
|
rescue ::Capybara::Apparition::BrowserError => e
|
193
|
-
raise unless
|
202
|
+
raise unless /is not a valid (XPath expression|selector)/.match? e.name
|
194
203
|
|
195
|
-
raise Capybara::Apparition::InvalidSelector, [method, selector]
|
204
|
+
raise Capybara::Apparition::InvalidSelector, 'args' => [method, selector]
|
196
205
|
end
|
197
206
|
|
198
207
|
def execute(script, *args)
|
@@ -225,12 +234,14 @@ module Capybara::Apparition
|
|
225
234
|
go_history(+1)
|
226
235
|
end
|
227
236
|
|
228
|
-
|
237
|
+
def response_headers
|
238
|
+
@response_headers[current_frame.id] || {}
|
239
|
+
end
|
229
240
|
|
230
241
|
attr_reader :status_code
|
231
242
|
|
232
243
|
def wait_for_loaded(allow_obsolete: false)
|
233
|
-
timer = Capybara::Helpers.timer(expire_in:
|
244
|
+
timer = Capybara::Helpers.timer(expire_in: 30)
|
234
245
|
cf = current_frame
|
235
246
|
until cf.usable? || (allow_obsolete && cf.obsolete?) || @js_error
|
236
247
|
if timer.expired?
|
@@ -276,7 +287,7 @@ module Capybara::Apparition
|
|
276
287
|
|
277
288
|
def element_from_point(x:, y:)
|
278
289
|
r_o = _raw_evaluate("document.elementFromPoint(#{x}, #{y})", context_id: main_frame.context_id)
|
279
|
-
while r_o && (r_o['description']
|
290
|
+
while r_o && (/^iframe/.match? r_o['description'])
|
280
291
|
frame_node = command('DOM.describeNode', objectId: r_o['objectId'])
|
281
292
|
frame = @frames.get(frame_node.dig('node', 'frameId'))
|
282
293
|
fo = frame_offset(frame)
|
@@ -291,7 +302,7 @@ module Capybara::Apparition
|
|
291
302
|
end
|
292
303
|
|
293
304
|
def set_viewport(width:, height:, screen: nil)
|
294
|
-
wait_for_loaded
|
305
|
+
# wait_for_loaded
|
295
306
|
@viewport_size = { width: width, height: height }
|
296
307
|
result = @browser.command('Browser.getWindowForTarget', targetId: @target_id)
|
297
308
|
begin
|
@@ -351,7 +362,7 @@ module Capybara::Apparition
|
|
351
362
|
|
352
363
|
def update_headers(async: false)
|
353
364
|
method = async ? :async_command : :command
|
354
|
-
if (ua = extra_headers.find { |k, _v|
|
365
|
+
if (ua = extra_headers.find { |k, _v| /^User-Agent$/i.match? k })
|
355
366
|
send(method, 'Network.setUserAgentOverride', userAgent: ua[1])
|
356
367
|
end
|
357
368
|
send(method, 'Network.setExtraHTTPHeaders', headers: extra_headers)
|
@@ -377,6 +388,14 @@ module Capybara::Apparition
|
|
377
388
|
|
378
389
|
attr_reader :url_blacklist, :url_whitelist
|
379
390
|
|
391
|
+
def current_frame
|
392
|
+
@frames.current
|
393
|
+
end
|
394
|
+
|
395
|
+
def main_frame
|
396
|
+
@frames.main
|
397
|
+
end
|
398
|
+
|
380
399
|
private
|
381
400
|
|
382
401
|
def eval_wrapped_script(wrapper, script, args)
|
@@ -420,8 +439,7 @@ module Capybara::Apparition
|
|
420
439
|
|
421
440
|
@session.on 'Page.windowOpen' do |params|
|
422
441
|
puts "**** windowOpen was called with: #{params}" if ENV['DEBUG']
|
423
|
-
|
424
|
-
sleep 0.4 # wait a bit so the window has time to start loading
|
442
|
+
@browser.refresh_pages(opener: self)
|
425
443
|
end
|
426
444
|
|
427
445
|
@session.on 'Page.frameAttached' do |params|
|
@@ -441,13 +459,16 @@ module Capybara::Apparition
|
|
441
459
|
puts "**** creating frome for #{frame_params['id']}" if ENV['DEBUG']
|
442
460
|
@frames.add(frame_params['id'], frame_params)
|
443
461
|
end
|
462
|
+
@frames.get(frame_params['id'])&.loading(frame_params['loaderId'] || -1)
|
444
463
|
end
|
445
464
|
|
446
465
|
@session.on 'Page.frameStartedLoading' do |params|
|
466
|
+
puts "Setting loading for #{params['frameId']}" if ENV['DEBUG']
|
447
467
|
@frames.get(params['frameId'])&.loading(-1)
|
448
468
|
end
|
449
469
|
|
450
470
|
@session.on 'Page.frameStoppedLoading' do |params|
|
471
|
+
puts "Setting loaded for #{params['frameId']}" if ENV['DEBUG']
|
451
472
|
@frames.get(params['frameId'])&.loaded!
|
452
473
|
end
|
453
474
|
|
@@ -516,7 +537,7 @@ module Capybara::Apparition
|
|
516
537
|
|
517
538
|
@session.on 'Network.responseReceived' do |params|
|
518
539
|
if params['type'] == 'Document'
|
519
|
-
@response_headers = params['response']['headers']
|
540
|
+
@response_headers[params['frameId']] = params['response']['headers']
|
520
541
|
@status_code = params['response']['status']
|
521
542
|
end
|
522
543
|
end
|
@@ -596,7 +617,7 @@ module Capybara::Apparition
|
|
596
617
|
unless @temp_no_redirect_headers.empty? || !navigation
|
597
618
|
headers.delete_if { |name, value| @temp_no_redirect_headers[name] == value }
|
598
619
|
end
|
599
|
-
if (accept = perm_headers.keys.find { |k|
|
620
|
+
if (accept = perm_headers.keys.find { |k| /accept/i.match? k })
|
600
621
|
headers[accept] = perm_headers[accept]
|
601
622
|
end
|
602
623
|
|
@@ -621,14 +642,6 @@ module Capybara::Apparition
|
|
621
642
|
async_command 'Network.continueInterceptedRequest', errorReason: reason, interceptionId: id
|
622
643
|
end
|
623
644
|
|
624
|
-
def current_frame
|
625
|
-
@frames.current
|
626
|
-
end
|
627
|
-
|
628
|
-
def main_frame
|
629
|
-
@frames.main
|
630
|
-
end
|
631
|
-
|
632
645
|
def go_history(delta)
|
633
646
|
history = command('Page.getNavigationHistory')
|
634
647
|
entry = history['entries'][history['currentIndex'] + delta]
|
@@ -667,7 +680,8 @@ module Capybara::Apparition
|
|
667
680
|
executionContextId: context_id,
|
668
681
|
arguments: args,
|
669
682
|
returnByValue: false,
|
670
|
-
awaitPromise: true
|
683
|
+
awaitPromise: true,
|
684
|
+
userGesture: true)
|
671
685
|
process_response(response)
|
672
686
|
end
|
673
687
|
|
@@ -754,7 +768,11 @@ module Capybara::Apparition
|
|
754
768
|
function(){
|
755
769
|
let apparitionId=0;
|
756
770
|
return (function ider(obj){
|
757
|
-
if (obj &&
|
771
|
+
if (obj &&
|
772
|
+
(typeof obj == 'object') &&
|
773
|
+
!(obj instanceof HTMLElement) &&
|
774
|
+
!(obj instanceof CSSStyleDeclaration) &&
|
775
|
+
!obj.apparitionId){
|
758
776
|
obj.apparitionId = ++apparitionId;
|
759
777
|
Reflect.ownKeys(obj).forEach(key => ider(obj[key]))
|
760
778
|
}
|
@@ -47,10 +47,12 @@ module Capybara::Apparition
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def loading(id)
|
50
|
+
puts "Setting loading to #{id}" if ENV['DEBUG']
|
50
51
|
self.loader_id = id
|
51
52
|
end
|
52
53
|
|
53
54
|
def reloading!
|
55
|
+
puts 'Reloading' if ENV['DEBUG']
|
54
56
|
self.loader_id = @prev_loader_id
|
55
57
|
end
|
56
58
|
|
@@ -64,6 +66,7 @@ module Capybara::Apparition
|
|
64
66
|
|
65
67
|
def loaded!
|
66
68
|
@prev_loader_id = loader_id
|
69
|
+
puts "Setting loaded - was #{loader_id}" if ENV['DEBUG']
|
67
70
|
self.loader_id = nil
|
68
71
|
end
|
69
72
|
|
@@ -102,8 +102,15 @@ module Capybara::Apparition
|
|
102
102
|
location: 0
|
103
103
|
)
|
104
104
|
|
105
|
+
if key.to_sym == :return
|
106
|
+
warn '[DEPRECATION]: Key symbol :return is deprecated in favor of :enter'
|
107
|
+
key = :enter
|
108
|
+
end
|
109
|
+
|
105
110
|
definition = KEY_DEFINITIONS[key.to_sym]
|
106
|
-
raise KeyError, "Unknown key: #{key}" if definition.nil?
|
111
|
+
raise KeyError, "Unknown key: #{key}" if definition.nil? && !key.is_a?(String)
|
112
|
+
|
113
|
+
definition ||= { text: key }
|
107
114
|
|
108
115
|
definition = OpenStruct.new definition
|
109
116
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apparition
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2019-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: backports
|
@@ -231,13 +231,12 @@ files:
|
|
231
231
|
- lib/capybara/apparition/browser/modal.rb
|
232
232
|
- lib/capybara/apparition/browser/render.rb
|
233
233
|
- lib/capybara/apparition/browser/window.rb
|
234
|
+
- lib/capybara/apparition/configuration.rb
|
234
235
|
- lib/capybara/apparition/console.rb
|
235
236
|
- lib/capybara/apparition/cookie.rb
|
236
237
|
- lib/capybara/apparition/cookie_jar.rb
|
237
238
|
- lib/capybara/apparition/dev_tools_protocol/remote_object.rb
|
238
239
|
- lib/capybara/apparition/dev_tools_protocol/session.rb
|
239
|
-
- lib/capybara/apparition/dev_tools_protocol/target.rb
|
240
|
-
- lib/capybara/apparition/dev_tools_protocol/target_manager.rb
|
241
240
|
- lib/capybara/apparition/driver.rb
|
242
241
|
- lib/capybara/apparition/driver/chrome_client.rb
|
243
242
|
- lib/capybara/apparition/driver/launcher.rb
|
@@ -270,14 +269,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
270
269
|
requirements:
|
271
270
|
- - ">="
|
272
271
|
- !ruby/object:Gem::Version
|
273
|
-
version: 2.
|
272
|
+
version: 2.4.0
|
274
273
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
275
274
|
requirements:
|
276
275
|
- - ">="
|
277
276
|
- !ruby/object:Gem::Version
|
278
277
|
version: '0'
|
279
278
|
requirements: []
|
280
|
-
rubygems_version: 3.0.
|
279
|
+
rubygems_version: 3.0.3
|
281
280
|
signing_key:
|
282
281
|
specification_version: 4
|
283
282
|
summary: Chrome driver using CDP for Capybara
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'capybara/apparition/dev_tools_protocol/session'
|
4
|
-
|
5
|
-
module Capybara::Apparition
|
6
|
-
module DevToolsProtocol
|
7
|
-
class Target
|
8
|
-
def initialize(browser, info)
|
9
|
-
@browser = browser
|
10
|
-
@info = info.dup
|
11
|
-
@page = nil
|
12
|
-
end
|
13
|
-
|
14
|
-
def info
|
15
|
-
@info.dup.freeze
|
16
|
-
end
|
17
|
-
|
18
|
-
def update(new_info)
|
19
|
-
@info ||= {}
|
20
|
-
@info.merge!(new_info)
|
21
|
-
info
|
22
|
-
end
|
23
|
-
|
24
|
-
def id
|
25
|
-
@info['targetId']
|
26
|
-
end
|
27
|
-
|
28
|
-
def context_id
|
29
|
-
@info['browserContextId']
|
30
|
-
end
|
31
|
-
|
32
|
-
def title
|
33
|
-
@info['title']
|
34
|
-
end
|
35
|
-
|
36
|
-
def url
|
37
|
-
@info['url']
|
38
|
-
end
|
39
|
-
|
40
|
-
def page
|
41
|
-
@page ||= begin
|
42
|
-
if info['type'] == 'page'
|
43
|
-
Page.create(@browser, create_session, id,
|
44
|
-
ignore_https_errors: @browser.ignore_https_errors,
|
45
|
-
js_errors: @browser.js_errors).inherit(@info.delete('inherit'))
|
46
|
-
else
|
47
|
-
nil
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def close
|
53
|
-
@browser.command('Target.closeTarget', targetId: id)
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def create_session
|
59
|
-
session_id = @browser.command('Target.attachToTarget', targetId: id)['sessionId']
|
60
|
-
Session.new(@browser, @browser.client, id, session_id)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|