puppeteer-ruby 0.28.1 → 0.31.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,164 @@
1
+ class Puppeteer::Puppeteer
2
+ # @param project_root [String]
3
+ # @param prefereed_revision [String]
4
+ # @param is_puppeteer_core [String]
5
+ def initialize(project_root:, preferred_revision:, is_puppeteer_core:)
6
+ @project_root = project_root
7
+ @preferred_revision = preferred_revision
8
+ @is_puppeteer_core = is_puppeteer_core
9
+ end
10
+
11
+ # @param product [String]
12
+ # @param executable_path [String]
13
+ # @param ignore_default_args [Array<String>|nil]
14
+ # @param handle_SIGINT [Boolean]
15
+ # @param handle_SIGTERM [Boolean]
16
+ # @param handle_SIGHUP [Boolean]
17
+ # @param timeout [Integer]
18
+ # @param dumpio [Boolean]
19
+ # @param env [Hash]
20
+ # @param pipe [Boolean]
21
+ # @param args [Array<String>]
22
+ # @param user_data_dir [String]
23
+ # @param devtools [Boolean]
24
+ # @param headless [Boolean]
25
+ # @param ignore_https_errors [Boolean]
26
+ # @param default_viewport [Puppeteer::Viewport|nil]
27
+ # @param slow_mo [Integer]
28
+ # @return [Puppeteer::Browser]
29
+ def launch(
30
+ product: nil,
31
+ executable_path: nil,
32
+ ignore_default_args: nil,
33
+ handle_SIGINT: nil,
34
+ handle_SIGTERM: nil,
35
+ handle_SIGHUP: nil,
36
+ timeout: nil,
37
+ dumpio: nil,
38
+ env: nil,
39
+ pipe: nil,
40
+ args: nil,
41
+ user_data_dir: nil,
42
+ devtools: nil,
43
+ headless: nil,
44
+ ignore_https_errors: nil,
45
+ default_viewport: nil,
46
+ slow_mo: nil
47
+ )
48
+ options = {
49
+ executable_path: executable_path,
50
+ ignore_default_args: ignore_default_args,
51
+ handle_SIGINT: handle_SIGINT,
52
+ handle_SIGTERM: handle_SIGTERM,
53
+ handle_SIGHUP: handle_SIGHUP,
54
+ timeout: timeout,
55
+ dumpio: dumpio,
56
+ env: env,
57
+ pipe: pipe,
58
+ args: args,
59
+ user_data_dir: user_data_dir,
60
+ devtools: devtools,
61
+ headless: headless,
62
+ ignore_https_errors: ignore_https_errors,
63
+ default_viewport: default_viewport,
64
+ slow_mo: slow_mo,
65
+ }
66
+
67
+ @product_name ||= product
68
+ browser = launcher.launch(options)
69
+ if block_given?
70
+ begin
71
+ yield(browser)
72
+ ensure
73
+ browser.close
74
+ end
75
+ else
76
+ browser
77
+ end
78
+ end
79
+
80
+ # @param browser_ws_endpoint [String]
81
+ # @param browser_url [String]
82
+ # @param transport [Puppeteer::WebSocketTransport]
83
+ # @param ignore_https_errors [Boolean]
84
+ # @param default_viewport [Puppeteer::Viewport|nil]
85
+ # @param slow_mo [Integer]
86
+ # @return [Puppeteer::Browser]
87
+ def connect(
88
+ browser_ws_endpoint: nil,
89
+ browser_url: nil,
90
+ transport: nil,
91
+ ignore_https_errors: nil,
92
+ default_viewport: nil,
93
+ slow_mo: nil
94
+ )
95
+ options = {
96
+ browser_ws_endpoint: browser_ws_endpoint,
97
+ browser_url: browser_url,
98
+ transport: transport,
99
+ ignore_https_errors: ignore_https_errors,
100
+ default_viewport: default_viewport,
101
+ slow_mo: slow_mo,
102
+ }.compact
103
+ browser = launcher.connect(options)
104
+ if block_given?
105
+ begin
106
+ yield(browser)
107
+ ensure
108
+ browser.disconnect
109
+ end
110
+ else
111
+ browser
112
+ end
113
+ end
114
+
115
+ # @return [String]
116
+ def executable_path
117
+ launcher.executable_path
118
+ end
119
+
120
+ private def launcher
121
+ @launcher ||= Puppeteer::Launcher.new(
122
+ project_root: @project_root,
123
+ preferred_revision: @preferred_revision,
124
+ is_puppeteer_core: @is_puppeteer_core,
125
+ product: @product_name,
126
+ )
127
+ end
128
+
129
+ # @return [String]
130
+ def product
131
+ launcher.product
132
+ end
133
+
134
+ # @return [Puppeteer::Devices]
135
+ def devices
136
+ Puppeteer::Devices
137
+ end
138
+
139
+ # # @return {Object}
140
+ # def errors
141
+ # # ???
142
+ # end
143
+
144
+ # @param args [Array<String>]
145
+ # @param user_data_dir [String]
146
+ # @param devtools [Boolean]
147
+ # @param headless [Boolean]
148
+ # @return [Array<String>]
149
+ def default_args(args: nil, user_data_dir: nil, devtools: nil, headless: nil)
150
+ options = {
151
+ args: args,
152
+ user_data_dir: user_data_dir,
153
+ devtools: devtools,
154
+ headless: headless,
155
+ }.compact
156
+ launcher.default_args(options)
157
+ end
158
+
159
+ # @param {!BrowserFetcher.Options=} options
160
+ # @return {!BrowserFetcher}
161
+ def createBrowserFetcher(options = {})
162
+ BrowserFetcher.new(@project_root, options)
163
+ end
164
+ end
@@ -0,0 +1,65 @@
1
+ require 'singleton'
2
+
3
+ class Puppeteer::QueryHandlerManager
4
+ include Singleton
5
+
6
+ def query_handlers
7
+ @query_handlers ||= {
8
+ aria: Puppeteer::AriaQueryHandler.new,
9
+ }
10
+ end
11
+
12
+ private def default_handler
13
+ @default_handler ||= Puppeteer::CustomQueryHandler.new(
14
+ query_one: '(element, selector) => element.querySelector(selector)',
15
+ query_all: '(element, selector) => element.querySelectorAll(selector)',
16
+ )
17
+ end
18
+
19
+ class Result
20
+ def initialize(query_handler:, selector:)
21
+ @query_handler = query_handler
22
+ @selector = selector
23
+ end
24
+
25
+ def query_one(element_handle)
26
+ @query_handler.query_one(element_handle, @selector)
27
+ end
28
+
29
+ def wait_for(dom_world, visible:, hidden:, timeout:)
30
+ @query_handler.wait_for(dom_world, @selector, visible: visible, hidden: hidden, timeout: timeout)
31
+ end
32
+
33
+ def query_all(element_handle)
34
+ @query_handler.query_all(element_handle, @selector)
35
+ end
36
+
37
+ def query_all_array(element_handle)
38
+ @query_handler.query_all_array(element_handle, @selector)
39
+ end
40
+ end
41
+
42
+ def detect_query_handler(selector)
43
+ unless /^[a-zA-Z]+\// =~ selector
44
+ return Result.new(
45
+ query_handler: default_handler,
46
+ selector: selector,
47
+ )
48
+ end
49
+
50
+ chunk = selector.split("/")
51
+ name = chunk.shift
52
+ updated_selector = chunk.join("/")
53
+
54
+ query_handler = query_handlers[name.to_sym]
55
+
56
+ unless query_handler
57
+ raise ArgumentError.new("Query set to use \"#{name}\", but no query handler of that name was found")
58
+ end
59
+
60
+ Result.new(
61
+ query_handler: query_handler,
62
+ selector: updated_selector,
63
+ )
64
+ end
65
+ end
@@ -97,6 +97,18 @@ class Puppeteer::RemoteObject
97
97
  nil
