cuprite 0.12 → 0.15.1

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 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