isomorfeus-puppetmaster 0.3.1 → 0.4.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: 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