98
98
  end
99
99
 
100
+ # used in ElementHandle#query_ax_tree
101
+ def query_ax_tree(client, accessible_name: nil, role: nil)
102
+ result = client.send_message('Accessibility.queryAXTree', {
103
+ objectId: @object_id,
104
+ accessibleName: accessible_name,
105
+ role: role,
106
+ }.compact)
107
+
108
+ result['nodes'].reject do |node|
109
+ node['role']['value'] == 'text'
110
+ end
111
+ end
100
112
 
101
113
  # helper#valueFromRemoteObject
102
114
  def value
@@ -1,3 +1,3 @@
1
- class Puppeteer
2
- VERSION = '0.28.1'
1
+ module Puppeteer
2
+ VERSION = '0.31.3'
3
3
  end
@@ -9,7 +9,7 @@ class Puppeteer::WaitTask
9
9
  end
10
10
  end
11
11
 
12
- def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [])
12
+ def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [], binding_function: nil)
13
13
  if polling.is_a?(String)
14
14
  if polling != 'raf' && polling != 'mutation'
15
15
  raise ArgumentError.new("Unknown polling option: #{polling}")
@@ -27,8 +27,12 @@ class Puppeteer::WaitTask
27
27
  @timeout = timeout
28
28
  @predicate_body = "return (#{predicate_body})(...args);"
