isomorfeus-puppetmaster 0.3.1 → 0.4.0

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: 7123d1c27ed7357e7ac6fff577bb2dcd025bd7d622c2ba849d1232ab9d3086d2
4
- data.tar.gz: 4020f116455824c9ff42c4dca646ef69fb252ee7113986cb3926109cb874d381
3
+ metadata.gz: 2d453a12b9cc8f801e1e81fdda6e615d7d256253eec19b60c7c969c8bf08bd72
4
+ data.tar.gz: cbef053148c753b2b03c6b15e1bb815af51dfdc7821a10d422c803862ac57dcd
5
5
  SHA512:
6
- metadata.gz: c30ba060532b000fe950b6bbbd0dcde8a22f0957771368b0b4f8862839f4690102ea71b7ce8e74e1f6d1c5aaded08331cb4a387f14adde8dd3b0cd902b3484e7
7
- data.tar.gz: 9418b91dd65c552b9f26fa1c92fe2eeb2e75eb5f807125a6413e1812fd58545d3c6c554474f565b43700acb54f6478add020444b9ee202978758e782ce24ab74
6
+ metadata.gz: 42dd469ad600d330aa8904a31d638c8915bd6f49aba4990ff76c3bc92886d662f08d483128be27ffc6fdb4cf136a552b6f9a648c4609c1f4e50a0a61fa664ca8
7
+ data.tar.gz: 5dc01c153258ca15823abaa9ad535869cebc5aced2b5e5af6c1346b64b73a4666b68969320e06a589ce9b0064e8b1889657da5742e3f39699d2505798a7865e3
data/README.md CHANGED
@@ -19,9 +19,7 @@ In Gemfile:
19
19
 
20
20
  Also requires the following npm modules with recommended versions:
21
21
 
