apparition 0.5.0 → 0.6.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 +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
|