cuprite 0.12 → 0.15.1

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: bac1e4393d6796d34198e45428ea8d443b67136e4805e77f7c7d3f35451cefbb
4
- data.tar.gz: 27edf7157ac47b0a6bbc579e7908aaa0313d91b7f3702417453f76f1230e882d
3
+ metadata.gz: 78b7dde58d02147e2985473da0a7527d338ff086e4a4aaf0fd9302e5faf3ee11
4
+ data.tar.gz: 777726c6161975950df923a305b0bdb9da54b5531b7a5339ed0d4beaf264beb6
5
5
  SHA512:
6
- metadata.gz: a8ed43b593e4a071e449a80ac92925abafff1d9afa2a3934783963a19411deddcc18b1906467d5d355a93ed891ec4aae4b21509031190020d154202ba21904e8
7
- data.tar.gz: ed39a7d445203f93b402fc313caa883449d7e743a6690196ba7f6bed9ee1d8c506f06d4b8ae620c8bc9d1c21c5770458b88564e9e937f7b5583e01a931fece39
6
+ metadata.gz: 04e88a47d9d2d2df68ad11dca8224f6d24001340b1743f538e6fdb64e818381037da7987b9db25284336b469c986d9d9e883192743749c4c6cad6c43adc160a1
7
+ data.tar.gz: a059dae9790af13c8a56999d6d76d5920e863e217b346a721a4051f0cc786e295a35c0e574c00e425b87e2d0c7a8b140c991183ab3246d1678566318745c9ecf
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018-2021 Dmitry Vorotilin
3
+ Copyright (c) 2018-2022 Dmitry Vorotilin
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -4,12 +4,7 @@ Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
4
4
  dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
5
5
  you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
