apparition 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -1
- data/lib/capybara/apparition/browser.rb +9 -2
- data/lib/capybara/apparition/browser/launcher.rb +25 -0
- data/lib/capybara/apparition/browser/launcher/local.rb +213 -0
- data/lib/capybara/apparition/browser/launcher/remote.rb +55 -0
- data/lib/capybara/apparition/console.rb +8 -1
- data/lib/capybara/apparition/driver.rb +6 -11
- data/lib/capybara/apparition/driver/chrome_client.rb +3 -7
- data/lib/capybara/apparition/driver/web_socket_client.rb +1 -0
- data/lib/capybara/apparition/network_traffic/error.rb +1 -0
- data/lib/capybara/apparition/node.rb +14 -6
- data/lib/capybara/apparition/node/drag.rb +8 -10
- data/lib/capybara/apparition/page.rb +6 -58
- data/lib/capybara/apparition/page/keyboard.rb +16 -4
- data/lib/capybara/apparition/page/mouse.rb +2 -1
- data/lib/capybara/apparition/version.rb +1 -1
- metadata +20 -4
- data/lib/capybara/apparition/driver/launcher.rb +0 -213
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a8ccd4ce950f7c17f8ab0ac0c5ddcbbc13a336f428011d66f4b3cb028ced150
|
4
|
+
data.tar.gz: c524d1e955e3e4e958d0bd64350190eec6f53c7f612a6bd1b41867c05ab11427
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d772071df2776e86a611a07b718eb3c411cf9048e466d66515920261f6aa8b6705ddd1559d3fa8c8728fe3510a58b37b82986d698ad17f5add7f7399a9d4fc8
|
7
|
+
data.tar.gz: bdd2bdfba3591b9a798cc820ff4dd8ce7d90af853f79c716828e3bbb2a51810c0e2f4705c16181b9284533569bd4f050fa94d95f1b62d3c5fc2880d29d817a72
|
data/README.md
CHANGED
@@ -179,8 +179,9 @@ end
|
|
179
179
|
`options` is a hash of options. The following options are supported:
|
180
180
|
|
181
181
|
* `:headless` (Boolean) - When false, run the browser visibly
|
182
|
+
* `:remote` (Boolean) - When true, connect to remote browser instead of starting locally (see [below](#Remote Chrome Driver))
|
182
183
|
* `:debug` (Boolean) - When true, debug output is logged to `STDERR`.
|
183
|
-
* `:logger` (
|
184
|
+
* `:logger` (Ruby logger object or any object responding to `puts`) - When present, debug output is written to this object
|
184
185
|
* `:browser_logger` (`IO` object) - This is where your `console.log` statements will show up. Default: `STDOUT`
|
185
186
|
* `:timeout` (Numeric) - The number of seconds we'll wait for a response
|
186
187
|
when communicating with Chrome. Default is 30.
|
@@ -198,6 +199,18 @@ end
|
|
198
199
|
* `:browser_options` (Hash) - Extra command line options to pass to Chrome when starting
|
199
200
|
* `:skip_image_loading` (Boolean) - Don't load images
|
200
201
|
|
202
|
+
### Remote Chrome Driver ###
|
203
|
+
Apparition can connect to already running instance of chrome.
|
204
|
+
Remote mode is useful when running tests in CI and chrome is available as separate docker container.
|
205
|
+
|
206
|
+
In order to use remote browser - set up apparition in the following way:
|
207
|
+
```ruby
|
208
|
+
Capybara.register_driver :apparition do |app|
|
209
|
+
browser_options = { 'remote-debugging-address' => '127.0.0.1', 'remote-debugging-port' => 9999 }
|
210
|
+
Capybara::Apparition::Driver.new(app, remote: true, browser_options: browser_options)
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
201
214
|
### URL Blacklisting & Whitelisting ###
|
202
215
|
Apparition supports URL blacklisting, which allows you
|
203
216
|
to prevent scripts from running on designated domains:
|
@@ -18,6 +18,7 @@ require 'time'
|
|
18
18
|
module Capybara::Apparition
|
19
19
|
class Browser
|
20
20
|
attr_reader :client, :paper_size, :zoom_factor, :console, :proxy_auth
|
21
|
+
|
21
22
|
extend Forwardable
|
22
23
|
|
23
24
|
delegate %i[visit current_url status_code
|
@@ -93,7 +94,7 @@ module Capybara::Apparition
|
|
93
94
|
ignore_https_errors: ignore_https_errors,
|
94
95
|
js_errors: js_errors, extensions: @extensions,
|
95
96
|
url_blacklist: @url_blacklist,
|
96
|
-
url_whitelist: @url_whitelist)
|
97
|
+
url_whitelist: @url_whitelist).send(:main_frame).loaded!
|
97
98
|
|
98
99
|
timer = Capybara::Helpers.timer(expire_in: 10)
|
99
100
|
until @pages[new_target_id].usable?
|
@@ -186,7 +187,13 @@ module Capybara::Apparition
|
|
186
187
|
private
|
187
188
|
|
188
189
|
def log(message)
|
189
|
-
@logger
|
190
|
+
return unless @logger && ENV['DEBUG']
|
191
|
+
|
192
|
+
if @logger.respond_to?(:puts)
|
193
|
+
@logger.puts(message)
|
194
|
+
else
|
195
|
+
@logger.debug(message)
|
196
|
+
end
|
190
197
|
end
|
191
198
|
|
192
199
|
def initialize_handlers
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'capybara/apparition/browser/launcher/local'
|
4
|
+
require 'capybara/apparition/browser/launcher/remote'
|
5
|
+
|
6
|
+
module Capybara::Apparition
|
7
|
+
class Browser
|
8
|
+
class Launcher
|
9
|
+
def self.start(options)
|
10
|
+
browser_options = options.fetch(:browser_options, {})
|
11
|
+
|
12
|
+
if options.fetch(:remote, false)
|
13
|
+
Remote.start(
|
14
|
+
browser_options
|
15
|
+
)
|
16
|
+
else
|
17
|
+
Local.start(
|
18
|
+
headless: options.fetch(:headless, true),
|
19
|
+
browser_options: browser_options
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module Capybara::Apparition
|
6
|
+
class Browser
|
7
|
+
class Launcher
|
8
|
+
class Local
|
9
|
+
KILL_TIMEOUT = 5
|
10
|
+
|
11
|
+
def self.start(*args, **options)
|
12
|
+
new(*args, **options).tap(&:start)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.process_killer(pid)
|
16
|
+
proc do
|
17
|
+
sleep 1
|
18
|
+
if Capybara::Apparition.windows?
|
19
|
+
::Process.kill('KILL', pid)
|
20
|
+
else
|
21
|
+
::Process.kill('USR1', pid)
|
22
|
+
timer = Capybara::Helpers.timer(expire_in: KILL_TIMEOUT)
|
23
|
+
while ::Process.wait(pid, ::Process::WNOHANG).nil?
|
24
|
+
sleep 0.05
|
25
|
+
next unless timer.expired?
|
26
|
+
|
27
|
+
::Process.kill('KILL', pid)
|
28
|
+
::Process.wait(pid)
|
29
|
+
break
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue Errno::ESRCH, Errno::ECHILD # rubocop:disable Lint/SuppressedException
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(headless:, **options)
|
37
|
+
@path = ENV['BROWSER_PATH']
|
38
|
+
@options = DEFAULT_OPTIONS.merge(options[:browser_options] || {})
|
39
|
+
if headless
|
40
|
+
@options.merge!(HEADLESS_OPTIONS)
|
41
|
+
@options['disable-gpu'] = nil if Capybara::Apparition.windows?
|
42
|
+
end
|
43
|
+
@options['user-data-dir'] = Dir.mktmpdir
|
44
|
+
end
|
45
|
+
|
46
|
+
def start
|
47
|
+
@output = Queue.new
|
48
|
+
|
49
|
+
process_options = {}
|
50
|
+
process_options[:pgroup] = true unless Capybara::Apparition.windows?
|
51
|
+
cmd = [path] + @options.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
|
52
|
+
|
53
|
+
stdin, @stdout_stderr, wait_thr = Open3.popen2e(*cmd, process_options)
|
54
|
+
stdin.close
|
55
|
+
|
56
|
+
@pid = wait_thr.pid
|
57
|
+
|
58
|
+
@out_thread = Thread.new do
|
59
|
+
while !@stdout_stderr.eof? && (data = @stdout_stderr.readpartial(512))
|
60
|
+
@output << data
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
|
65
|
+
end
|
66
|
+
|
67
|
+
def stop
|
68
|
+
return unless @pid
|
69
|
+
|
70
|
+
kill
|
71
|
+
ObjectSpace.undefine_finalizer(self)
|
72
|
+
end
|
73
|
+
|
74
|
+
def restart
|
75
|
+
stop
|
76
|
+
start
|
77
|
+
end
|
78
|
+
|
79
|
+
def host
|
80
|
+
@host ||= ws_url.host
|
81
|
+
end
|
82
|
+
|
83
|
+
def port
|
84
|
+
@port ||= ws_url.port
|
85
|
+
end
|
86
|
+
|
87
|
+
def ws_url
|
88
|
+
@ws_url ||= begin
|
89
|
+
regexp = %r{DevTools listening on (ws://.*)}
|
90
|
+
url = nil
|
91
|
+
|
92
|
+
sleep 3
|
93
|
+
loop do
|
94
|
+
break if (url = @output.pop.scan(regexp)[0])
|
95
|
+
end
|
96
|
+
@out_thread.kill
|
97
|
+
@out_thread.join # wait for thread to end before closing io
|
98
|
+
close_io
|
99
|
+
Addressable::URI.parse(url[0])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def kill
|
106
|
+
self.class.process_killer(@pid).call
|
107
|
+
@pid = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def close_io
|
111
|
+
@stdout_stderr.close unless @stdout_stderr.closed?
|
112
|
+
rescue IOError
|
113
|
+
raise unless RUBY_ENGINE == 'jruby'
|
114
|
+
end
|
115
|
+
|
116
|
+
def path
|
117
|
+
host_os = RbConfig::CONFIG['host_os']
|
118
|
+
@path ||= case RbConfig::CONFIG['host_os']
|
119
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
120
|
+
windows_path
|
121
|
+
when /darwin|mac os/
|
122
|
+
macosx_path
|
123
|
+
when /linux|solaris|bsd/
|
124
|
+
linux_path
|
125
|
+
else
|
126
|
+
raise ArgumentError, "unknown os: #{host_os.inspect}"
|
127
|
+
end
|
128
|
+
|
129
|
+
raise ArgumentError, 'Unable to find Chrome executeable' unless File.file?(@path.to_s) && File.executable?(@path.to_s)
|
130
|
+
|
131
|
+
@path
|
132
|
+
end
|
133
|
+
|
134
|
+
def windows_path
|
135
|
+
envs = %w[LOCALAPPDATA PROGRAMFILES PROGRAMFILES(X86)]
|
136
|
+
directories = %w[\\Google\\Chrome\\Application \\Chromium\\Application]
|
137
|
+
files = %w[chrome.exe]
|
138
|
+
|
139
|
+
directories.product(envs, files).lazy.map { |(dir, env, file)| "#{ENV[env]}\\#{dir}\\#{file}" }
|
140
|
+
.find { |f| File.exist?(f) } || find_first_binary(*files)
|
141
|
+
end
|
142
|
+
|
143
|
+
def macosx_path
|
144
|
+
directories = ['', File.expand_path('~')]
|
145
|
+
files = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
146
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium']
|
147
|
+
directories.product(files).map(&:join).find { |f| File.exist?(f) } ||
|
148
|
+
find_first_binary('Google Chrome', 'Chromium')
|
149
|
+
end
|
150
|
+
|
151
|
+
def linux_path
|
152
|
+
directories = %w[/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /opt/google/chrome]
|
153
|
+
files = %w[google-chrome chrome chromium chromium-browser]
|
154
|
+
|
155
|
+
directories.product(files).map { |p| p.join('/') }.find { |f| File.exist?(f) } ||
|
156
|
+
find_first_binary(*files)
|
157
|
+
end
|
158
|
+
|
159
|
+
def find_first_binary(*binaries)
|
160
|
+
paths = ENV['PATH'].split(File::PATH_SEPARATOR)
|
161
|
+
|
162
|
+
binaries.product(paths).lazy.map do |(binary, path)|
|
163
|
+
Dir.glob(File.join(path, binary)).find { |f| File.executable?(f) }
|
164
|
+
end.reject(&:nil?).first
|
165
|
+
end
|
166
|
+
|
167
|
+
# Chromium command line options
|
168
|
+
# https://peter.sh/experiments/chromium-command-line-switches/
|
169
|
+
DEFAULT_BOOLEAN_OPTIONS = %w[
|
170
|
+
disable-background-networking
|
171
|
+
disable-background-timer-throttling
|
172
|
+
disable-breakpad
|
173
|
+
disable-client-side-phishing-detection
|
174
|
+
disable-default-apps
|
175
|
+
disable-dev-shm-usage
|
176
|
+
disable-extensions
|
177
|
+
disable-features=site-per-process
|
178
|
+
disable-hang-monitor
|
179
|
+
disable-infobars
|
180
|
+
disable-popup-blocking
|
181
|
+
disable-prompt-on-repost
|
182
|
+
disable-sync
|
183
|
+
disable-translate
|
184
|
+
disable-session-crashed-bubble
|
185
|
+
metrics-recording-only
|
186
|
+
no-first-run
|
187
|
+
safebrowsing-disable-auto-update
|
188
|
+
enable-automation
|
189
|
+
password-store=basic
|
190
|
+
use-mock-keychain
|
191
|
+
keep-alive-for-test
|
192
|
+
].freeze
|
193
|
+
# Note: --no-sandbox is not needed if you properly setup a user in the container.
|
194
|
+
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
195
|
+
# no-sandbox
|
196
|
+
# disable-web-security
|
197
|
+
DEFAULT_VALUE_OPTIONS = {
|
198
|
+
'window-size' => '1024,768',
|
199
|
+
'homepage' => 'about:blank',
|
200
|
+
'remote-debugging-address' => '127.0.0.1'
|
201
|
+
}.freeze
|
202
|
+
DEFAULT_OPTIONS = DEFAULT_BOOLEAN_OPTIONS.each_with_object({}) { |opt, hsh| hsh[opt] = nil }
|
203
|
+
.merge(DEFAULT_VALUE_OPTIONS)
|
204
|
+
.freeze
|
205
|
+
HEADLESS_OPTIONS = {
|
206
|
+
'headless' => nil,
|
207
|
+
'hide-scrollbars' => nil,
|
208
|
+
'mute-audio' => nil
|
209
|
+
}.freeze
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
module Capybara::Apparition
|
7
|
+
class Browser
|
8
|
+
class Launcher
|
9
|
+
class Remote
|
10
|
+
attr_reader :ws_url
|
11
|
+
|
12
|
+
def self.start(options)
|
13
|
+
new(options).tap(&:start)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(options)
|
17
|
+
@remote_host = options.fetch('remote-debugging-address', '127.0.0.1')
|
18
|
+
@remote_port = options.fetch('remote-debugging-port', '9222')
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
@ws_url = Addressable::URI.parse(get_ws_url(@remote_host, @remote_port))
|
23
|
+
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
# Remote instance cannot be stopped
|
29
|
+
end
|
30
|
+
|
31
|
+
def restart
|
32
|
+
# Remote instance cannot be restarted
|
33
|
+
end
|
34
|
+
|
35
|
+
def host
|
36
|
+
ws_url.host
|
37
|
+
end
|
38
|
+
|
39
|
+
def port
|
40
|
+
ws_url.port
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def get_ws_url(host, port)
|
46
|
+
response = Net::HTTP.get(host, '/json/version', port)
|
47
|
+
response = JSON.parse(response)
|
48
|
+
response['webSocketDebuggerUrl']
|
49
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
50
|
+
raise ArgumentError, "Cannot connect to remote Chrome at: 'http://#{host}:#{port}/json/version'"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -8,8 +8,15 @@ module Capybara::Apparition
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def log(type, message, **options)
|
11
|
+
return unless @logger
|
12
|
+
|
11
13
|
@messages << OpenStruct.new(type: type, message: message, **options)
|
12
|
-
|
14
|
+
message_to_log = "#{type}: #{message}"
|
15
|
+
if @logger.respond_to?(:puts)
|
16
|
+
@logger.puts(message_to_log)
|
17
|
+
else
|
18
|
+
@logger.info(message_to_log)
|
19
|
+
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def clear
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require 'uri'
|
4
4
|
require 'forwardable'
|
5
5
|
require 'capybara/apparition/driver/chrome_client'
|
6
|
-
require 'capybara/apparition/driver/launcher'
|
7
6
|
require 'capybara/apparition/configuration'
|
7
|
+
require 'capybara/apparition/browser/launcher'
|
8
8
|
|
9
9
|
module Capybara::Apparition
|
10
10
|
class Driver < Capybara::Driver::Base
|
@@ -64,10 +64,7 @@ module Capybara::Apparition
|
|
64
64
|
|
65
65
|
def client
|
66
66
|
@client ||= begin
|
67
|
-
@launcher ||= Browser::Launcher.start(
|
68
|
-
headless: options.fetch(:headless, true),
|
69
|
-
browser_options: browser_options
|
70
|
-
)
|
67
|
+
@launcher ||= Browser::Launcher.start(options)
|
71
68
|
ws_url = @launcher.ws_url
|
72
69
|
::Capybara::Apparition::ChromeClient.client(ws_url.to_s)
|
73
70
|
end
|
@@ -221,9 +218,7 @@ module Capybara::Apparition
|
|
221
218
|
alias_method :header, :add_header
|
222
219
|
|
223
220
|
def response_headers
|
224
|
-
browser.response_headers.
|
225
|
-
hsh[key.split('-').map(&:capitalize).join('-')] = value
|
226
|
-
end
|
221
|
+
browser.response_headers.transform_keys { |key| key.split('-').map(&:capitalize).join('-') }
|
227
222
|
end
|
228
223
|
|
229
224
|
def set_cookie(name, value = nil, options = {})
|
@@ -295,7 +290,7 @@ module Capybara::Apparition
|
|
295
290
|
begin
|
296
291
|
input = read.read_nonblock(80) # clear out the read buffer
|
297
292
|
puts unless input&.end_with?("\n")
|
298
|
-
rescue EOFError, IO::WaitReadable
|
293
|
+
rescue EOFError, IO::WaitReadable
|
299
294
|
# Ignore problems reading from STDIN.
|
300
295
|
end
|
301
296
|
end
|
@@ -446,7 +441,7 @@ module Capybara::Apparition
|
|
446
441
|
end
|
447
442
|
end
|
448
443
|
when Hash
|
449
|
-
options.
|
444
|
+
options.transform_keys { |option| option.to_s.tr('_', '-') }
|
450
445
|
else
|
451
446
|
raise ArgumentError, 'browser_options must be an Array or a Hash'
|
452
447
|
end
|
@@ -523,7 +518,7 @@ module Capybara::Apparition
|
|
523
518
|
object_cache[arg]
|
524
519
|
when Hash
|
525
520
|
if (arg['subtype'] == 'node') && arg['objectId']
|
526
|
-
tag_name = arg['description'].split(/[
|
521
|
+
tag_name = arg['description'].split(/[.#]/, 2)[0]
|
527
522
|
Capybara::Apparition::Node.new(self, browser.current_page, arg['objectId'], tag_name: tag_name)
|
528
523
|
else
|
529
524
|
object_cache[arg] = {}
|
@@ -206,9 +206,7 @@ module Capybara::Apparition
|
|
206
206
|
event_name = event['method']
|
207
207
|
handlers[event_name].each do |handler|
|
208
208
|
puts "Calling handler for #{event_name}" if ENV['DEBUG'] == 'V'
|
209
|
-
|
210
|
-
# handler.call(event['params'].transform_keys(&method(:snake_sym)))
|
211
|
-
handler.call(**event['params'].each_with_object({}) { |(k, v), hash| hash[snake_sym(k)] = v })
|
209
|
+
handler.call(**event['params'].transform_keys(&method(:snake_sym)))
|
212
210
|
end
|
213
211
|
end
|
214
212
|
|
@@ -224,10 +222,8 @@ module Capybara::Apparition
|
|
224
222
|
@async_response_handler.abort_on_exception = true
|
225
223
|
|
226
224
|
@listener = Thread.new do
|
227
|
-
|
228
|
-
|
229
|
-
rescue EOFError # rubocop:disable Lint/SuppressedException
|
230
|
-
end
|
225
|
+
listen
|
226
|
+
rescue EOFError # rubocop:disable Lint/SuppressedException
|
231
227
|
end
|
232
228
|
# @listener.abort_on_exception = true
|
233
229
|
end
|
@@ -30,7 +30,7 @@ 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
|
-
tag_name = r_o['description'].split(/[
|
33
|
+
tag_name = r_o['description'].split(/[.#]/, 2)[0]
|
34
34
|
Capybara::Apparition::Node.new(driver, @page, r_o['objectId'], tag_name: tag_name)
|
35
35
|
end
|
36
36
|
rescue ::Capybara::Apparition::BrowserError => e
|
@@ -197,7 +197,7 @@ module Capybara::Apparition
|
|
197
197
|
evaluate_on ELEMENT_DISABLED_JS
|
198
198
|
end
|
199
199
|
|
200
|
-
def click(keys = [], button: 'left', count: 1, **options)
|
200
|
+
def click(keys = [], button: 'left', count: 1, delay: 0, **options)
|
201
201
|
pos = element_click_pos(**options)
|
202
202
|
raise ::Capybara::Apparition::MouseEventImpossible.new(self, 'args' => ['click']) if pos.nil?
|
203
203
|
|
@@ -208,7 +208,7 @@ module Capybara::Apparition
|
|
208
208
|
raise ::Capybara::Apparition::MouseEventFailed.new(self, 'args' => ['click', test.selector, pos]) unless test.success
|
209
209
|
end
|
210
210
|
|
211
|
-
@page.mouse.click_at(**pos.merge(button: button, count: count, modifiers: keys))
|
211
|
+
@page.mouse.click_at(**pos.merge(button: button, count: count, modifiers: keys, delay: delay))
|
212
212
|
if ENV['DEBUG']
|
213
213
|
begin
|
214
214
|
new_pos = element_click_pos(**options)
|
@@ -490,13 +490,21 @@ module Capybara::Apparition
|
|
490
490
|
DevToolsProtocol::RemoteObject.new(@page, response['result'] || response['object']).value
|
491
491
|
end
|
492
492
|
|
493
|
-
def set_text(value, clear: nil, delay: 0, **_unused)
|
493
|
+
def set_text(value, clear: nil, delay: 0, rapid: nil, **_unused)
|
494
494
|
value = value.to_s
|
495
495
|
if value.empty? && clear.nil?
|
496
496
|
evaluate_on CLEAR_ELEMENT_JS
|
497
497
|
else
|
498
498
|
focus
|
499
|
-
|
499
|
+
if (rapid && (value.length >= 6)) || ((value.length > 30) && rapid != false)
|
500
|
+
_send_keys(*keys_to_send(value[0..2], clear), delay: delay)
|
501
|
+
driver.execute_script <<~JS, self, value[0...-3]
|
502
|
+
arguments[0].value = arguments[1]
|
503
|
+
JS
|
504
|
+
_send_keys(*keys_to_send(value[-3..-1], :none), delay: delay)
|
505
|
+
else
|
506
|
+
_send_keys(*keys_to_send(value, clear), delay: delay)
|
507
|
+
end
|
500
508
|
end
|
501
509
|
end
|
502
510
|
|
@@ -563,7 +571,7 @@ module Capybara::Apparition
|
|
563
571
|
r_o = @page.element_from_point(x: x, y: y)
|
564
572
|
return nil unless r_o && r_o['objectId']
|
565
573
|
|
566
|
-
tag_name = r_o['description'].split(/[
|
574
|
+
tag_name = r_o['description'].split(/[.#]/, 2)[0]
|
567
575
|
hit_node = Capybara::Apparition::Node.new(driver, @page, r_o['objectId'], tag_name: tag_name)
|
568
576
|
result = begin
|
569
577
|
evaluate_on(<<~JS, objectId: hit_node.id)
|
@@ -17,15 +17,13 @@ module Capybara::Apparition
|
|
17
17
|
m.up(**other.visible_center)
|
18
18
|
else
|
19
19
|
@page.keyboard.with_keys(drop_modifiers) do
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
sleep delay
|
28
|
-
end
|
20
|
+
other.scroll_if_needed
|
21
|
+
sleep delay
|
22
|
+
m.move_to(**other.visible_center)
|
23
|
+
sleep delay
|
24
|
+
ensure
|
25
|
+
m.up
|
26
|
+
sleep delay
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
@@ -47,7 +45,7 @@ module Capybara::Apparition
|
|
47
45
|
def drop(*args)
|
48
46
|
if args[0].is_a? String
|
49
47
|
input = evaluate_on ATTACH_FILE
|
50
|
-
tag_name = input['description'].split(/[
|
48
|
+
tag_name = input['description'].split(/[.#]/, 2)[0]
|
51
49
|
input = Capybara::Apparition::Node.new(driver, @page, input['objectId'], tag_name: tag_name)
|
52
50
|
input.set(args)
|
53
51
|
evaluate_on DROP_FILE, objectId: input.id
|
@@ -197,7 +197,7 @@ module Capybara::Apparition
|
|
197
197
|
js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
|
198
198
|
query = method == :css ? CSS_FIND_JS : XPATH_FIND_JS
|
199
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(/[
|
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
|
|
@@ -300,7 +300,7 @@ module Capybara::Apparition
|
|
300
300
|
|
301
301
|
def element_from_point(x:, y:)
|
302
302
|
r_o = _raw_evaluate("document.elementFromPoint(#{x}, #{y})", context_id: main_frame.context_id)
|
303
|
-
while r_o
|
303
|
+
while r_o&.[]('description')&.start_with?('iframe')
|
304
304
|
frame_node = command('DOM.describeNode', objectId: r_o['objectId'])
|
305
305
|
frame = @frames.get(frame_node.dig('node', 'frameId'))
|
306
306
|
fo = frame_offset(frame)
|
@@ -366,7 +366,7 @@ module Capybara::Apparition
|
|
366
366
|
end
|
367
367
|
|
368
368
|
def async_command(name, **params)
|
369
|
-
@browser.command_for_session(@session.session_id, name,
|
369
|
+
@browser.command_for_session(@session.session_id, name, params).discard_result
|
370
370
|
end
|
371
371
|
|
372
372
|
def extra_headers
|
@@ -374,11 +374,9 @@ module Capybara::Apparition
|
|
374
374
|
end
|
375
375
|
|
376
376
|
def update_headers(async: false)
|
377
|
-
method = async ? :async_command : :command
|
378
377
|
if (ua = extra_headers.find { |k, _v| /^User-Agent$/i.match? k })
|
379
|
-
send(
|
378
|
+
send(async ? :async_command : :command, 'Network.setUserAgentOverride', userAgent: ua[1])
|
380
379
|
end
|
381
|
-
send(method, 'Network.setExtraHTTPHeaders', headers: extra_headers)
|
382
380
|
setup_network_interception
|
383
381
|
end
|
384
382
|
|
@@ -509,8 +507,6 @@ module Capybara::Apparition
|
|
509
507
|
end
|
510
508
|
|
511
509
|
@session.on 'Runtime.executionContextCreated' do |context:|
|
512
|
-
puts "**** executionContextCreated: #{params}" if ENV['DEBUG']
|
513
|
-
# context = params['context']
|
514
510
|
frame_id = context.dig('auxData', 'frameId')
|
515
511
|
if context.dig('auxData', 'isDefault') && frame_id
|
516
512
|
if (frame = @frames.get(frame_id))
|
@@ -560,20 +556,6 @@ module Capybara::Apparition
|
|
560
556
|
end
|
561
557
|
end
|
562
558
|
|
563
|
-
@session.on(
|
564
|
-
'Network.requestIntercepted'
|
565
|
-
) do |request:, interception_id:, auth_challenge: nil, is_navigation_request: nil, **|
|
566
|
-
if auth_challenge
|
567
|
-
if auth_challenge['source'] == 'Proxy'
|
568
|
-
handle_proxy_auth(interception_id)
|
569
|
-
else
|
570
|
-
handle_user_auth(interception_id)
|
571
|
-
end
|
572
|
-
else
|
573
|
-
process_intercepted_request(interception_id, request, is_navigation_request)
|
574
|
-
end
|
575
|
-
end
|
576
|
-
|
577
559
|
@session.on 'Fetch.requestPaused' do |request:, request_id:, resource_type:, **|
|
578
560
|
process_intercepted_fetch(request_id, request, resource_type)
|
579
561
|
end
|
@@ -645,39 +627,13 @@ module Capybara::Apparition
|
|
645
627
|
|
646
628
|
def setup_network_interception
|
647
629
|
async_command 'Network.setCacheDisabled', cacheDisabled: true
|
648
|
-
|
649
|
-
async_command 'Network.setRequestInterception', patterns: [{ urlPattern: '*' }]
|
650
|
-
end
|
651
|
-
|
652
|
-
def process_intercepted_request(interception_id, request, navigation)
|
653
|
-
headers, url = request.values_at('headers', 'url')
|
654
|
-
|
655
|
-
unless @temp_headers.empty? || navigation # rubocop:disable Style/IfUnlessModifier
|
656
|
-
headers.delete_if { |name, value| @temp_headers[name] == value }
|
657
|
-
end
|
658
|
-
unless @temp_no_redirect_headers.empty? || !navigation
|
659
|
-
headers.delete_if { |name, value| @temp_no_redirect_headers[name] == value }
|
660
|
-
end
|
661
|
-
if (accept = perm_headers.keys.find { |k| /accept/i.match? k })
|
662
|
-
headers[accept] = perm_headers[accept]
|
663
|
-
end
|
664
|
-
|
665
|
-
if @url_blacklist.any? { |r| url.match Regexp.escape(r).gsub('\*', '.*?') }
|
666
|
-
block_request(interception_id, 'Failed')
|
667
|
-
elsif @url_whitelist.any?
|
668
|
-
if @url_whitelist.any? { |r| url.match Regexp.escape(r).gsub('\*', '.*?') }
|
669
|
-
continue_request(interception_id, headers: headers)
|
670
|
-
else
|
671
|
-
block_request(interception_id, 'Failed')
|
672
|
-
end
|
673
|
-
else
|
674
|
-
continue_request(interception_id, headers: headers)
|
675
|
-
end
|
630
|
+
async_command 'Fetch.enable', handleAuthRequests: true
|
676
631
|
end
|
677
632
|
|
678
633
|
def process_intercepted_fetch(interception_id, request, resource_type)
|
679
634
|
navigation = (resource_type == 'Document')
|
680
635
|
headers, url = request.values_at('headers', 'url')
|
636
|
+
headers = headers.merge(extra_headers)
|
681
637
|
|
682
638
|
unless @temp_headers.empty? || navigation # rubocop:disable Style/IfUnlessModifier
|
683
639
|
headers.delete_if { |name, value| @temp_headers[name] == value }
|
@@ -706,14 +662,6 @@ module Capybara::Apparition
|
|
706
662
|
end
|
707
663
|
end
|
708
664
|
|
709
|
-
def continue_request(id, **params)
|
710
|
-
async_command 'Network.continueInterceptedRequest', interceptionId: id, **params
|
711
|
-
end
|
712
|
-
|
713
|
-
def block_request(id, reason)
|
714
|
-
async_command 'Network.continueInterceptedRequest', errorReason: reason, interceptionId: id
|
715
|
-
end
|
716
|
-
|
717
665
|
def go_history(delta)
|
718
666
|
history = command('Page.getNavigationHistory')
|
719
667
|
entry = history['entries'][history['currentIndex'] + delta]
|
@@ -66,6 +66,10 @@ module Capybara::Apparition
|
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
|
+
def insert_emoji(str)
|
70
|
+
@page.command('Input.insertText', text: str)
|
71
|
+
end
|
72
|
+
|
69
73
|
def type_with_modifiers(keys, delay:)
|
70
74
|
old_pressed_keys, @pressed_keys = @pressed_keys, {}
|
71
75
|
|
@@ -74,9 +78,17 @@ module Capybara::Apparition
|
|
74
78
|
when Array
|
75
79
|
type_with_modifiers(sequence, delay: delay)
|
76
80
|
when String
|
77
|
-
sequence.
|
78
|
-
|
79
|
-
|
81
|
+
clusters = sequence.grapheme_clusters.chunk { |gc| gc.match?(/\p{Emoji Presentation}/) }
|
82
|
+
clusters.each do |emoji, chars|
|
83
|
+
if emoji
|
84
|
+
insert_emoji(chars.join)
|
85
|
+
sleep delay
|
86
|
+
else
|
87
|
+
chars.each do |char|
|
88
|
+
press char
|
89
|
+
sleep delay
|
90
|
+
end
|
91
|
+
end
|
80
92
|
end
|
81
93
|
else
|
82
94
|
press sequence
|
@@ -91,7 +103,7 @@ module Capybara::Apparition
|
|
91
103
|
end
|
92
104
|
|
93
105
|
def release_pressed_keys
|
94
|
-
@pressed_keys.
|
106
|
+
@pressed_keys.each_value { |desc| up(desc) }
|
95
107
|
end
|
96
108
|
|
97
109
|
def key_description(key)
|
@@ -9,12 +9,13 @@ module Capybara::Apparition
|
|
9
9
|
@current_buttons = BUTTONS[:none]
|
10
10
|
end
|
11
11
|
|
12
|
-
def click_at(x:, y:, button: 'left', count: 1, modifiers: [])
|
12
|
+
def click_at(x:, y:, button: 'left', count: 1, delay: 0, modifiers: [])
|
13
13
|
move_to x: x, y: y
|
14
14
|
count.times do |num|
|
15
15
|
@keyboard.with_keys(modifiers) do
|
16
16
|
mouse_params = { x: x, y: y, button: button, count: num + 1 }
|
17
17
|
down(**mouse_params)
|
18
|
+
sleep(delay || 0)
|
18
19
|
up(**mouse_params)
|
19
20
|
end
|
20
21
|
end
|
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.6.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: 2020-
|
11
|
+
date: 2020-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -44,6 +44,20 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 0.6.5
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: byebug
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
62
|
name: chunky_png
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -242,6 +256,9 @@ files:
|
|
242
256
|
- lib/capybara/apparition/browser/cookie.rb
|
243
257
|
- lib/capybara/apparition/browser/frame.rb
|
244
258
|
- lib/capybara/apparition/browser/header.rb
|
259
|
+
- lib/capybara/apparition/browser/launcher.rb
|
260
|
+
- lib/capybara/apparition/browser/launcher/local.rb
|
261
|
+
- lib/capybara/apparition/browser/launcher/remote.rb
|
245
262
|
- lib/capybara/apparition/browser/modal.rb
|
246
263
|
- lib/capybara/apparition/browser/page_manager.rb
|
247
264
|
- lib/capybara/apparition/browser/render.rb
|
@@ -254,7 +271,6 @@ files:
|
|
254
271
|
- lib/capybara/apparition/dev_tools_protocol/session.rb
|
255
272
|
- lib/capybara/apparition/driver.rb
|
256
273
|
- lib/capybara/apparition/driver/chrome_client.rb
|
257
|
-
- lib/capybara/apparition/driver/launcher.rb
|
258
274
|
- lib/capybara/apparition/driver/response.rb
|
259
275
|
- lib/capybara/apparition/driver/web_socket_client.rb
|
260
276
|
- lib/capybara/apparition/errors.rb
|
@@ -284,7 +300,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
284
300
|
requirements:
|
285
301
|
- - ">="
|
286
302
|
- !ruby/object:Gem::Version
|
287
|
-
version: 2.
|
303
|
+
version: 2.5.0
|
288
304
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
289
305
|
requirements:
|
290
306
|
- - ">="
|
@@ -1,213 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'open3'
|
4
|
-
|
5
|
-
module Capybara::Apparition
|
6
|
-
class Browser
|
7
|
-
class Launcher
|
8
|
-
KILL_TIMEOUT = 5
|
9
|
-
|
10
|
-
def self.start(*args, **options)
|
11
|
-
new(*args, **options).tap(&:start)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.process_killer(pid)
|
15
|
-
proc do
|
16
|
-
begin
|
17
|
-
sleep 1
|
18
|
-
if Capybara::Apparition.windows?
|
19
|
-
::Process.kill('KILL', pid)
|
20
|
-
else
|
21
|
-
::Process.kill('USR1', pid)
|
22
|
-
timer = Capybara::Helpers.timer(expire_in: KILL_TIMEOUT)
|
23
|
-
while ::Process.wait(pid, ::Process::WNOHANG).nil?
|
24
|
-
sleep 0.05
|
25
|
-
next unless timer.expired?
|
26
|
-
|
27
|
-
::Process.kill('KILL', pid)
|
28
|
-
::Process.wait(pid)
|
29
|
-
break
|
30
|
-
end
|
31
|
-
end
|
32
|
-
rescue Errno::ESRCH, Errno::ECHILD # rubocop:disable Lint/SuppressedException
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize(headless:, **options)
|
38
|
-
@path = ENV['BROWSER_PATH']
|
39
|
-
@options = DEFAULT_OPTIONS.merge(options[:browser_options] || {})
|
40
|
-
if headless
|
41
|
-
@options.merge!(HEADLESS_OPTIONS)
|
42
|
-
@options['disable-gpu'] = nil if Capybara::Apparition.windows?
|
43
|
-
end
|
44
|
-
@options['user-data-dir'] = Dir.mktmpdir
|
45
|
-
end
|
46
|
-
|
47
|
-
def start
|
48
|
-
@output = Queue.new
|
49
|
-
|
50
|
-
process_options = {}
|
51
|
-
process_options[:pgroup] = true unless Capybara::Apparition.windows?
|
52
|
-
cmd = [path] + @options.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
|
53
|
-
|
54
|
-
stdin, @stdout_stderr, wait_thr = Open3.popen2e(*cmd, process_options)
|
55
|
-
stdin.close
|
56
|
-
|
57
|
-
@pid = wait_thr.pid
|
58
|
-
|
59
|
-
@out_thread = Thread.new do
|
60
|
-
while !@stdout_stderr.eof? && (data = @stdout_stderr.readpartial(512))
|
61
|
-
@output << data
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
|
66
|
-
end
|
67
|
-
|
68
|
-
def stop
|
69
|
-
return unless @pid
|
70
|
-
|
71
|
-
kill
|
72
|
-
ObjectSpace.undefine_finalizer(self)
|
73
|
-
end
|
74
|
-
|
75
|
-
def restart
|
76
|
-
stop
|
77
|
-
start
|
78
|
-
end
|
79
|
-
|
80
|
-
def host
|
81
|
-
@host ||= ws_url.host
|
82
|
-
end
|
83
|
-
|
84
|
-
def port
|
85
|
-
@port ||= ws_url.port
|
86
|
-
end
|
87
|
-
|
88
|
-
def ws_url
|
89
|
-
@ws_url ||= begin
|
90
|
-
regexp = %r{DevTools listening on (ws://.*)}
|
91
|
-
url = nil
|
92
|
-
|
93
|
-
sleep 3
|
94
|
-
loop do
|
95
|
-
break if (url = @output.pop.scan(regexp)[0])
|
96
|
-
end
|
97
|
-
@out_thread.kill
|
98
|
-
@out_thread.join # wait for thread to end before closing io
|
99
|
-
close_io
|
100
|
-
Addressable::URI.parse(url[0])
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
|
106
|
-
def kill
|
107
|
-
self.class.process_killer(@pid).call
|
108
|
-
@pid = nil
|
109
|
-
end
|
110
|
-
|
111
|
-
def close_io
|
112
|
-
@stdout_stderr.close unless @stdout_stderr.closed?
|
113
|
-
rescue IOError
|
114
|
-
raise unless RUBY_ENGINE == 'jruby'
|
115
|
-
end
|
116
|
-
|
117
|
-
def path
|
118
|
-
host_os = RbConfig::CONFIG['host_os']
|
119
|
-
@path ||= case RbConfig::CONFIG['host_os']
|
120
|
-
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
121
|
-
windows_path
|
122
|
-
when /darwin|mac os/
|
123
|
-
macosx_path
|
124
|
-
when /linux|solaris|bsd/
|
125
|
-
linux_path
|
126
|
-
else
|
127
|
-
raise ArgumentError, "unknown os: #{host_os.inspect}"
|
128
|
-
end
|
129
|
-
|
130
|
-
raise ArgumentError, 'Unable to find Chrome executeable' unless File.file?(@path.to_s) && File.executable?(@path.to_s)
|
131
|
-
|
132
|
-
@path
|
133
|
-
end
|
134
|
-
|
135
|
-
def windows_path
|
136
|
-
envs = %w[LOCALAPPDATA PROGRAMFILES PROGRAMFILES(X86)]
|
137
|
-
directories = %w[\\Google\\Chrome\\Application \\Chromium\\Application]
|
138
|
-
files = %w[chrome.exe]
|
139
|
-
|
140
|
-
directories.product(envs, files).lazy.map { |(dir, env, file)| "#{ENV[env]}\\#{dir}\\#{file}" }
|
141
|
-
.find { |f| File.exist?(f) } || find_first_binary(*files)
|
142
|
-
end
|
143
|
-
|
144
|
-
def macosx_path
|
145
|
-
directories = ['', File.expand_path('~')]
|
146
|
-
files = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
147
|
-
'/Applications/Chromium.app/Contents/MacOS/Chromium']
|
148
|
-
directories.product(files).map(&:join).find { |f| File.exist?(f) } ||
|
149
|
-
find_first_binary('Google Chrome', 'Chromium')
|
150
|
-
end
|
151
|
-
|
152
|
-
def linux_path
|
153
|
-
directories = %w[/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /opt/google/chrome]
|
154
|
-
files = %w[google-chrome chrome chromium chromium-browser]
|
155
|
-
|
156
|
-
directories.product(files).map { |p| p.join('/') }.find { |f| File.exist?(f) } ||
|
157
|
-
find_first_binary(*files)
|
158
|
-
end
|
159
|
-
|
160
|
-
def find_first_binary(*binaries)
|
161
|
-
paths = ENV['PATH'].split(File::PATH_SEPARATOR)
|
162
|
-
|
163
|
-
binaries.product(paths).lazy.map do |(binary, path)|
|
164
|
-
Dir.glob(File.join(path, binary)).find { |f| File.executable?(f) }
|
165
|
-
end.reject(&:nil?).first
|
166
|
-
end
|
167
|
-
|
168
|
-
# Chromium command line options
|
169
|
-
# https://peter.sh/experiments/chromium-command-line-switches/
|
170
|
-
DEFAULT_BOOLEAN_OPTIONS = %w[
|
171
|
-
disable-background-networking
|
172
|
-
disable-background-timer-throttling
|
173
|
-
disable-breakpad
|
174
|
-
disable-client-side-phishing-detection
|
175
|
-
disable-default-apps
|
176
|
-
disable-dev-shm-usage
|
177
|
-
disable-extensions
|
178
|
-
disable-features=site-per-process
|
179
|
-
disable-hang-monitor
|
180
|
-
disable-infobars
|
181
|
-
disable-popup-blocking
|
182
|
-
disable-prompt-on-repost
|
183
|
-
disable-sync
|
184
|
-
disable-translate
|
185
|
-
disable-session-crashed-bubble
|
186
|
-
metrics-recording-only
|
187
|
-
no-first-run
|
188
|
-
safebrowsing-disable-auto-update
|
189
|
-
enable-automation
|
190
|
-
password-store=basic
|
191
|
-
use-mock-keychain
|
192
|
-
keep-alive-for-test
|
193
|
-
].freeze
|
194
|
-
# Note: --no-sandbox is not needed if you properly setup a user in the container.
|
195
|
-
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
196
|
-
# no-sandbox
|
197
|
-
# disable-web-security
|
198
|
-
DEFAULT_VALUE_OPTIONS = {
|
199
|
-
'window-size' => '1024,768',
|
200
|
-
'homepage' => 'about:blank',
|
201
|
-
'remote-debugging-address' => '127.0.0.1'
|
202
|
-
}.freeze
|
203
|
-
DEFAULT_OPTIONS = DEFAULT_BOOLEAN_OPTIONS.each_with_object({}) { |opt, hsh| hsh[opt] = nil }
|
204
|
-
.merge(DEFAULT_VALUE_OPTIONS)
|
205
|
-
.freeze
|
206
|
-
HEADLESS_OPTIONS = {
|
207
|
-
'headless' => nil,
|
208
|
-
'hide-scrollbars' => nil,
|
209
|
-
'mute-audio' => nil
|
210
|
-
}.freeze
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|