22
- - [puppeteer 2.0.0](https://github.com/GoogleChrome/puppeteer#readme) - for the chromium driver
23
- - [jsdom 15.2.1](https://github.com/jsdom/jsdom#readme) - for the jsdom driver
24
- - [canvas 2.6.0](https://github.com/Automattic/node-canvas) - for the jsdom driver (optional)
22
+ - [puppeteer 9.1.x](https://github.com/GoogleChrome/puppeteer#readme) - for the chromium driver
25
23
 
26
24
  Simply install them in your projects root. Puppetmaster also depends on isomorfeus-speednode, which will be installed automatically.
27
25
  Speednode will pickup the node modules then from the projects root node_modules directory.
@@ -30,15 +28,11 @@ Speednode will pickup the node modules then from the projects root node_modules
30
28
 
31
29
  Puppetmaster provides these drivers:
32
30
  - chromium - a real browser, headless, fast
33
- - chromium_wsl - as above but with options so it can execute within the Windows Linux WSL
34
31
  - chromium_debug - opens a chromium browser window with devtools enabled, useful for debugging tests
35
- - jsdom - provides a dom implementation in javascript, can execute javascript in the document, super fast, headless, has certain limitations,
36
- especially because its not rendering anything (no element bounding box, etc.)
37
- - jsdom_canvas - jsdom driver with additional support for 'canvas', requires certain libraries to be installed, see: [canvas at github](https://github.com/Automattic/node-canvas)
38
32
 
39
- Chromium is the default driver. Selecting another driver, for example jsdom:
33
+ Chromium is the default driver. Selecting another driver, for example firefox:
40
34
  ```ruby
41
- Isomorfeus::Puppetmaster.driver = :jsdom
35
+ Isomorfeus::Puppetmaster.driver = :chromium_debug
42
36
  ```
43
37
 
44
38
  Puppetmaster provides support for these rack servers:
@@ -36,9 +36,6 @@ require 'isomorfeus/puppetmaster/iframe'
36
36
  require 'isomorfeus/puppetmaster/driver/puppeteer_document'
37
37
  require 'isomorfeus/puppetmaster/driver/puppeteer_node'
38
38
  require 'isomorfeus/puppetmaster/driver/puppeteer'
39
- require 'isomorfeus/puppetmaster/driver/jsdom_document'
40
- require 'isomorfeus/puppetmaster/driver/jsdom_node'
41
- require 'isomorfeus/puppetmaster/driver/jsdom'
42
39
  require 'isomorfeus/puppetmaster/driver_registration'
43
40
  require 'isomorfeus/puppetmaster/server/executor_middleware'
44
41
  require 'isomorfeus/puppetmaster/server/middleware'
@@ -46,4 +43,4 @@ require 'isomorfeus/puppetmaster/server/checker'
46
43
  require 'isomorfeus/puppetmaster/server'
47
44
  require 'isomorfeus/puppetmaster/server_registration'
48
45
 
49
- require 'isomorfeus/puppetmaster/dsl'
46
+ require 'isomorfeus/puppetmaster/dsl'
@@ -15,7 +15,6 @@ module Isomorfeus
15
15
  clear_cookies
16
16
  clear_extra_headers
17
17
  clear_url_blacklist
18
- click
19
18
  close
20
19
  cookies
21
20
  console
@@ -34,15 +33,11 @@ module Isomorfeus
34
33
  html
35
34
  open_new_document
36
35
  remove_cookie
37
- render_base64
38
36
  reset_user_agent
39
37
  right_click
40
- save_pdf
41
- save_screenshot
42
38
  scroll_by
43
39
  scroll_to
44
40
  set_authentication_credentials
45
- set_cookie
46
41
  set_extra_headers
47
42
  set_url_blacklist
48
43
  set_user_agent
@@ -56,6 +51,14 @@ module Isomorfeus
56
51
  wait_for_xpath
57
52
  ]
58
53
 
54
+ document_forward_kwargs %i[
55
+ click
56
+ render_base64
57
+ save_pdf
58
+ save_screenshot
59
+ set_cookie
60
+ ]
61
+
59
62
  attr_reader :handle, :response
60
63
 
61
64
  def initialize(driver, handle, response)
@@ -79,21 +82,28 @@ module Isomorfeus
79
82
  end
80
83
  evaluate_script <<~JAVASCRIPT
81
84
  (function(){
82
- Opal.gvars.promise_resolved = false;
83
- Opal.await_ruby_exception = null;
84
- try {
85
- return #{compiled_ruby}
86
- } catch (e) {
87
- Opal.await_ruby_exception = e;
88
- Opal.gvars.promise_resolved = true;
85
+ fun = function() {
86
+ if (Opal) {
87
+ Opal.gvars.promise_resolved = false;
88
+ Opal.await_ruby_exception = null;
89
+ try {
90
+ return #{compiled_ruby}
91
+ } catch (e) {
92
+ Opal.await_ruby_exception = e;
93
+ Opal.gvars.promise_resolved = true;
94
+ }
95
+ } else {
96
+ setTimeout(fun, 100);
97
+ }
89
98
  }
99
+ fun();
90
100
  })()
91
101
  JAVASCRIPT
92
102
  have_result = false
93
103
  start = Time.now
94
104
  until have_result do
95
- break if (Time.now - start) > 30
96
- have_result = evaluate_script 'Opal.gvars.promise_resolved'
105
+ raise "await_ruby: execution timed out! Is Opal available?" if (Time.now - start) > 30
106
+ have_result = evaluate_script 'Opal ? Opal.gvars.promise_resolved : null'
97
107
  sleep 0.1 unless have_result
98
108
  end
99
109
  result, exception = execute_script <<~JAVASCRIPT
@@ -102,10 +112,12 @@ module Isomorfeus
102
112
  if (Opal.await_ruby_exception) {
103
113
  var e = Opal.await_ruby_exception;
104
114
  exception = { message: e.message, name: e.name, stack: e.stack }
105
- } else if (typeof Opal.gvars.promise_result.$to_n === 'function') {
106
- result = Opal.gvars.promise_result.$to_n();
107
- }
108
- else { result = Opal.gvars.promise_result };
115
+ } else if (Opal.gvars.promise_result['$respond_to?']('is_a?') && Opal.gvars.promise_result['$is_a?'](Opal.Exception)) {
116
+ let r = Opal.gvars.promise_result;
117
+ exception = { message: r.$message(), name: r.$class().$name(), stack: r.$backtrace() }
118
+ } else if (Opal.gvars.promise_result['$respond_to?']('to_n')) {
119
+ result = Opal.gvars.promise_result.$to_n()
120
+ } else { result = Opal.gvars.promise_result };
109
121
  delete Opal.gvars.promise_result;
110
122
  delete Opal.gvars.promise_resolved;
111
123
  return [result, exception];
@@ -166,11 +178,11 @@ module Isomorfeus
166
178
  alias_method :visit, :goto
167
179
 
168
180
  def has_content?(content, **options)
169
- body.has_content?(content, options)
181
+ body.has_content?(content, **options)
170
182
  end
171
183
 
172
184
  def has_css?(selector, **options)
173
- body.has_css?(selector, options)
185
+ body.has_css?(selector, **options)
174
186
  end
175
187
 
176
188
  def has_current_path?(other_path)
@@ -178,11 +190,11 @@ module Isomorfeus
178
190
  end
179
191
 
180
192
  def has_text?(text, **options)
181
- body.has_text?(text, options)
193
+ body.has_text?(text, **options)
182
194
  end
183
195
 
184
196
  def has_xpath?(query, **options)
185
- body.has_xpath?(query, options)
197
+ body.has_xpath?(query, **options)
186
198
  end
187
199
 
188
200
  def isomorphic(ruby_source = '', &block)
@@ -251,7 +263,7 @@ module Isomorfeus
251
263
  protected
252
264
 
253
265
  def compile_ruby_source(source_code)
254
- # TODO maybe use compile server
266
+ # TODO maybe use compile server, which can cache code
255
267
  Opal.compile(source_code, parse_comments: false)
256
268
  end
257
269
  end
@@ -36,10 +36,11 @@ module Isomorfeus
36
36
 
37
37
  def initialize(options = {})
38
38
  # https://pptr.dev/#?product=Puppeteer&version=v1.12.2&show=api-puppeteerlaunchoptions
39
- # init ExecJs context
39
+ # init ExecJS context
40
40
  @app = options.delete(:app)
41
41
  @options = options.dup
42
42
  @browser_type = @options.delete(:browser_type) { :chromium }
43
+ @options[:product] = "'#{@browser_type}'" unless @options.key?(:product)
43
44
  @max_width = @options.delete(:max_width) { VIEWPORT_MAX_WIDTH }
44
45
  @max_height = @options.delete(:max_height) { VIEWPORT_MAX_HEIGHT }
45
46
  @width = @options.delete(:width) { VIEWPORT_DEFAULT_WIDTH > @max_width ? @max_width : VIEWPORT_DEFAULT_WIDTH }
@@ -196,18 +197,16 @@ module Isomorfeus
196
197
  def await(script)
197
198
  @context.eval <<~JAVASCRIPT
198
199
  (async () => {
199
- try {
200
- LastExecutionFinished = false;
201
- LastResult = null;
202
- LastErr = null;
203
- #{script}
204
- LastExecutionFinished = true;
205
- } catch(err) {
206
- LastResult = null;
207
- LastErr = err;
208
- LastExecutionFinished = true;
209
- }
210
- })()
200
+ LastExecutionFinished = false;
201
+ LastResult = null;
202
+ LastErr = null;
203
+ #{script}
204
+ LastExecutionFinished = true;
205
+ })().catch(function(err) {
206
+ LastResult = null;
207
+ LastErr = err;
208
+ LastExecutionFinished = true;
209
+ })
211
210
  JAVASCRIPT
212
211
  await_result
213
212
  end
@@ -220,26 +219,29 @@ module Isomorfeus
220
219
  get_result
221
220
  end
222
221
 
223
- def chromium_require
222
+ def default_require
224
223
  <<~JAVASCRIPT
225
224
  const MasterPuppeteer = require('puppeteer');
226
225
  JAVASCRIPT
227
226
  end
228
227
 
229
- def determine_error(message)
230
- if message.include?('net::ERR_CERT_') || message.include?('SEC_ERROR_EXPIRED_CERTIFICATE')
231
- Isomorfeus::Puppetmaster::CertificateError.new(message)
232
- elsif message.include?('net::ERR_NAME_') || message.include?('NS_ERROR_UNKNOWN_HOST')
233
- Isomorfeus::Puppetmaster::DNSError.new(message)
234
- elsif message.include?('Unknown key: ')
235
- Isomorfeus::Puppetmaster::KeyError.new(message)
236
- elsif message.include?('Execution context was destroyed, most likely because of a navigation.')
237
- Isomorfeus::Puppetmaster::ExecutionContextError.new(message)
238
- elsif message.include?('Evaluation failed: DOMException:') || (message.include?('Evaluation failed:') && (message.include?('is not a valid selector') || message.include?('is not a legal expression')))
239
- Isomorfeus::Puppetmaster::DOMException.new(message)
240
- else
241
- Isomorfeus::Puppetmaster::JavaScriptError.new(message)
242
- end
228
+ def determine_error(error)
229
+ message = "#{error['name']}: #{error['message']}"
230
+ exception = if message.include?('net::ERR_CERT_') || message.include?('SEC_ERROR_EXPIRED_CERTIFICATE')
231
+ Isomorfeus::Puppetmaster::CertificateError.new(message)
232
+ elsif message.include?('net::ERR_NAME_') || message.include?('NS_ERROR_UNKNOWN_HOST')
233
+ Isomorfeus::Puppetmaster::DNSError.new(message)
234
+ elsif message.include?('Unknown key: ')
235
+ Isomorfeus::Puppetmaster::KeyError.new(message)
236
+ elsif message.include?('Execution context was destroyed, most likely because of a navigation.')
237
+ Isomorfeus::Puppetmaster::ExecutionContextError.new(message)
238
+ elsif message.include?('Evaluation failed: DOMException:') || (message.include?('Evaluation failed:') && (message.include?('is not a valid selector') || message.include?('is not a legal expression')))
239
+ Isomorfeus::Puppetmaster::DOMException.new(message)
240
+ else
241
+ Isomorfeus::Puppetmaster::JavaScriptError.new(message)
242
+ end
243
+ exception.set_backtrace(error['stack'])
244
+ exception
243
245
  end
244
246
 
245
247
  def execution_finished?
@@ -247,8 +249,8 @@ module Isomorfeus
247
249
  end
248
250
 
249
251
  def get_result
250
- res, err_msg = @context.eval 'GetLastResult()'
251
- raise determine_error(err_msg) if err_msg
252
+ res, error = @context.eval 'GetLastResult()'
253
+ raise determine_error(error) if error && !error.empty?
252
254
  res
253
255
  end
254
256
 
@@ -276,8 +278,8 @@ module Isomorfeus
276
278
  def puppeteer_launch
277
279
  # todo target_handle, puppeteer save path
278
280
  puppeteer_require = case @browser_type
279
- when :chrome then chromium_require
280
- when :chromium then chromium_require
281
+ when :chrome then default_require
282
+ when :chromium then default_require
281
283
  else
282
284
  raise "Browser type #{@browser_type} not supported!"
283
285
  end
@@ -307,11 +309,12 @@ module Isomorfeus
307
309
  LastRes = null;
308
310
  LastExecutionFinished = false;
309
311
 
310
- if (err) { return [null, err.toString() + "\\n" + err.stack]; }
312
+ if (err) { return [null, {name: err.name, message: err.message, stack: err.stack}]; }
311
313
  else { return [res, null]; }
312
314
 
313
315
  } else {
314
- return [null, (new Error('Last command did not yet finish execution!')).message];
316
+ var new_err = new Error('Last command did not yet finish execution!');
317
+ return [null, {name: new_err.name, message: new_err.message, stack: new_err.stack}];
315
318
  }
316
319
  };
