cuprite 0.4.0 → 0.5.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 +8 -2
- data/lib/capybara/cuprite.rb +4 -0
- data/lib/capybara/cuprite/browser.rb +51 -28
- data/lib/capybara/cuprite/browser/client.rb +3 -2
- data/lib/capybara/cuprite/browser/frame.rb +2 -0
- data/lib/capybara/cuprite/browser/javascripts/index.js +15 -5
- data/lib/capybara/cuprite/browser/net.rb +90 -0
- data/lib/capybara/cuprite/browser/page.rb +19 -10
- data/lib/capybara/cuprite/browser/process.rb +41 -15
- data/lib/capybara/cuprite/browser/runtime.rb +9 -4
- data/lib/capybara/cuprite/driver.rb +8 -14
- data/lib/capybara/cuprite/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f883874c9b6d85c217809bb7423c5dc7b31db4932b9bf8ad8e8c469eb0820973
|
4
|
+
data.tar.gz: c29bbb53cd0a8153988e4d26dc709a3aa49ad94114a7c175d636705f94eb263c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0f9a932c1e3640eac45e9b1f3af0964f0f8e9c24037d961f6d10825bcbc97f323b324ae58595de0c84c73d900add6ef96357c6555796938fc3e8d2c261217b1
|
7
|
+
data.tar.gz: 4de0397ab55e6863d64a2faa20a63bc6b97b585308b6f8d5694c75dbc10b90b18b1b202a7501b51a409bd59eaa2fa415d637816060550927751d42047bf4a530
|
data/README.md
CHANGED
@@ -67,6 +67,10 @@ All the mandatory capybara features plus optional ones:
|
|
67
67
|
* `page.response_headers`
|
68
68
|
* `page.save_screenshot`
|
69
69
|
* `page.driver.render_base64(format, options)`
|
70
|
+
* `page.driver.scroll_to(left, top)`
|
71
|
+
* `page.driver.basic_authorize(user, password)`
|
72
|
+
* `element.send_keys(*keys)`
|
73
|
+
* `page.driver.set_proxy(ip, port, type, user, password)`
|
70
74
|
* window API
|
71
75
|
* cookie handling
|
72
76
|
|
@@ -134,6 +138,8 @@ end
|
|
134
138
|
* `:browser_path` (String) - Path to chrome binary, you can also set ENV
|
135
139
|
variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
|
136
140
|
* `:headless` (Boolean) - Set browser as headless or not, `true` by default.
|
141
|
+
* `:slowmo` (Integer | Float) - Set a delay to wait before sending command.
|
142
|
+
Usefull companion of headless option, so that you have time to see changes.
|
137
143
|
* `:logger` (Object responding to `puts`) - When present, debug output is
|
138
144
|
written to this object.
|
139
145
|
* `:timeout` (Numeric) - The number of seconds we'll wait for a response when
|
@@ -156,14 +162,14 @@ Cuprite supports URL blacklisting, which allows you to prevent scripts from
|
|
156
162
|
running on designated domains:
|
157
163
|
|
158
164
|
```ruby
|
159
|
-
page.driver.browser.url_blacklist = [
|
165
|
+
page.driver.browser.url_blacklist = ["http://www.example.com"]
|
160
166
|
```
|
161
167
|
|
162
168
|
and also URL whitelisting, which allows scripts to only run
|
163
169
|
on designated domains:
|
164
170
|
|
165
171
|
```ruby
|
166
|
-
page.driver.browser.url_whitelist = [
|
172
|
+
page.driver.browser.url_whitelist = ["http://www.example.com"]
|
167
173
|
```
|
168
174
|
|
169
175
|
If you are experiencing slower run times, consider creating a URL whitelist of
|
data/lib/capybara/cuprite.rb
CHANGED
@@ -10,13 +10,14 @@ require "capybara/cuprite/browser/page"
|
|
10
10
|
module Capybara::Cuprite
|
11
11
|
class Browser
|
12
12
|
TIMEOUT = 5
|
13
|
+
WINDOW_SIZE = [1024, 768].freeze
|
13
14
|
EXTENSIONS = [
|
14
15
|
File.expand_path("browser/javascripts/index.js", __dir__)
|
15
16
|
].freeze
|
16
17
|
|
17
18
|
extend Forwardable
|
18
19
|
|
19
|
-
attr_reader :headers
|
20
|
+
attr_reader :headers, :window_size
|
20
21
|
|
21
22
|
def self.start(*args)
|
22
23
|
new(*args)
|
@@ -26,27 +27,41 @@ module Capybara::Cuprite
|
|
26
27
|
delegate %i(window_handle window_handles switch_to_window open_new_window
|
27
28
|
close_window within_window page) => :targets
|
28
29
|
delegate %i(visit status_code body all_text property attributes attribute
|
29
|
-
value visible? disabled?
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
accept_prompt dismiss_prompt reset_modals
|
36
|
-
|
37
|
-
|
30
|
+
value visible? disabled? network_traffic clear_network_traffic
|
31
|
+
path response_headers refresh click right_click double_click
|
32
|
+
hover set click_coordinates drag drag_by select trigger
|
33
|
+
scroll_to send_keys evaluate evaluate_on evaluate_async execute
|
34
|
+
frame_url frame_title switch_to_frame current_url title go_back
|
35
|
+
go_forward find_modal accept_confirm dismiss_confirm
|
36
|
+
accept_prompt dismiss_prompt reset_modals authorize
|
37
|
+
proxy_authorize) => :page
|
38
|
+
|
39
|
+
attr_reader :process, :logger, :js_errors, :slowmo,
|
40
|
+
:url_blacklist, :url_whitelist
|
38
41
|
attr_writer :timeout
|
39
42
|
|
40
43
|
def initialize(options = nil)
|
41
|
-
|
44
|
+
# Doesn't work on MacOS, so we need to set it by CDP as well
|
45
|
+
options ||= {}
|
46
|
+
@window_size = options.fetch(:window_size, WINDOW_SIZE)
|
47
|
+
@original_window_size = @window_size
|
48
|
+
|
49
|
+
@options = Hash(options.merge(window_size: @window_size))
|
42
50
|
@logger, @timeout = @options.values_at(:logger, :timeout)
|
43
51
|
@js_errors = @options.fetch(:js_errors, false)
|
52
|
+
@slowmo = @options[:slowmo]
|
53
|
+
|
54
|
+
self.url_blacklist = @options[:url_blacklist]
|
55
|
+
self.url_whitelist = @options[:url_whitelist]
|
44
56
|
|
45
|
-
if ENV["CUPRITE_DEBUG"]
|
57
|
+
if ENV["CUPRITE_DEBUG"] && !@logger
|
46
58
|
STDOUT.sync = true
|
47
59
|
@logger = STDOUT
|
60
|
+
@options[:logger] = @logger
|
48
61
|
end
|
49
62
|
|
63
|
+
@options.freeze
|
64
|
+
|
50
65
|
start
|
51
66
|
end
|
52
67
|
|
@@ -169,23 +184,14 @@ module Capybara::Cuprite
|
|
169
184
|
page.command("Network.clearBrowserCookies")
|
170
185
|
end
|
171
186
|
|
172
|
-
def
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
def page_settings=(settings)
|
177
|
-
raise NotImplementedError
|
178
|
-
end
|
179
|
-
|
180
|
-
def url_whitelist=(whitelist)
|
181
|
-
@url_whitelist = Array(whitelist).map { |p| { urlPattern: p } }
|
182
|
-
page.command("Network.setRequestInterception", patterns: @url_whitelist)
|
187
|
+
def url_whitelist=(wildcards)
|
188
|
+
@url_whitelist = prepare_wildcards(wildcards)
|
189
|
+
page.intercept_request("*") if @client && !@url_whitelist.empty?
|
183
190
|
end
|
184
191
|
|
185
|
-
def url_blacklist=(
|
186
|
-
|
187
|
-
|
188
|
-
page.command("Network.setRequestInterception", patterns: @url_blacklist)
|
192
|
+
def url_blacklist=(wildcards)
|
193
|
+
@url_blacklist = prepare_wildcards(wildcards)
|
194
|
+
page.intercept_request("*") if @client && !@url_blacklist.empty?
|
189
195
|
end
|
190
196
|
|
191
197
|
def clear_memory_cache
|
@@ -195,6 +201,7 @@ module Capybara::Cuprite
|
|
195
201
|
def reset
|
196
202
|
@headers = {}
|
197
203
|
@zoom_factor = nil
|
204
|
+
@window_size = @original_window_size
|
198
205
|
targets.reset
|
199
206
|
end
|
200
207
|
|
@@ -217,6 +224,11 @@ module Capybara::Cuprite
|
|
217
224
|
page.evaluate("_cuprite.browserError()")
|
218
225
|
end
|
219
226
|
|
227
|
+
def resize(**options)
|
228
|
+
@window_size = [options[:width], options[:height]]
|
229
|
+
page.resize(**options)
|
230
|
+
end
|
231
|
+
|
220
232
|
def command(*args)
|
221
233
|
id = @client.command(*args)
|
222
234
|
@client.wait(id: id)
|
@@ -234,7 +246,7 @@ module Capybara::Cuprite
|
|
234
246
|
def start
|
235
247
|
@headers = {}
|
236
248
|
@process = Process.start(@options)
|
237
|
-
@client = Client.new(self, @process.ws_url)
|
249
|
+
@client = Client.new(self, @process.ws_url, false)
|
238
250
|
end
|
239
251
|
|
240
252
|
def render_options(format, opts)
|
@@ -290,5 +302,16 @@ module Capybara::Cuprite
|
|
290
302
|
raise
|
291
303
|
end
|
292
304
|
end
|
305
|
+
|
306
|
+
def prepare_wildcards(wc)
|
307
|
+
Array(wc).map do |wildcard|
|
308
|
+
if wildcard.is_a?(Regexp)
|
309
|
+
wildcard
|
310
|
+
else
|
311
|
+
wildcard = wildcard.gsub("*", ".*")
|
312
|
+
Regexp.new(wildcard, Regexp::IGNORECASE)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
293
316
|
end
|
294
317
|
end
|
@@ -8,10 +8,10 @@ module Capybara::Cuprite
|
|
8
8
|
class Client
|
9
9
|
class IdError < RuntimeError; end
|
10
10
|
|
11
|
-
def initialize(browser, ws_url)
|
11
|
+
def initialize(browser, ws_url, allow_slowmo = true)
|
12
12
|
@command_id = 0
|
13
13
|
@subscribed = Hash.new { |h, k| h[k] = [] }
|
14
|
-
@browser = browser
|
14
|
+
@browser, @allow_slowmo = browser, allow_slowmo
|
15
15
|
@commands = Queue.new
|
16
16
|
@ws = WebSocket.new(ws_url, @browser.logger)
|
17
17
|
|
@@ -31,6 +31,7 @@ module Capybara::Cuprite
|
|
31
31
|
|
32
32
|
def command(method, params = {})
|
33
33
|
message = build_message(method, params)
|
34
|
+
sleep(@browser.slowmo) if !@browser.slowmo.nil? && @allow_slowmo
|
34
35
|
@ws.send_message(message)
|
35
36
|
message[:id]
|
36
37
|
end
|
@@ -124,12 +124,12 @@ class Cuprite {
|
|
124
124
|
}
|
125
125
|
|
126
126
|
this.trigger(node, "focus");
|
127
|
-
node
|
127
|
+
this.setValue(node, "");
|
128
128
|
|
129
129
|
if (node.type == "number" || node.type == "date") {
|
130
|
-
node
|
130
|
+
this.setValue(node, value);
|
131
131
|
} else if (node.type == "time") {
|
132
|
-
node
|
132
|
+
this.setValue(node, new Date(value).toTimeString().split(" ")[0]);
|
133
133
|
} else if (node.type == "datetime-local") {
|
134
134
|
value = new Date(value);
|
135
135
|
let year = value.getFullYear();
|
@@ -138,13 +138,13 @@ class Cuprite {
|
|
138
138
|
let hour = ("0" + value.getHours()).slice(-2);
|
139
139
|
let min = ("0" + value.getMinutes()).slice(-2);
|
140
140
|
let sec = ("0" + value.getSeconds()).slice(-2);
|
141
|
-
node
|
141
|
+
this.setValue(node, `${year}-${month}-${date}T${hour}:${min}:${sec}`);
|
142
142
|
} else {
|
143
143
|
for (let i = 0; i < value.length; i++) {
|
144
144
|
let char = value[i];
|
145
145
|
let keyCode = this.characterToKeyCode(char);
|
146
146
|
this.keyupdowned(node, "keydown", keyCode);
|
147
|
-
node.value
|
147
|
+
this.setValue(node, node.value + char);
|
148
148
|
|
149
149
|
this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0));
|
150
150
|
this.keyupdowned(node, "keyup", keyCode);
|
@@ -156,6 +156,16 @@ class Cuprite {
|
|
156
156
|
this.trigger(node, "blur");
|
157
157
|
}
|
158
158
|
|
159
|
+
setValue(node, value) {
|
160
|
+
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
|
161
|
+
let nativeTextareaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
|
162
|
+
|
163
|
+
if (node.tagName.toLowerCase() === 'input') {
|
164
|
+
return nativeInputValueSetter.call(node, value);
|
165
|
+
}
|
166
|
+
return nativeTextareaValueSetter.call(node, value);
|
167
|
+
}
|
168
|
+
|
159
169
|
input(node) {
|
160
170
|
let event = document.createEvent("HTMLEvents");
|
161
171
|
event.initEvent("input", true, false);
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Capybara::Cuprite
|
2
|
+
class Browser
|
3
|
+
module Net
|
4
|
+
def proxy_authorize(user, password)
|
5
|
+
if user && password
|
6
|
+
@proxy_username, @proxy_password = user, password
|
7
|
+
intercept_request("*")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def authorize(user, password)
|
12
|
+
@username, @password = user, password
|
13
|
+
intercept_request("*")
|
14
|
+
end
|
15
|
+
|
16
|
+
def intercept_request(patterns)
|
17
|
+
patterns = Array(patterns).map { |p| { urlPattern: p } }
|
18
|
+
@client.command("Network.setRequestInterception", patterns: patterns)
|
19
|
+
end
|
20
|
+
|
21
|
+
def continue_request(interception_id, options = nil)
|
22
|
+
options ||= {}
|
23
|
+
options = options.merge(interceptionId: interception_id)
|
24
|
+
@client.command("Network.continueInterceptedRequest", **options)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def subscribe_events
|
30
|
+
super if defined?(super)
|
31
|
+
|
32
|
+
@client.subscribe("Network.loadingFailed") do |params|
|
33
|
+
# Free mutex as we aborted main request we are waiting for
|
34
|
+
if params["requestId"] == @request_id && params["canceled"] == true
|
35
|
+
signal
|
36
|
+
@client.command("DOM.getDocument", depth: 0)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@client.subscribe("Network.requestIntercepted") do |params|
|
41
|
+
@authorized_ids ||= []
|
42
|
+
@proxy_authorized_ids ||= []
|
43
|
+
url = params.dig("request", "url")
|
44
|
+
interception_id = params["interceptionId"]
|
45
|
+
|
46
|
+
if params["authChallenge"]
|
47
|
+
response = if params.dig("authChallenge", "source") == "Proxy"
|
48
|
+
if @proxy_authorized_ids.include?(interception_id)
|
49
|
+
{ response: "CancelAuth" }
|
50
|
+
elsif @proxy_username && @proxy_password
|
51
|
+
{ response: "ProvideCredentials",
|
52
|
+
username: @proxy_username,
|
53
|
+
password: @proxy_password }
|
54
|
+
else
|
55
|
+
{ response: "CancelAuth" }
|
56
|
+
end
|
57
|
+
else
|
58
|
+
if @authorized_ids.include?(interception_id)
|
59
|
+
{ response: "CancelAuth" }
|
60
|
+
elsif @username && @password
|
61
|
+
{ response: "ProvideCredentials",
|
62
|
+
username: @username,
|
63
|
+
password: @password }
|
64
|
+
else
|
65
|
+
{ response: "CancelAuth" }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@authorized_ids << interception_id
|
70
|
+
continue_request(interception_id, authChallengeResponse: response)
|
71
|
+
elsif @browser.url_blacklist && !@browser.url_blacklist.empty?
|
72
|
+
if @browser.url_blacklist.any? { |r| r.match(url) }
|
73
|
+
continue_request(interception_id, errorReason: "Aborted")
|
74
|
+
else
|
75
|
+
continue_request(interception_id)
|
76
|
+
end
|
77
|
+
elsif @browser.url_whitelist && !@browser.url_whitelist.empty?
|
78
|
+
if @browser.url_whitelist.any? { |r| r.match(url) }
|
79
|
+
continue_request(interception_id)
|
80
|
+
else
|
81
|
+
continue_request(interception_id, errorReason: "Aborted")
|
82
|
+
end
|
83
|
+
else
|
84
|
+
continue_request(interception_id)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -5,6 +5,7 @@ require "capybara/cuprite/browser/input"
|
|
5
5
|
require "capybara/cuprite/browser/runtime"
|
6
6
|
require "capybara/cuprite/browser/frame"
|
7
7
|
require "capybara/cuprite/browser/client"
|
8
|
+
require "capybara/cuprite/browser/net"
|
8
9
|
require "capybara/cuprite/network/error"
|
9
10
|
require "capybara/cuprite/network/request"
|
10
11
|
require "capybara/cuprite/network/response"
|
@@ -29,7 +30,7 @@ require "capybara/cuprite/network/response"
|
|
29
30
|
module Capybara::Cuprite
|
30
31
|
class Browser
|
31
32
|
class Page
|
32
|
-
include Input, DOM, Runtime, Frame
|
33
|
+
include Input, DOM, Runtime, Frame, Net
|
33
34
|
|
34
35
|
attr_accessor :referrer
|
35
36
|
attr_reader :target_id, :status_code, :response_headers
|
@@ -74,7 +75,11 @@ module Capybara::Cuprite
|
|
74
75
|
options = { url: url }
|
75
76
|
options.merge!(referrer: referrer) if referrer
|
76
77
|
response = command("Page.navigate", **options)
|
77
|
-
|
78
|
+
# https://cs.chromium.org/chromium/src/net/base/net_error_list.h
|
79
|
+
if %w[net::ERR_NAME_NOT_RESOLVED
|
80
|
+
net::ERR_NAME_RESOLUTION_FAILED
|
81
|
+
net::ERR_INTERNET_DISCONNECTED
|
82
|
+
net::ERR_CONNECTION_TIMED_OUT].include?(response["errorText"])
|
78
83
|
raise StatusFailError, "url" => url
|
79
84
|
end
|
80
85
|
response["frameId"]
|
@@ -109,13 +114,13 @@ module Capybara::Cuprite
|
|
109
114
|
end
|
110
115
|
|
111
116
|
def network_traffic(type = nil)
|
112
|
-
case type
|
117
|
+
case type.to_s
|
113
118
|
when "all"
|
114
119
|
@network_traffic
|
115
120
|
when "blocked"
|
116
|
-
@network_traffic # when request blocked
|
121
|
+
@network_traffic.select { |r| r.response.nil? } # when request blocked
|
117
122
|
else
|
118
|
-
@network_traffic # when
|
123
|
+
@network_traffic.select { |r| r.response } # when request isn't blocked
|
119
124
|
end
|
120
125
|
end
|
121
126
|
|
@@ -274,10 +279,6 @@ module Capybara::Cuprite
|
|
274
279
|
end
|
275
280
|
end
|
276
281
|
|
277
|
-
@client.subscribe("Network.requestIntercepted") do |params|
|
278
|
-
@client.command("Network.continueInterceptedRequest", interceptionId: params["interceptionId"], errorReason: "Aborted")
|
279
|
-
end
|
280
|
-
|
281
282
|
@client.subscribe("Log.entryAdded") do |params|
|
282
283
|
source = params.dig("entry", "source")
|
283
284
|
level = params.dig("entry", "level")
|
@@ -297,8 +298,9 @@ module Capybara::Cuprite
|
|
297
298
|
command("Runtime.enable")
|
298
299
|
command("Log.enable")
|
299
300
|
command("Network.enable")
|
301
|
+
|
300
302
|
if Capybara.save_path
|
301
|
-
command("Page.setDownloadBehavior", behavior: "allow", downloadPath: Capybara.save_path)
|
303
|
+
command("Page.setDownloadBehavior", behavior: "allow", downloadPath: Capybara.save_path.to_s)
|
302
304
|
end
|
303
305
|
|
304
306
|
@browser.extensions.each do |extension|
|
@@ -307,6 +309,13 @@ module Capybara::Cuprite
|
|
307
309
|
|
308
310
|
inject_extensions
|
309
311
|
|
312
|
+
width, height = @browser.window_size
|
313
|
+
resize(width: width, height: height)
|
314
|
+
|
315
|
+
url_whitelist = Array(@browser.url_whitelist)
|
316
|
+
url_blacklist = Array(@browser.url_blacklist)
|
317
|
+
intercept_request("*") if !url_whitelist.empty? || !url_blacklist.empty?
|
318
|
+
|
310
319
|
response = command("Page.getNavigationHistory")
|
311
320
|
if response.dig("entries", 0, "transitionType") != "typed"
|
312
321
|
# If we create page by clicking links, submiting forms and so on it
|
@@ -6,26 +6,47 @@ module Capybara::Cuprite
|
|
6
6
|
class Browser
|
7
7
|
class Process
|
8
8
|
KILL_TIMEOUT = 2
|
9
|
-
|
10
9
|
BROWSER_PATH = ENV["BROWSER_PATH"]
|
11
10
|
BROWSER_HOST = "127.0.0.1"
|
12
11
|
BROWSER_PORT = "0"
|
13
|
-
|
14
|
-
# Chromium command line options
|
15
|
-
# https://peter.sh/experiments/chromium-command-line-switches/
|
16
12
|
DEFAULT_OPTIONS = {
|
17
13
|
"headless" => nil,
|
18
14
|
"disable-gpu" => nil,
|
19
15
|
"hide-scrollbars" => nil,
|
20
16
|
"mute-audio" => nil,
|
17
|
+
"enable-automation" => nil,
|
18
|
+
"disable-web-security" => nil,
|
19
|
+
"disable-session-crashed-bubble" => nil,
|
20
|
+
"disable-breakpad" => nil,
|
21
|
+
"disable-sync" => nil,
|
22
|
+
"no-first-run" => nil,
|
23
|
+
"use-mock-keychain" => nil,
|
24
|
+
"keep-alive-for-test" => nil,
|
25
|
+
"disable-popup-blocking" => nil,
|
26
|
+
"disable-extensions" => nil,
|
27
|
+
"disable-hang-monitor" => nil,
|
28
|
+
"disable-features" => "site-per-process,TranslateUI",
|
29
|
+
"disable-translate" => nil,
|
30
|
+
"disable-background-networking" => nil,
|
31
|
+
"enable-features" => "NetworkService,NetworkServiceInProcess",
|
32
|
+
"disable-background-timer-throttling" => nil,
|
33
|
+
"disable-backgrounding-occluded-windows" => nil,
|
34
|
+
"disable-client-side-phishing-detection" => nil,
|
35
|
+
"disable-default-apps" => nil,
|
36
|
+
"disable-dev-shm-usage" => nil,
|
37
|
+
"disable-ipc-flooding-protection" => nil,
|
38
|
+
"disable-prompt-on-repost" => nil,
|
39
|
+
"disable-renderer-backgrounding" => nil,
|
40
|
+
"force-color-profile" => "srgb",
|
41
|
+
"metrics-recording-only" => nil,
|
42
|
+
"safebrowsing-disable-auto-update" => nil,
|
43
|
+
"password-store" => "basic",
|
21
44
|
# Note: --no-sandbox is not needed if you properly setup a user in the container.
|
22
45
|
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
23
46
|
# "no-sandbox" => nil,
|
24
|
-
"enable-automation" => nil,
|
25
|
-
"disable-web-security" => nil,
|
26
47
|
}.freeze
|
27
48
|
|
28
|
-
attr_reader :host, :port, :ws_url, :pid, :path, :options
|
49
|
+
attr_reader :host, :port, :ws_url, :pid, :path, :options, :cmd
|
29
50
|
|
30
51
|
def self.start(*args)
|
31
52
|
new(*args).tap(&:start)
|
@@ -37,7 +58,7 @@ module Capybara::Cuprite
|
|
37
58
|
if Capybara::Cuprite.windows?
|
38
59
|
::Process.kill("KILL", pid)
|
39
60
|
else
|
40
|
-
::Process.kill("
|
61
|
+
::Process.kill("USR1", pid)
|
41
62
|
start = Time.now
|
42
63
|
while ::Process.wait(pid, ::Process::WNOHANG).nil?
|
43
64
|
sleep 0.05
|
@@ -57,8 +78,8 @@ module Capybara::Cuprite
|
|
57
78
|
|
58
79
|
detect_browser_path(options)
|
59
80
|
|
60
|
-
|
61
|
-
@options.merge!("window-size" => window_size.join(","))
|
81
|
+
# Doesn't work on MacOS, so we need to set it by CDP as well
|
82
|
+
@options.merge!("window-size" => options[:window_size].join(","))
|
62
83
|
|
63
84
|
port = options.fetch(:port, BROWSER_PORT)
|
64
85
|
@options.merge!("remote-debugging-port" => port)
|
@@ -66,6 +87,8 @@ module Capybara::Cuprite
|
|
66
87
|
host = options.fetch(:host, BROWSER_HOST)
|
67
88
|
@options.merge!("remote-debugging-address" => host)
|
68
89
|
|
90
|
+
@options.merge!("user-data-dir" => Dir.mktmpdir)
|
91
|
+
|
69
92
|
@options = DEFAULT_OPTIONS.merge(@options)
|
70
93
|
|
71
94
|
unless options.fetch(:headless, true)
|
@@ -74,6 +97,8 @@ module Capybara::Cuprite
|
|
74
97
|
end
|
75
98
|
|
76
99
|
@options.merge!(options.fetch(:browser_options, {}))
|
100
|
+
|
101
|
+
@logger = options.fetch(:logger, nil)
|
77
102
|
end
|
78
103
|
|
79
104
|
def start
|
@@ -85,8 +110,8 @@ module Capybara::Cuprite
|
|
85
110
|
end
|
86
111
|
|
87
112
|
redirect_stdout(write_io) do
|
88
|
-
cmd = [@path] + @options.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
|
89
|
-
@pid = ::Process.spawn(
|
113
|
+
@cmd = [@path] + @options.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
|
114
|
+
@pid = ::Process.spawn(*@cmd, process_options)
|
90
115
|
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
|
91
116
|
end
|
92
117
|
|
@@ -118,7 +143,7 @@ module Capybara::Cuprite
|
|
118
143
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
119
144
|
].find { |path| File.exist?(path) }
|
120
145
|
else
|
121
|
-
%w[chromium google-chrome-unstable google-chrome-beta google-chrome chrome].reduce(nil) do |path, exe|
|
146
|
+
%w[chromium google-chrome-unstable google-chrome-beta google-chrome chrome chromium-browser].reduce(nil) do |path, exe|
|
122
147
|
path = Cliver.detect(exe)
|
123
148
|
break path if path
|
124
149
|
end
|
@@ -126,7 +151,7 @@ module Capybara::Cuprite
|
|
126
151
|
)
|
127
152
|
|
128
153
|
unless @path
|
129
|
-
message = "Could not find an executable
|
154
|
+
message = "Could not find an executable for chrome. Try to make it " \
|
130
155
|
"available on the PATH or set environment varible for " \
|
131
156
|
"example BROWSER_PATH=\"/Applications/Chromium.app/Contents/MacOS/Chromium\""
|
132
157
|
raise Cliver::Dependency::NotFound.new(message)
|
@@ -167,7 +192,7 @@ module Capybara::Cuprite
|
|
167
192
|
IO.select([read_io], nil, nil, max_time - now)
|
168
193
|
else
|
169
194
|
if output.match(regexp)
|
170
|
-
@ws_url = Addressable::URI.parse(output.match(regexp)[1])
|
195
|
+
@ws_url = Addressable::URI.parse(output.match(regexp)[1].strip)
|
171
196
|
@host = @ws_url.host
|
172
197
|
@port = @ws_url.port
|
173
198
|
break
|
@@ -176,6 +201,7 @@ module Capybara::Cuprite
|
|
176
201
|
end
|
177
202
|
|
178
203
|
unless @ws_url
|
204
|
+
@logger.puts output if @logger
|
179
205
|
raise "Chrome process did not produce websocket url within #{timeout} seconds"
|
180
206
|
end
|
181
207
|
end
|
@@ -132,15 +132,20 @@ module Capybara::Cuprite
|
|
132
132
|
|
133
133
|
case response["subtype"]
|
134
134
|
when "node"
|
135
|
-
|
136
|
-
|
137
|
-
|
135
|
+
begin
|
136
|
+
node_id = command("DOM.requestNode", objectId: object_id)["nodeId"]
|
137
|
+
node = command("DOM.describeNode", nodeId: node_id)["node"].merge("nodeId" => node_id)
|
138
|
+
{ "target_id" => target_id, "node" => node }
|
139
|
+
rescue BrowserError => e
|
140
|
+
# Node has disappeared while we were trying to get it
|
141
|
+
raise if e.message != "Could not find node with given id"
|
142
|
+
end
|
138
143
|
when "array"
|
139
144
|
reduce_props(object_id, []) do |memo, key, value|
|
140
145
|
next(memo) unless (Integer(key) rescue nil)
|
141
146
|
value = value["objectId"] ? handle(value) : value["value"]
|
142
147
|
memo.insert(key.to_i, value)
|
143
|
-
end
|
148
|
+
end.compact
|
144
149
|
when "date"
|
145
150
|
response["description"]
|
146
151
|
when "null"
|
@@ -129,8 +129,8 @@ module Capybara::Cuprite
|
|
129
129
|
|
130
130
|
def reset!
|
131
131
|
browser.reset
|
132
|
-
browser.url_blacklist = @options[:url_blacklist]
|
133
|
-
browser.url_whitelist = @options[:url_whitelist]
|
132
|
+
browser.url_blacklist = @options[:url_blacklist]
|
133
|
+
browser.url_whitelist = @options[:url_whitelist]
|
134
134
|
@started = false
|
135
135
|
end
|
136
136
|
|
@@ -190,11 +190,11 @@ module Capybara::Cuprite
|
|
190
190
|
browser.clear_network_traffic
|
191
191
|
end
|
192
192
|
|
193
|
-
# FIXME: check user/pass proxy auth
|
194
193
|
def set_proxy(ip, port, type = "http", user = nil, password = nil, bypass = nil)
|
195
|
-
@options[:
|
196
|
-
@options[:
|
197
|
-
@options[:
|
194
|
+
@options[:browser_options] ||= {}
|
195
|
+
@options[:browser_options].merge!("proxy-server" => "#{type}=#{ip}:#{port}")
|
196
|
+
@options[:browser_options].merge!("proxy-bypass-list" => bypass) if bypass
|
197
|
+
browser.proxy_authorize(user, password)
|
198
198
|
end
|
199
199
|
|
200
200
|
def headers
|
@@ -246,16 +246,10 @@ module Capybara::Cuprite
|
|
246
246
|
browser.clear_memory_cache
|
247
247
|
end
|
248
248
|
|
249
|
-
# * Browser with set settings does not send `Authorize` on POST request
|
250
|
-
# * With manually set header browser makes next request with
|
251
|
-
# `Authorization: Basic Og==` header when settings are empty and the
|
252
|
-
# response was `401 Unauthorized` (which means Base64.encode64(":")).
|
253
|
-
# Combining both methods to reach proper behavior.
|
254
249
|
def basic_authorize(user, password)
|
255
|
-
browser.
|
256
|
-
credentials = ["#{user}:#{password}"].pack("m*").strip
|
257
|
-
add_header("Authorization", "Basic #{credentials}")
|
250
|
+
browser.authorize(user, password)
|
258
251
|
end
|
252
|
+
alias_method :authorize, :basic_authorize
|
259
253
|
|
260
254
|
def pause
|
261
255
|
# STDIN is not necessarily connected to a keyboard. It might even be closed.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuprite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Vorotilin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -208,6 +208,7 @@ files:
|
|
208
208
|
- lib/capybara/cuprite/browser/input.json
|
209
209
|
- lib/capybara/cuprite/browser/input.rb
|
210
210
|
- lib/capybara/cuprite/browser/javascripts/index.js
|
211
|
+
- lib/capybara/cuprite/browser/net.rb
|
211
212
|
- lib/capybara/cuprite/browser/page.rb
|
212
213
|
- lib/capybara/cuprite/browser/process.rb
|
213
214
|
- lib/capybara/cuprite/browser/runtime.rb
|