29
29
  @args = args
30
+ @binding_function = binding_function
30
31
  @run_count = 0
31
- @dom_world._wait_tasks.add(self)
32
+ @dom_world.send(:_wait_tasks).add(self)
33
+ if binding_function
34
+ @dom_world.send(:_bound_functions)[binding_function.name] = binding_function
35
+ end
32
36
  @promise = resolvable_future
33
37
 
34
38
  # Since page navigation requires us to re-install the pageScript, we should track
@@ -53,8 +57,16 @@ class Puppeteer::WaitTask
53
57
 
54
58
  def rerun
55
59
  run_count = (@run_count += 1)
60
+ context = @dom_world.execution_context
61
+
62
+ return if @terminated || run_count != @run_count
63
+ if @binding_function
64
+ @dom_world.add_binding_to_context(context, @binding_function)
65
+ end
66
+ return if @terminated || run_count != @run_count
67
+
56
68
  begin
57
- success = @dom_world.execution_context.evaluate_handle(
69
+ success = context.evaluate_handle(
58
70
  WAIT_FOR_PREDICATE_PAGE_FUNCTION,
59
71
  @predicate_body,
60
72
  @polling,
@@ -103,7 +115,7 @@ class Puppeteer::WaitTask
103
115
 
104
116
  private def cleanup
105
117
  @timeout_cleared = true
106
- @dom_world._wait_tasks.delete(self)
118
+ @dom_world.send(:_wait_tasks).delete(self)
107
119
  end
108
120
 
109
121
  private define_async_method :async_rerun
@@ -123,18 +135,17 @@ class Puppeteer::WaitTask
123
135
  /**
124
136
  * @return {!Promise<*>}
125
137
  */
126
- function pollMutation() {
127
- const success = predicate(...args);
128
- if (success)
129
- return Promise.resolve(success);
138
+ async function pollMutation() {
139
+ const success = await predicate(...args);
140
+ if (success) return Promise.resolve(success);
130
141
  let fulfill;
131
142
  const result = new Promise((x) => (fulfill = x));
132
- const observer = new MutationObserver(() => {
143
+ const observer = new MutationObserver(async () => {
133
144
  if (timedOut) {
134
145
  observer.disconnect();
135
146
  fulfill();
136
147
  }
137
- const success = predicate(...args);
148
+ const success = await predicate(...args);
138
149
  if (success) {
139
150
  observer.disconnect();
140
151
  fulfill(success);
@@ -147,38 +158,34 @@ class Puppeteer::WaitTask
147
158
  });
148
159
  return result;
149
160
  }
150
- function pollRaf() {
161
+ async function pollRaf() {
151
162
  let fulfill;
152
163
  const result = new Promise((x) => (fulfill = x));
153
- onRaf();
164
+ await onRaf();
154
165
  return result;
155
- function onRaf() {
166
+ async function onRaf() {
156
167
  if (timedOut) {
157
168
  fulfill();
158
169
  return;
159
170
  }
160
- const success = predicate(...args);
161
- if (success)
162
- fulfill(success);
163
- else
164
- requestAnimationFrame(onRaf);
171
+ const success = await predicate(...args);
172
+ if (success) fulfill(success);
173
+ else requestAnimationFrame(onRaf);
165
174
  }
166
175
  }
167
- function pollInterval(pollInterval) {
176
+ async function pollInterval(pollInterval) {
168
177
  let fulfill;
169
178
  const result = new Promise((x) => (fulfill = x));
170
- onTimeout();
179
+ await onTimeout();
171
180
  return result;
172
- function onTimeout() {
181
+ async function onTimeout() {
173
182
  if (timedOut) {
174
183
  fulfill();
175
184
  return;
176
185
  }
177
- const success = predicate(...args);
178
- if (success)
179
- fulfill(success);
180
- else
181
- setTimeout(onTimeout, pollInterval);
186
+ const success = await predicate(...args);
187
+ if (success) fulfill(success);
188
+ else setTimeout(onTimeout, pollInterval);
182
189
  }
183
190
  }
184
191
  }
@@ -18,6 +18,8 @@ class Puppeteer::WebSocket
18
18
  @socket.write(data)
19
19
  rescue Errno::EPIPE
20
20
  raise EOFError.new('already closed')
21
+ rescue Errno::ECONNRESET
22
+ raise EOFError.new('closed by remote')
21
23
  end
22
24
 
23
25
  def readpartial(maxlen = 1024)
@@ -37,6 +37,8 @@ class Puppeteer::WebSocketTransport
37
37
 
38
38
  def close
39
39
  @ws.close
40
+ rescue EOFError
41
+ # ignore EOLError. The connection is already closed.
40
42
  end
41
43
 
42
44
  def on_close(&block)
@@ -12,7 +12,9 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = 'https://github.com/YusukeIwaki/puppeteer-ruby'
13
13
 
14
14
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
15
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/}) || f.include?(".git") || f.include?(".circleci") || f.start_with?("development/")
17
+ end
16
18
  end
17
19
  spec.bindir = 'exe'
18
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -23,11 +25,12 @@ Gem::Specification.new do |spec|
23
25
  spec.add_dependency 'mime-types', '>= 3.0'
24
26
  spec.add_development_dependency 'bundler', '~> 2.2.3'
25
27
  spec.add_development_dependency 'chunky_png'
28
+ spec.add_development_dependency 'dry-inflector'
26
29
  spec.add_development_dependency 'pry-byebug'
27
30
  spec.add_development_dependency 'rake', '~> 13.0.3'
28
31
  spec.add_development_dependency 'rspec', '~> 3.10.0 '
29
32
  spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
30
- spec.add_development_dependency 'rubocop', '~> 1.8.0'
33
+ spec.add_development_dependency 'rubocop', '~> 1.12.0'
31
34
  spec.add_development_dependency 'rubocop-rspec'
32
35
  spec.add_development_dependency 'sinatra'
33
36
  spec.add_development_dependency 'webrick'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppeteer-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.1
4
+ version: 0.31.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-10 00:00:00.000000000 Z
11
+ date: 2021-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: dry-inflector
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: pry-byebug
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +156,14 @@ dependencies:
142
156
  requirements:
143
157
  - - "~>"
144
158
  - !ruby/object:Gem::Version
145
- version: 1.8.0
159
+ version: 1.12.0
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
- version: 1.8.0
166
+ version: 1.12.0
153
167
  - !ruby/object:Gem::Dependency
154
168
  name: rubocop-rspec
155
169
  requirement: !ruby/object:Gem::Requirement
@@ -213,15 +227,8 @@ executables: []
213
227
  extensions: []
214
228
  extra_rdoc_files: []
215
229
  files:
216
- - ".circleci/config.yml"
217
- - ".github/ISSUE_TEMPLATE/bug_report.md"
218
- - ".github/stale.yml"
219
- - ".github/workflows/docs.yml"
220
- - ".github/workflows/reviewdog.yml"
221
- - ".gitignore"
222
230
  - ".rspec"
223
231
  - ".rubocop.yml"
224
- - ".travis.yml"
225
232
  - CHANGELOG.md
226
233
  - Dockerfile
227
234
  - Gemfile
@@ -230,7 +237,9 @@ files:
230
237
  - bin/console
231
238
  - bin/setup
232
239
  - docker-compose.yml
240
+ - docs/api_coverage.md
233
241
  - lib/puppeteer.rb
242
+ - lib/puppeteer/aria_query_handler.rb
234
243
  - lib/puppeteer/browser.rb
235
244
  - lib/puppeteer/browser_context.rb
236
245
  - lib/puppeteer/browser_fetcher.rb
@@ -239,6 +248,7 @@ files:
239
248
  - lib/puppeteer/concurrent_ruby_utils.rb
240
249
  - lib/puppeteer/connection.rb
241
250
  - lib/puppeteer/console_message.rb
251
+ - lib/puppeteer/custom_query_handler.rb
242
252
  - lib/puppeteer/debug_print.rb
243
253
  - lib/puppeteer/define_async_method.rb
244
254
  - lib/puppeteer/device.rb
@@ -279,6 +289,8 @@ files:
279
289
  - lib/puppeteer/page/pdf_options.rb
280
290
  - lib/puppeteer/page/screenshot_options.rb
281
291
  - lib/puppeteer/page/screenshot_task_queue.rb
292
+ - lib/puppeteer/puppeteer.rb
293
+ - lib/puppeteer/query_handler_manager.rb
282
294
  - lib/puppeteer/remote_object.rb
283
295
  - lib/puppeteer/request.rb
284
296
  - lib/puppeteer/response.rb