6
6
  uses [Ferrum](https://github.com/rubycdp/ferrum#index) which is high-level API
7
- to the browser by CDP protocol. The design of the driver is as close to
8
- [Poltergeist](https://github.com/teampoltergeist/poltergeist) as possible though
9
- it's not a goal.
10
-
11
- [Cuprite](https://evrone.com/cuprite) designed & supported by [Evrone](https://evrone.com/)
12
- What else we build [with Ruby](https://evrone.com/ruby)
7
+ to the browser by CDP protocol.
13
8
 
14
9
 
15
10
  ## Install
@@ -46,8 +41,7 @@ browser = page.driver.browser
46
41
  browser.mouse.move(x: 123, y: 456).down.up
47
42
  ```
48
43
 
49
- If you already have tests on Poltergeist then it should simply work, for
50
- Selenium you better check your code for `manage` calls because it works
44
+ For Selenium you better check your code for `manage` calls because it works
51
45
  differently in Cuprite, see the documentation below.
52
46
 
53
47
 
@@ -67,8 +61,8 @@ end
67
61
  `Cuprite`-specific options are:
68
62
 
69
63
  * options `Hash`
70
- * `:url_blacklist` (Array) - array of strings to match against requested URLs
71
- * `:url_whitelist` (Array) - array of strings to match against requested URLs
64
+ * `:url_blacklist` (Array) - array of regexes to match against requested URLs
65
+ * `:url_whitelist` (Array) - array of regexes to match against requested URLs
72
66
 
73
67
 
74
68
  ## Debugging
@@ -186,6 +180,9 @@ Besides capybara screenshot method you can get image as Base64:
186
180
  ## Authorization
187
181
 
188
182
  * `page.driver.basic_authorize(user, password)`
183
+
184
+ ## Proxy
185
+
189
186
  * `page.driver.set_proxy(ip, port, type, user, password)`
190
187
 
191
188
 
@@ -195,16 +192,21 @@ Cuprite supports URL blacklisting, which allows you to prevent scripts from
195
192
  running on designated domains:
196
193
 
197
194
  ```ruby
198
- page.driver.browser.url_blacklist = ["http://www.example.com"]
195
+ page.driver.browser.url_blacklist = %r{http://www.example.com}
199
196
  ```
200
197
 
201
198
  and also URL whitelisting, which allows scripts to only run on designated
202
199
  domains:
203
200
 
204
201
  ```ruby
205
- page.driver.browser.url_whitelist = ["http://www.example.com"]
202
+ page.driver.browser.url_whitelist = %r{http://www.example.com}
206
203
  ```
207
204
 
208
205
  If you are experiencing slower run times, consider creating a URL whitelist of
209
206
  domains that are essential or a blacklist of domains that are not essential,
210
207
  such as ad networks or analytics, to your testing environment.
208
+
209
+ ## License
210
+
211
+ The gem is available as open source under the terms of the
212
+ [MIT License](https://opensource.org/licenses/MIT).
@@ -2,213 +2,254 @@
2
2
 
3
3
  require "forwardable"
4
4
 
5
- module Capybara::Cuprite
6
- class Browser < Ferrum::Browser
7
- extend Forwardable
5
+ module Capybara
6
+ module Cuprite
7
+ class Browser < Ferrum::Browser
8
+ extend Forwardable
8
9
 
9
- delegate %i[send_keys select set hover trigger before_click switch_to_frame
10
- find_modal accept_confirm dismiss_confirm accept_prompt
11
- dismiss_prompt reset_modals] => :page
10
+ delegate %i[send_keys select set hover trigger before_click switch_to_frame
11
+ find_modal accept_confirm dismiss_confirm accept_prompt
12
+ dismiss_prompt reset_modals] => :page
12
13
 
13
- attr_reader :url_blacklist, :url_whitelist
14
+ def initialize(options = nil)
15
+ super
14
16
 
15
- def initialize(options = nil)
16
- options ||= {}
17
- self.url_blacklist = options[:url_blacklist]
18
- self.url_whitelist = options[:url_whitelist]
17
+ @options.url_blacklist = prepare_wildcards(options&.dig(:url_blacklist))
18
+ @options.url_whitelist = prepare_wildcards(options&.dig(:url_whitelist))
19
19
 
20
- super
21
- @page = false
22
- end
20
+ @page = nil
21
+ end
23
22
 
24
- def page
25
- raise Ferrum::NoSuchPageError if @page.nil?
26
- @page ||= attach_page
27
- end
23
+ def command(...)
24
+ super
25
+ rescue Ferrum::DeadBrowserError
26
+ restart
27
+ raise
28
+ end
28
29
 
29
- def reset
30
- super
31
- @page = attach_page
32
- end
30
+ def page
31
+ raise Ferrum::NoSuchPageError if @page&.closed?
33
32
 
34
- def quit
35
- super
36
- @page = false
37
- end
33
+ @page ||= attach_page
34
+ end
38
35
 
39
- def url_whitelist=(patterns)
40
- @url_whitelist = prepare_wildcards(patterns)
41
- page.network.intercept if @client && !@url_whitelist.empty?
42
- end
36
+ def reset
37
+ super
38
+ @options.reset_window_size
39
+ @page = nil
40
+ end
43
41
 
44
- def url_blacklist=(patterns)
45
- @url_blacklist = prepare_wildcards(patterns)
46
- page.network.intercept if @client && !@url_blacklist.empty?
47
- end
42
+ def quit
43
+ super
44
+ @page = nil
45
+ end
48
46
 
49
- def visit(*args)
50
- goto(*args)
51
- end
47
+ def resize(**options)
48
+ @options.window_size = [options[:width], options[:height]]
49
+ super
50
+ end
52
51
 
53
- def status_code
54
- network.status
55
- end
52
+ def url_whitelist
53
+ @options.url_whitelist
54
+ end
55
+ alias url_allowlist url_whitelist
56
56
 
57
- def find(method, selector)
58
- find_all(method, selector)
59
- end
57
+ def url_whitelist=(patterns)
58
+ @options.url_whitelist = prepare_wildcards(patterns)
59
+ page.network.whitelist = @options.url_whitelist if @client && @options.url_whitelist.any?
60
+ end
61
+ alias url_allowlist= url_whitelist=
60
62
 
61
- def property(node, name)
62
- node.property(name)
63
- end
63
+ def url_blacklist
64
+ @options.url_blacklist
65
+ end
66
+ alias url_blocklist url_blacklist
64
67
 
65
- def find_within(node, method, selector)
66
- resolved = page.command("DOM.resolveNode", nodeId: node.node_id)
67
- object_id = resolved.dig("object", "objectId")
68
- find_all(method, selector, { "objectId" => object_id })
69
- end
68
+ def url_blacklist=(patterns)
69
+ @options.url_blacklist = prepare_wildcards(patterns)
70
+ page.network.blacklist = @options.url_blacklist if @client && @options.url_blacklist.any?
71
+ end
72
+ alias url_blocklist= url_blacklist=
70
73
 
71
- def window_handle
72
- page.target_id
73
- end
74
+ def visit(*args)
75
+ goto(*args)
76
+ end
74
77
 
75
- def window_handles
76
- targets.keys
77
- end
78
+ def status_code
79
+ network.status
80
+ end
78
81
 
79
- def within_window(locator = nil, &block)
80
- original = window_handle
82
+ def find(method, selector)
83
+ find_all(method, selector)
84
+ end
81
85
 
82
- if Capybara::VERSION.to_f < 3.0
83
- target_id = window_handles.find do |target_id|
84
- page = attach_page(target_id)
85
- locator == page.frame_name
86
- end
87
- locator = target_id if target_id
86
+ def property(node, name)
87
+ node.property(name)
88
+ end
89
+
90
+ def find_within(node, method, selector)
91
+ resolved = page.command("DOM.resolveNode", nodeId: node.node_id)
92
+ object_id = resolved.dig("object", "objectId")
93
+ find_all(method, selector, { "objectId" => object_id })
94
+ end
95
+
96
+ def window_handle
97
+ page.target_id
88
98
  end
89
99
 
90
- if window_handles.include?(locator)
100
+ def window_handles
101
+ targets.keys
102
+ end
103
+
104
+ def within_window(locator = nil)
105
+ original = window_handle
106
+ raise Ferrum::NoSuchPageError unless window_handles.include?(locator)
107
+
91
108
  switch_to_window(locator)
92
109
  yield
93
- else
94
- raise Ferrum::NoSuchPageError
110
+ ensure
111
+ switch_to_window(original)
95
112
  end
96
- ensure
97
- switch_to_window(original)
98
- end
99
113
 
100
- def switch_to_window(target_id)
101
- target = targets[target_id]
102
- raise Ferrum::NoSuchPageError unless target
103
- @page = attach_page(target.id)
104
- end
114
+ def switch_to_window(target_id)
115
+ target = targets[target_id]
116
+ raise Ferrum::NoSuchPageError unless target
105
117
 
106
- def close_window(target_id)
107
- target = targets[target_id]
108
- raise Ferrum::NoSuchPageError unless target
109
- @page = nil if @page.target_id == target.id
110
- target.page.close
111
- end
118
+ @page = attach_page(target.id)
119
+ end
112
120
 
113
- def browser_error
114
- evaluate("_cuprite.browserError()")
115
- end
121
+ def close_window(target_id)
122
+ target = targets[target_id]
123
+ raise Ferrum::NoSuchPageError unless target
116
124
 
117
- def source
118
- raise NotImplementedError
119
- end
125
+ @page = ClosedPage.new if @page.target_id == target.id
126
+ target.page.close
127
+ targets.delete(target_id) # page.close is async, delete target asap
128
+ end
120
129
 
121
- def drag(node, other)
122
- raise NotImplementedError
123
- end
130
+ def active_element
131
+ evaluate("document.activeElement")
132
+ end
124
133
 
125
- def drag_by(node, x, y)
126
- raise NotImplementedError
127
- end
134
+ def browser_error
135
+ evaluate("_cuprite.browserError()")
136
+ end
128
137
 
129
- def select_file(node, value)
130
- node.select_file(value)
131
- end
138
+ def source
139
+ raise NotImplementedError
140
+ end
132
141
 
133
- def parents(node)
134
- evaluate_on(node: node, expression: "_cuprite.parents(this)", by_value: false)
135
- end
142
+ def drag(node, other, steps, delay = nil)
143
+ x1, y1 = node.find_position
144
+ x2, y2 = other.find_position
136
145
 
137
- def visible_text(node)
138
- evaluate_on(node: node, expression: "_cuprite.visibleText(this)")
139
- end
146
+ mouse.move(x: x1, y: y1)
147
+ mouse.down
148
+ sleep delay if delay
149
+ mouse.move(x: x2, y: y2, steps: steps)
150
+ mouse.up
151
+ end
140
152
 
141
- def delete_text(node)
142
- evaluate_on(node: node, expression: "_cuprite.deleteText(this)")
143
- end
153
+ def drag_by(node, x, y, steps, delay = nil)
154
+ x1, y1 = node.find_position
155
+ x2 = x1 + x
156
+ y2 = y1 + y
144
157
 
145
- def attributes(node)
146
- value = evaluate_on(node: node, expression: "_cuprite.getAttributes(this)")
147
- JSON.parse(value)
148
- end
158
+ mouse.move(x: x1, y: y1)
159
+ mouse.down
160
+ sleep delay if delay
161
+ mouse.move(x: x2, y: y2, steps: steps)
162
+ mouse.up
163
+ end
149
164
 
150
- def attribute(node, name)
151
- evaluate_on(node: node, expression: %Q(_cuprite.getAttribute(this, "#{name}")))
152
- end
165
+ def select_file(node, value)
166
+ node.select_file(value)
167
+ end
153
168
 
154
- def value(node)
155
- evaluate_on(node: node, expression: "_cuprite.value(this)")
156
- end
169
+ def parents(node)
170
+ evaluate_on(node: node, expression: "_cuprite.parents(this)", by_value: false)
171
+ end
157
172
 
158
- def visible?(node)
159
- evaluate_on(node: node, expression: "_cuprite.isVisible(this)")
160
- end
173
+ def visible_text(node)
174
+ evaluate_on(node: node, expression: "_cuprite.visibleText(this)")
175
+ end
161
176
 
162
- def disabled?(node)
163
- evaluate_on(node: node, expression: "_cuprite.isDisabled(this)")
164
- end
177
+ def delete_text(node)
178
+ evaluate_on(node: node, expression: "_cuprite.deleteText(this)")
179
+ end
165
180
 
166
- def path(node)
167
- evaluate_on(node: node, expression: "_cuprite.path(this)")
168
- end
181
+ def attributes(node)
182
+ value = evaluate_on(node: node, expression: "_cuprite.getAttributes(this)")
183
+ JSON.parse(value)
184
+ end
169
185
 
170
- def all_text(node)
171
- node.text
172
- end
186
+ def attribute(node, name)
187
+ evaluate_on(node: node, expression: %(_cuprite.getAttribute(this, "#{name}")))
188
+ end
189
+
190
+ def value(node)
191
+ evaluate_on(node: node, expression: "_cuprite.value(this)")
192
+ end
193
+
194
+ def visible?(node)
195
+ evaluate_on(node: node, expression: "_cuprite.isVisible(this)")
196
+ end
197
+
198
+ def disabled?(node)
199
+ evaluate_on(node: node, expression: "_cuprite.isDisabled(this)")
200
+ end
201
+
202
+ def path(node)
203
+ evaluate_on(node: node, expression: "_cuprite.path(this)")
204
+ end
205
+
206
+ def all_text(node)
207
+ node.text
208
+ end
173
209
 
174
- private
210
+ private
175
211
 
176
- def find_all(method, selector, within = nil)
177
- begin
212
+ def find_all(method, selector, within = nil)
178
213
  nodes = if within
179
- evaluate("_cuprite.find(arguments[0], arguments[1], arguments[2])", method, selector, within)
180
- else
181
- evaluate("_cuprite.find(arguments[0], arguments[1])", method, selector)
182
- end
214
+ evaluate("_cuprite.find(arguments[0], arguments[1], arguments[2])", method, selector, within)
215
+ else
216
+ evaluate("_cuprite.find(arguments[0], arguments[1])", method, selector)
217
+ end
183
218
 
184
- nodes.map { |n| n.node? ? n : next }.compact
219
+ nodes.select(&:node?)
185
220
  rescue Ferrum::JavaScriptError => e
186
- if e.class_name == "InvalidSelector"
187
- raise InvalidSelector.new(e.response, method, selector)
188
- end
221
+ raise InvalidSelector.new(e.response, method, selector) if e.class_name == "InvalidSelector"
222
+
189
223
  raise
190
224
  end
191
- end
192
225
 
193
- def prepare_wildcards(wc)
194
- Array(wc).map do |wildcard|
195
- if wildcard.is_a?(Regexp)
196
- wildcard
197
- else
198
- wildcard = wildcard.gsub("*", ".*")
199
- Regexp.new(wildcard, Regexp::IGNORECASE)
226
+ def prepare_wildcards(patterns)
227
+ string_passed = false
228
+
229
+ Array(patterns).map do |pattern|
230
+ if pattern.is_a?(Regexp)
231
+ pattern
232
+ else
233
+ string_passed = true
234
+ pattern = pattern.gsub("*", ".*")
235
+ Regexp.new(pattern, Regexp::IGNORECASE)
236
+ end
237
+ end
238
+ ensure
239
+ if string_passed
240
+ warn "Passing strings to blacklist/whitelist is deprecated, pass regexp at #{caller(4..4).first}"
200
241
  end
201
242
  end
202
- end
203
243
 
204
- def attach_page(target_id = nil)
205
- target = targets[target_id] if target_id
206
- target ||= default_context.default_target
207
- return target.page if target.attached?
244
+ def attach_page(target_id = nil)
245
+ target = targets[target_id] if target_id
246
+ target ||= default_context.default_target
247
+ return target.page if target.connected?
208
248
 
209
- target.maybe_sleep_if_new_window
210
- target.page = Page.new(target.id, self)
211
- target.page
249
+ target.maybe_sleep_if_new_window
250
+ target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
251
+ target.page
252
+ end
212
253
  end
213
254
  end
214
255
  end
@@ -1,46 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Capybara::Cuprite
4
- class Cookie
5
- def initialize(attributes)
6
- @attributes = attributes
7
- end
3
+ module Capybara
4
+ module Cuprite
5
+ class Cookie
6
+ def initialize(attributes)
7
+ @attributes = attributes
8
+ end
8
9
 
9
- def name
10
- @attributes["name"]
11
- end
10
+ def name
11
+ @attributes["name"]
12
+ end
12
13
 
13
- def value
14
- @attributes["value"]
15
- end
14
+ def value
15
+ @attributes["value"]
16
+ end
16
17
 
17
- def domain
18
- @attributes["domain"]
19
- end
18
+ def domain
19
+ @attributes["domain"]
20
+ end
20
21
 
21
- def path
22
- @attributes["path"]
23
- end
22
+ def path
23
+ @attributes["path"]
24
+ end
24
25
 
25
- def size
26
- @attributes["size"]
27
- end
26
+ def size
27
+ @attributes["size"]
28
+ end
28
29
 
29
- def secure?
30
- @attributes["secure"]
31
- end
30
+ def secure?
31
+ @attributes["secure"]
32
+ end
32
33
 
33
- def httponly?
34
- @attributes["httpOnly"]
35
- end
34
+ def httponly?
35
+ @attributes["httpOnly"]
36
+ end
36
37
 
37
- def session?
38
- @attributes["session"]
39
- end
38
+ def session?
39
+ @attributes["session"]
40
+ end
40
41
 
41
- def expires
42
- if @attributes["expires"] > 0
43
- Time.at(@attributes["expires"])
42
+ def expires
43
+ Time.at(@attributes["expires"]) if (@attributes["expires"]).positive?
44
44
  end
45
45
  end
46
46
  end