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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d183c86ecad6aaaf681364bda5ee919e5952a2b3a8d55cad40b3a6cec57ff26f
4
- data.tar.gz: cccf18bb4e24c66262a0a6302ea7611e3aa1897c9b2442c24cfbc0feedfb46b1
3
+ metadata.gz: f883874c9b6d85c217809bb7423c5dc7b31db4932b9bf8ad8e8c469eb0820973
4
+ data.tar.gz: c29bbb53cd0a8153988e4d26dc709a3aa49ad94114a7c175d636705f94eb263c
5
5
  SHA512:
6
- metadata.gz: '056827d707f4e8388aa2350b53791d060f7c30872d5930311606d750822483a667b5d74093e03217cfa3a342c4fe8cd5d5b766fd3155de9c5ff0c6da6b335a17'
7
- data.tar.gz: 389f981e6ae4b3a06cc3babbb70b942b0e2a8844bee690fc508af111bab65b333777f7b330554d2ae0f43abae918e3395ff1ae306b17df015efe94eaebd8c0e8
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 = ['http://www.example.com']
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 = ['http://www.example.com']
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
@@ -17,6 +17,10 @@ module Capybara::Cuprite
17
17
  RbConfig::CONFIG["host_os"] =~ /mingw|mswin|cygwin/
18
18
  end
19
19
 
20
+ def mac?
21
+ RbConfig::CONFIG["host_os"] =~ /darwin/
22
+ end
23
+
20
24
  def mri?
21
25
  defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby"
22
26
  end
@@ -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? resize path network_traffic
30
- clear_network_traffic response_headers refresh click right_click
31
- double_click hover set click_coordinates drag drag_by select
32
- trigger scroll_to send_keys evaluate evaluate_on evaluate_async
33
- execute frame_url frame_title switch_to_frame current_url title
34
- go_back go_forward find_modal accept_confirm dismiss_confirm
35
- accept_prompt dismiss_prompt reset_modals) => :page
36
-
37
- attr_reader :process, :logger, :js_errors
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
- @options = Hash(options)
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 set_http_auth(user, password)
173
- raise NotImplementedError
174
- end
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=(blacklist)
186
- # FIXME: We have to change the format and make it compatible with Chrome not PhantomJS
187
- @url_blacklist = Array(blacklist).map { |p| { urlPattern: p.include?("*") ? p : "*#{p}*" } }
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
@@ -38,6 +38,8 @@ module Capybara::Cuprite
38
38
  private
39
39
 
40
40
  def subscribe_events
41
+ super if defined?(super)
42
+
41
43
  @client.subscribe("Page.frameAttached") do |params|
42
44
  @frames[params["frameId"]] = { "parent_id" => params["parentFrameId"] }
43
45
  end
@@ -124,12 +124,12 @@ class Cuprite {
124
124
  }
125
125
 
126
126
  this.trigger(node, "focus");
127
- node.value = "";
127
+ this.setValue(node, "");
128
128
 
129
129
  if (node.type == "number" || node.type == "date") {
130
- node.value = value;
130
+ this.setValue(node, value);
131
131
  } else if (node.type == "time") {
132
- node.value = new Date(value).toTimeString().split(" ")[0];
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.value = `${year}-${month}-${date}T${hour}:${min}:${sec}`;
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 += char;
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
- if %w[net::ERR_NAME_NOT_RESOLVED net::ERR_NAME_RESOLUTION_FAILED].include?(response["errorText"])
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 not request blocked
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("TERM", pid)
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
- window_size = options.fetch(:window_size, [1024, 768])
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(*cmd, process_options)
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 `#{exe}`. Try to make it " \
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
- node_id = command("DOM.requestNode", objectId: object_id)["nodeId"]
136
- node = command("DOM.describeNode", nodeId: node_id)["node"].merge("nodeId" => node_id)
137
- { "target_id" => target_id, "node" => node }
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] if @options.key?(:url_blacklist)
133
- browser.url_whitelist = @options[:url_whitelist] if @options.key?(: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[:browser] ||= {}
196
- @options[:browser].merge!("proxy-server" => "#{type}=#{ip}:#{port}")
197
- @options[:browser].merge!("proxy-bypass-list" => bypass) if bypass
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.set_http_auth(user, password)
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.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Cuprite
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
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.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-01-31 00:00:00.000000000 Z
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