317
320
 
@@ -374,7 +377,10 @@ module Isomorfeus
374
377
  LastErr = err;
375
378
  LastExecutionFinished = true;
376
379
  }
377
- })();
380
+ })().catch(function(err) {
381
+ LastErr = err;
382
+ LastExecutionFinished = true;
383
+ });
378
384
  JAVASCRIPT
379
385
  end
380
386
 
@@ -310,17 +310,39 @@ module Isomorfeus
310
310
 
311
311
  def document_evaluate_script(document, script, *args)
312
312
  await <<~JAVASCRIPT
313
- LastResult = await AllPageHandles[#{document.handle}].evaluate((arguments) => {
314
- return #{script.strip}
313
+ var result = await AllPageHandles[#{document.handle}].evaluate((arguments) => {
314
+ try {
315
+ var res = (function(arguments) {
316
+ return #{script.strip};
317
+ })(arguments);
318
+ return [res, null];
319
+ } catch (err) {
320
+ return [null, {name: err.name, message: err.message, stack: err.stack}];
321
+ }
315
322
  }, #{args});
323
+ if (result) {
324
+ LastResult = result[0];
325
+ LastErr = result[1];
326
+ }
316
327
  JAVASCRIPT
317
328
  end
318
329
 
319
330
  def document_execute_script(document, script, *args)
320
331
  await <<~JAVASCRIPT
321
- LastResult = await AllPageHandles[#{document.handle}].evaluate((arguments) => {
322
- #{script.strip}
332
+ var result = await AllPageHandles[#{document.handle}].evaluate((arguments) => {
333
+ try {
334
+ var res = (function(arguments) {
335
+ #{script.strip};
336
+ })(arguments);
337
+ return [res, null];
338
+ } catch (err) {
339
+ return [null, {name: err.name, message: err.message, stack: err.stack}];
340
+ }
323
341
  }, #{args});
342
+ if (result) {
343
+ LastResult = result[0];
344
+ LastErr = result[1];
345
+ }
324
346
  JAVASCRIPT
325
347
  end
326
348
 
@@ -454,7 +476,7 @@ module Isomorfeus
454
476
  }
455
477
  JAVASCRIPT
456
478
  con_messages = messages.map {|m| Isomorfeus::Puppetmaster::ConsoleMessage.new(m)}
457
- con_messages.each { |m| raise determine_error(m.text) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
479
+ con_messages.each { |m| raise determine_error({ 'message' => m.text }) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
458
480
  if response_hash
459
481
  response = Isomorfeus::Puppetmaster::Response.new(response_hash)
460
482
  document.instance_variable_set(:@response, response)
@@ -491,7 +513,7 @@ module Isomorfeus
491
513
  }
492
514
  JAVASCRIPT
493
515
  con_messages = messages.map {|m| Isomorfeus::Puppetmaster::ConsoleMessage.new(m)}
494
- con_messages.each { |m| raise determine_error(m.text) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
516
+ con_messages.each { |m| raise determine_error({ 'message' => m.text }) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
495
517
  if response_hash
496
518
  response = Isomorfeus::Puppetmaster::Response.new(response_hash)
497
519
  document.instance_variable_set(:@response, response)
@@ -532,7 +554,7 @@ module Isomorfeus
532
554
  }
533
555
  JAVASCRIPT
534
556
  con_messages = messages.map {|m| Isomorfeus::Puppetmaster::ConsoleMessage.new(m)}
535
- con_messages.each { |m| raise determine_error(m.text) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
557
+ con_messages.each { |m| raise determine_error({ 'message' => m.text }) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
536
558
  if response_hash
537
559
  response = Isomorfeus::Puppetmaster::Response.new(response_hash)
538
560
  document.instance_variable_set(:@response, response)
@@ -610,7 +632,7 @@ module Isomorfeus
610
632
  LastResult = [page_handle, result_response, ConsoleMessages[page_handle]];
611
633
  JAVASCRIPT
612
634
  con_messages = messages.map {|m| Isomorfeus::Puppetmaster::ConsoleMessage.new(m)}
613
- con_messages.each { |m| raise determine_error(m.text) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
635
+ con_messages.each { |m| raise determine_error({ 'message' => m.text }) if m.level == 'error' && !m.text.start_with?('Failed to load resource:') }
614
636
  Isomorfeus::Puppetmaster::Document.new(self, handle, Isomorfeus::Puppetmaster::Response.new(response_hash))
615
637
  end
616
638
 
@@ -1,19 +1,9 @@
1
+ require 'tty-which'
2
+
1
3
  Isomorfeus::Puppetmaster.register_driver(:chromium) do |app|
2
4
  Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :chromium, headless: true, app: app)
3
5
  end
4
6
 
5
- Isomorfeus::Puppetmaster.register_driver(:chromium_wsl) do |app|
6
- Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :chromium, headless: true, app: app, args: ['--no-sandbox'])
7
- end
8
-
9
7
  Isomorfeus::Puppetmaster.register_driver(:chromium_debug) do |app|
10
8
  Isomorfeus::Puppetmaster::Driver::Puppeteer.new(browser_type: :chromium, headless: false, devtools: true, app: app)
11
9
  end
12
-
13
- Isomorfeus::Puppetmaster.register_driver(:jsdom) do |app|
14
- Isomorfeus::Puppetmaster::Driver::Jsdom.new(app: app)
15
- end
16
-
17
- Isomorfeus::Puppetmaster.register_driver(:jsdom_canvas) do |app|
18
- Isomorfeus::Puppetmaster::Driver::Jsdom.new(app: app, canvas: true)
19
- end
@@ -10,6 +10,14 @@ module Isomorfeus
10
10
  end
11
11
  end
12
12
 
13
+ base.define_singleton_method(:document_forward_kwargs) do |methods|
14
+ methods.each do |method|
15
+ define_method(method) do |*args, **kwargs, &block|
16
+ @driver.send("document_#{method}", self, *args, **kwargs, &block)
17
+ end
18
+ end
19
+ end
20
+
13
21
  base.define_singleton_method(:frame_forward) do |methods|
14
22
  methods.each do |method|
15
23
  define_method(method) do |*args|
@@ -66,18 +66,22 @@ module Isomorfeus
66
66
  ruby_source = Isomorfeus::Puppetmaster.block_source_code(&block) if block_given?
67
67
  request_hash = { 'key' => @request_key, 'code' => ruby_source }
68
68
  response = if using_ssl?
69
- http = Net::HTTP.start(@host, @port, { use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE })
70
- http.post('/__executor__', Oj.dump(request_hash, {}))
71
- else
72
- http = Net::HTTP.start(@host, @port)
73
- http.post('/__executor__', Oj.dump(request_hash, {}))
74
- end
69
+ http = Net::HTTP.start(@host, @port, { use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE })
70
+ http.post('/__executor__', Oj.dump(request_hash, mode: :strict))
71
+ else
72
+ http = Net::HTTP.start(@host, @port)
73
+ http.post('/__executor__', Oj.dump(request_hash, mode: :strict))
74
+ end
75
75
  if response.code == '200'
76
- result_hash = Oj.load(response.body, {})
77
- raise result_hash['error'] if result_hash.has_key?('error')
76
+ result_hash = Oj.load(response.body)
77
+ if result_hash.has_key?('error')
78
+ error = RuntimeError.new(result_hash['error'])
79
+ error.set_backtrace(result_hash['backtrace'])
80
+ raise error
81
+ end
78
82
  result_hash['result']
79
83
  else
80
- raise 'A error occured.'
84
+ raise 'A error occurred.'
81
85
  end
82
86
  end
83
87