playwright-ruby-client 1.39.1 → 1.41.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/documentation/docs/api/api_request_context.md +1 -2
  3. data/documentation/docs/api/browser.md +1 -1
  4. data/documentation/docs/api/browser_context.md +10 -1
  5. data/documentation/docs/api/browser_type.md +1 -0
  6. data/documentation/docs/api/download.md +1 -2
  7. data/documentation/docs/api/element_handle.md +5 -1
  8. data/documentation/docs/api/frame.md +4 -1
  9. data/documentation/docs/api/locator.md +5 -1
  10. data/documentation/docs/api/locator_assertions.md +12 -2
  11. data/documentation/docs/api/page.md +16 -3
  12. data/documentation/docs/api/tracing.md +2 -2
  13. data/documentation/docs/article/guides/rails_integration.md +80 -51
  14. data/documentation/docs/article/guides/rspec_integration.md +59 -0
  15. data/documentation/docs/include/api_coverage.md +2 -0
  16. data/documentation/docusaurus.config.js +1 -1
  17. data/documentation/package.json +7 -7
  18. data/documentation/yarn.lock +4641 -5023
  19. data/lib/playwright/api_response_impl.rb +2 -2
  20. data/lib/playwright/channel_owners/api_request_context.rb +12 -3
  21. data/lib/playwright/channel_owners/browser.rb +11 -7
  22. data/lib/playwright/channel_owners/browser_context.rb +40 -15
  23. data/lib/playwright/channel_owners/element_handle.rb +2 -0
  24. data/lib/playwright/channel_owners/frame.rb +38 -14
  25. data/lib/playwright/channel_owners/page.rb +36 -16
  26. data/lib/playwright/channel_owners/web_socket.rb +8 -13
  27. data/lib/playwright/connection.rb +18 -2
  28. data/lib/playwright/errors.rb +20 -2
  29. data/lib/playwright/input_files.rb +4 -4
  30. data/lib/playwright/locator_assertions_impl.rb +15 -15
  31. data/lib/playwright/locator_impl.rb +2 -0
  32. data/lib/playwright/test.rb +2 -2
  33. data/lib/playwright/utils.rb +3 -10
  34. data/lib/playwright/version.rb +2 -2
  35. data/lib/playwright/waiter.rb +146 -0
  36. data/lib/playwright.rb +1 -1
  37. data/lib/playwright_api/api_request_context.rb +1 -2
  38. data/lib/playwright_api/browser.rb +2 -2
  39. data/lib/playwright_api/browser_context.rb +9 -3
  40. data/lib/playwright_api/browser_type.rb +2 -1
  41. data/lib/playwright_api/download.rb +1 -2
  42. data/lib/playwright_api/element_handle.rb +6 -2
  43. data/lib/playwright_api/frame.rb +4 -1
  44. data/lib/playwright_api/locator.rb +6 -2
  45. data/lib/playwright_api/locator_assertions.rb +14 -4
  46. data/lib/playwright_api/page.rb +20 -10
  47. data/lib/playwright_api/request.rb +4 -4
  48. data/lib/playwright_api/tracing.rb +2 -2
  49. data/lib/playwright_api/worker.rb +4 -4
  50. data/sig/playwright.rbs +12 -10
  51. metadata +7 -6
  52. data/lib/playwright/wait_helper.rb +0 -73
@@ -1,6 +1,6 @@
1
1
  module Playwright
2
2
  define_api_implementation :APIResponseImpl do
3
- include Utils::Errors::SafeCloseError
3
+ include Utils::Errors::TargetClosedErrorMethods
4
4
 
5
5
  # @params context [APIRequestContext]
6
6
  # @params initializer [Hash]
@@ -50,7 +50,7 @@ module Playwright
50
50
  raise AlreadyDisposedError.new unless binary
51
51
  Base64.strict_decode64(binary)
52
52
  rescue => err
53
- if safe_close_error?(err)
53
+ if target_closed_error?(err)
54
54
  raise AlreadyDisposedError.new
55
55
  else
56
56
  raise
@@ -115,12 +115,12 @@ module Playwright
115
115
  case data
116
116
  when String
117
117
  if headers_obj&.any? { |key, value| key.downcase == 'content-type' && value == 'application/json' }
118
- json_data = data
118
+ json_data = json_parsable?(data) ? data : data.to_json
119
119
  else
120
120
  post_data_buffer = data
121
121
  end
122
122
  when Hash, Array, Numeric, true, false
123
- json_data = data
123
+ json_data = data.to_json
124
124
  else
125
125
  raise ArgumentError.new("Unsupported 'data' type: #{data.class}")
126
126
  end
@@ -142,7 +142,6 @@ module Playwright
142
142
  if post_data_buffer
143
143
  fetch_params[:postData] = Base64.strict_encode64(post_data_buffer)
144
144
  end
145
-
146
145
  fetch_params[:jsonData] = json_data
147
146
  fetch_params[:formData] = form_data
148
147
  fetch_params[:multipartData] = multipart_data
@@ -174,5 +173,15 @@ module Playwright
174
173
  { name: key, value: value.to_s }
175
174
  end
176
175
  end
176
+
177
+ private def json_parsable?(data)
178
+ return false unless data.is_a?(String)
179
+ begin
180
+ JSON.parse(data)
181
+ true
182
+ rescue JSON::ParserError
183
+ false
184
+ end
185
+ end
177
186
  end
178
187
  end
@@ -3,17 +3,21 @@ require 'fileutils'
3
3
  module Playwright
4
4
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser.py
5
5
  define_channel_owner :Browser do
6
- include Utils::Errors::SafeCloseError
6
+ include Utils::Errors::TargetClosedErrorMethods
7
7
  include Utils::PrepareBrowserContextOptions
8
8
 
9
9
  private def after_initialize
10
10
  @browser_type = @parent
11
11
  @connected = true
12
- @closed_or_closing = false
13
12
  @should_close_connection_on_close = false
14
13
 
15
14
  @contexts = Set.new
16
15
  @channel.on('close', method(:on_close))
16
+ @close_reason = nil
17
+ end
18
+
19
+ private def close_reason
20
+ @close_reason
17
21
  end
18
22
 
19
23
  def contexts
@@ -63,16 +67,16 @@ module Playwright
63
67
  @browser_type = browser_type
64
68
  end
65
69
 
66
- def close
67
- return if @closed_or_closing
68
- @closed_or_closing = true
69
- @channel.send_message_to_server('close')
70
+ def close(reason: nil)
71
+ @close_reason = reason
70
72
  if @should_close_connection_on_close
71
73
  @connection.stop
74
+ else
75
+ @channel.send_message_to_server('close', { reason: reason }.compact)
72
76
  end
73
77
  nil
74
78
  rescue => err
75
- raise unless safe_close_error?(err)
79
+ raise unless target_closed_error?(err)
76
80
  end
77
81
 
78
82
  def version
@@ -1,7 +1,6 @@
1
1
  module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser_context.py
3
3
  define_channel_owner :BrowserContext do
4
- include Utils::Errors::SafeCloseError
5
4
  include Utils::PrepareBrowserContextOptions
6
5
 
7
6
  attr_accessor :browser
@@ -68,6 +67,9 @@ module Playwright
68
67
  ChannelOwners::Page.from_nullable(params['page']),
69
68
  )
70
69
  })
70
+
71
+ @closed_promise = Concurrent::Promises.resolvable_future
72
+ @close_reason = nil
71
73
  set_event_to_subscription_mapping({
72
74
  Events::BrowserContext::Console => 'console',
73
75
  Events::BrowserContext::Dialog => 'dialog',
@@ -77,7 +79,7 @@ module Playwright
77
79
  Events::BrowserContext::RequestFailed => "requestFailed",
78
80
  })
79
81
 
80
- @closed_promise = Concurrent::Promises.resolvable_future
82
+ @close_was_called = false
81
83
  end
82
84
 
83
85
  private def update_options(context_options:, browser_options:)
@@ -333,6 +335,11 @@ module Playwright
333
335
  update_interception_patterns
334
336
  end
335
337
 
338
+ def unroute_all(behavior: nil)
339
+ @routes.clear
340
+ update_interception_patterns
341
+ end
342
+
336
343
  def unroute(url, handler: nil)
337
344
  @routes.reject! do |handler_entry|
338
345
  handler_entry.same_value?(url: url, handler: handler)
@@ -382,13 +389,17 @@ module Playwright
382
389
  end
383
390
 
384
391
  def expect_event(event, predicate: nil, timeout: nil, &block)
385
- wait_helper = WaitHelper.new
386
- wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
387
- wait_helper.wait_for_event(self, event, predicate: predicate)
392
+ waiter = Waiter.new(self, wait_name: "browser_context.expect_event(#{event})")
393
+ timeout_value = timeout || @timeout_settings.timeout
394
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout}ms exceeded while waiting for event \"#{event}\"")
395
+ unless event == Events::BrowserContext::Close
396
+ waiter.reject_on_event(self, Events::BrowserContext::Close, TargetClosedError.new)
397
+ end
398
+ waiter.wait_for_event(self, event, predicate: predicate)
388
399
 
389
400
  block&.call
390
401
 
391
- wait_helper.promise.value!
402
+ waiter.result.value!
392
403
  end
393
404
 
394
405
  private def on_close
@@ -397,7 +408,18 @@ module Playwright
397
408
  @closed_promise.fulfill(true)
398
409
  end
399
410
 
400
- def close
411
+ def close(reason: nil)
412
+ return if @close_was_called
413
+ @close_was_called = true
414
+ @close_reason = reason
415
+
416
+ inner_close
417
+ @channel.send_message_to_server('close', { reason: reason }.compact)
418
+ @closed_promise.value!
419
+ nil
420
+ end
421
+
422
+ private def inner_close
401
423
  @har_recorders.each do |har_id, params|
402
424
  har = ChannelOwners::Artifact.from(@channel.send_message_to_server('harExport', harId: har_id))
403
425
  # Server side will compress artifact if content is attach or if file is .zip.
@@ -413,11 +435,6 @@ module Playwright
413
435
 
414
436
  har.delete
415
437
  end
416
- @channel.send_message_to_server('close')
417
- @closed_promise.value!
418
- nil
419
- rescue => err
420
- raise unless safe_close_error?(err)
421
438
  end
422
439
 
423
440
  # REMARK: enable_debug_console is playwright-ruby-client specific method.
@@ -459,6 +476,10 @@ module Playwright
459
476
  end
460
477
  end
461
478
 
479
+ private def effective_close_reason
480
+ @close_reason || @browser&.send(:close_reason)
481
+ end
482
+
462
483
  def expect_console_message(predicate: nil, timeout: nil, &block)
463
484
  params = {
464
485
  predicate: predicate,
@@ -502,10 +523,14 @@ module Playwright
502
523
  end
503
524
 
504
525
  # called from InputFiles
505
- # @param name [string]
526
+ # @param filepath [string]
506
527
  # @return [WritableStream]
507
- private def create_temp_file(name)
508
- result = @channel.send_message_to_server('createTempFile', name: name)
528
+ private def create_temp_file(filepath)
529
+ result = @channel.send_message_to_server(
530
+ 'createTempFile',
531
+ name: File.basename(filepath),
532
+ lastModifiedMs: File.mtime(filepath).to_i * 1000,
533
+ )
509
534
  ChannelOwners::WritableStream.from(result)
510
535
  end
511
536
  end
@@ -301,6 +301,7 @@ module Playwright
301
301
  path: nil,
302
302
  quality: nil,
303
303
  scale: nil,
304
+ style: nil,
304
305
  timeout: nil,
305
306
  type: nil)
306
307
 
@@ -312,6 +313,7 @@ module Playwright
312
313
  path: path,
313
314
  quality: quality,
314
315
  scale: scale,
316
+ style: style,
315
317
  timeout: timeout,
316
318
  type: type,
317
319
  }.compact
@@ -71,12 +71,24 @@ module Playwright
71
71
  ChannelOwners::Response.from_nullable(resp)
72
72
  end
73
73
 
74
- private def setup_navigation_wait_helper(timeout:)
75
- WaitHelper.new.tap do |helper|
76
- helper.reject_on_event(@page, Events::Page::Close, AlreadyClosedError.new)
77
- helper.reject_on_event(@page, Events::Page::Crash, CrashedError.new)
78
- helper.reject_on_event(@page, Events::Page::FrameDetached, FrameAlreadyDetachedError.new)
79
- helper.reject_on_timeout(timeout, "Timeout #{timeout}ms exceeded.")
74
+ class CrashedError < StandardError
75
+ def initialize
76
+ super('Navigation failed because page crashed!')
77
+ end
78
+ end
79
+
80
+ class FrameAlreadyDetachedError < StandardError
81
+ def initialize
82
+ super('Navigating frame was detached!')
83
+ end
84
+ end
85
+
86
+ private def setup_navigation_waiter(wait_name:, timeout_value:)
87
+ Waiter.new(page, wait_name: "frame.#{wait_name}").tap do |waiter|
88
+ waiter.reject_on_event(@page, Events::Page::Close, -> { @page.send(:close_error_with_reason) })
89
+ waiter.reject_on_event(@page, Events::Page::Crash, CrashedError.new)
90
+ waiter.reject_on_event(@page, Events::Page::FrameDetached, FrameAlreadyDetachedError.new, predicate: -> (frame) { frame == self })
91
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout_value}ms exceeded.")
80
92
  end
81
93
  end
82
94
 
@@ -85,21 +97,30 @@ module Playwright
85
97
  option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
86
98
  time_start = Time.now
87
99
 
88
- wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
100
+ waiter = setup_navigation_waiter(wait_name: :expect_navigation, timeout_value: option_timeout)
89
101
 
102
+ to_url = url ? " to \"#{url}\"" : ''
103
+ waiter.log("waiting for navigation#{to_url} until '#{option_wait_until}'")
90
104
  predicate =
91
105
  if url
92
106
  matcher = UrlMatcher.new(url, base_url: @page.context.send(:base_url))
93
- ->(event) { event['error'] || matcher.match?(event['url']) }
107
+ ->(event) {
108
+ if event['error']
109
+ true
110
+ else
111
+ waiter.log(" navigated to \"#{event['url']}\"")
112
+ matcher.match?(event['url'])
113
+ end
114
+ }
94
115
  else
95
116
  ->(_) { true }
96
117
  end
97
118
 
98
- wait_helper.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
119
+ waiter.wait_for_event(@event_emitter, 'navigated', predicate: predicate)
99
120
 
100
121
  block&.call
101
122
 
102
- event = wait_helper.promise.value!
123
+ event = waiter.result.value!
103
124
  if event['error']
104
125
  raise event['error']
105
126
  end
@@ -135,11 +156,14 @@ module Playwright
135
156
  return
136
157
  end
137
158
 
138
- wait_helper = setup_navigation_wait_helper(timeout: option_timeout)
159
+ waiter = setup_navigation_waiter(wait_name: :wait_for_load_state, timeout_value: option_timeout)
139
160
 
140
- predicate = ->(state) { state == option_state }
141
- wait_helper.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
142
- wait_helper.promise.value!
161
+ predicate = ->(actual_state) {
162
+ waiter.log("\"#{actual_state}\" event fired")
163
+ actual_state == option_state
164
+ }
165
+ waiter.wait_for_event(@event_emitter, 'loadstate', predicate: predicate)
166
+ waiter.result.value!
143
167
 
144
168
  nil
145
169
  end
@@ -4,7 +4,7 @@ require_relative '../locator_utils'
4
4
  module Playwright
5
5
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_page.py
6
6
  define_channel_owner :Page do
7
- include Utils::Errors::SafeCloseError
7
+ include Utils::Errors::TargetClosedErrorMethods
8
8
  include LocatorUtils
9
9
  attr_writer :owned_context
10
10
 
@@ -33,10 +33,12 @@ module Playwright
33
33
  @frames = Set.new
34
34
  @frames << @main_frame
35
35
  @opener = ChannelOwners::Page.from_nullable(@initializer['opener'])
36
+ @close_reason = nil
36
37
 
37
38
  @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
39
+ @closed_or_crashed_promise = Concurrent::Promises.resolvable_future
38
40
  @channel.once('close', ->(_) { on_close })
39
- @channel.on('crash', ->(_) { emit(Events::Page::Crash) })
41
+ @channel.on('crash', ->(_) { on_crash })
40
42
  @channel.on('download', method(:on_download))
41
43
  @channel.on('fileChooser', ->(params) {
42
44
  chooser = FileChooserImpl.new(
@@ -147,9 +149,19 @@ module Playwright
147
149
  @closed = true
148
150
  @browser_context.send(:remove_page, self)
149
151
  @browser_context.send(:remove_background_page, self)
152
+ if @closed_or_crashed_promise.pending?
153
+ @closed_or_crashed_promise.fulfill(close_error_with_reason)
154
+ end
150
155
  emit(Events::Page::Close)
151
156
  end
152
157
 
158
+ private def on_crash
159
+ if @closed_or_crashed_promise.pending?
160
+ @closed_or_crashed_promise.fulfill(TargetClosedError.new)
161
+ end
162
+ emit(Events::Page::Crash)
163
+ end
164
+
153
165
  private def on_download(params)
154
166
  artifact = ChannelOwners::Artifact.from(params['artifact'])
155
167
  download = DownloadImpl.new(
@@ -402,6 +414,11 @@ module Playwright
402
414
  update_interception_patterns
403
415
  end
404
416
 
417
+ def unroute_all(behavior: nil)
418
+ @routes.clear
419
+ update_interception_patterns
420
+ end
421
+
405
422
  def unroute(url, handler: nil)
406
423
  @routes.reject! do |handler_entry|
407
424
  handler_entry.same_value?(url: url, handler: handler)
@@ -445,6 +462,7 @@ module Playwright
445
462
  path: nil,
446
463
  quality: nil,
447
464
  scale: nil,
465
+ style: nil,
448
466
  timeout: nil,
449
467
  type: nil)
450
468
 
@@ -458,6 +476,7 @@ module Playwright
458
476
  animations: animations,
459
477
  caret: caret,
460
478
  scale: scale,
479
+ style: style,
461
480
  timeout: timeout,
462
481
  }.compact
463
482
  if mask.is_a?(Enumerable)
@@ -479,7 +498,8 @@ module Playwright
479
498
  @main_frame.title
480
499
  end
481
500
 
482
- def close(runBeforeUnload: nil)
501
+ def close(runBeforeUnload: nil, reason: nil)
502
+ @close_reason = reason
483
503
  if @owned_context
484
504
  @owned_context.close
485
505
  else
@@ -488,7 +508,7 @@ module Playwright
488
508
  end
489
509
  nil
490
510
  rescue => err
491
- raise if !safe_close_error?(err) || !runBeforeUnload
511
+ raise if !target_closed_error?(err) || !runBeforeUnload
492
512
  end
493
513
 
494
514
  def closed?
@@ -878,34 +898,34 @@ module Playwright
878
898
  end
879
899
  end
880
900
 
881
- class AlreadyClosedError < StandardError
882
- def initialize
883
- super('Page closed')
884
- end
885
- end
886
-
887
901
  class FrameAlreadyDetachedError < StandardError
888
902
  def initialize
889
903
  super('Navigating frame was detached!')
890
904
  end
891
905
  end
892
906
 
907
+ private def close_error_with_reason
908
+ reason = @close_reason || @browser_context.send(:effective_close_reason)
909
+ TargetClosedError.new(message: reason)
910
+ end
911
+
893
912
  def expect_event(event, predicate: nil, timeout: nil, &block)
894
- wait_helper = WaitHelper.new
895
- wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
913
+ waiter = Waiter.new(self, wait_name: "Page.expect_event(#{event})")
914
+ timeout_value = timeout || @timeout_settings.timeout
915
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout_value}ms exceeded while waiting for event \"#{event}\"")
896
916
 
897
917
  unless event == Events::Page::Crash
898
- wait_helper.reject_on_event(self, Events::Page::Crash, CrashedError.new)
918
+ waiter.reject_on_event(self, Events::Page::Crash, CrashedError.new)
899
919
  end
900
920
 
901
921
  unless event == Events::Page::Close
902
- wait_helper.reject_on_event(self, Events::Page::Close, AlreadyClosedError.new)
922
+ waiter.reject_on_event(self, Events::Page::Close, -> { close_error_with_reason })
903
923
  end
904
924
 
905
- wait_helper.wait_for_event(self, event, predicate: predicate)
925
+ waiter.wait_for_event(self, event, predicate: predicate)
906
926
  block&.call
907
927
 
908
- wait_helper.promise.value!
928
+ waiter.result.value!
909
929
  end
910
930
 
911
931
  def expect_console_message(predicate: nil, timeout: nil, &block)
@@ -33,29 +33,24 @@ module Playwright
33
33
  end
34
34
  end
35
35
 
36
- class PageClosedError < StandardError
37
- def initialize
38
- super('Page closed')
39
- end
40
- end
41
-
42
36
  def expect_event(event, predicate: nil, timeout: nil, &block)
43
- wait_helper = WaitHelper.new
44
- wait_helper.reject_on_timeout(timeout || @parent.send(:timeout_settings).timeout, "Timeout while waiting for event \"#{event}\"")
37
+ waiter = Waiter.new(self, wait_name: "WebSocket.expect_event(#{event})")
38
+ timeout_value = timeout || @parent.send(:timeout_settings).timeout
39
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout_value}ms exceeded while waiting for event \"#{event}\"")
45
40
 
46
41
  unless event == Events::WebSocket::Close
47
- wait_helper.reject_on_event(self, Events::WebSocket::Close, SocketClosedError.new)
42
+ waiter.reject_on_event(self, Events::WebSocket::Close, SocketClosedError.new)
48
43
  end
49
44
 
50
45
  unless event == Events::WebSocket::Error
51
- wait_helper.reject_on_event(self, Events::WebSocket::Error, SocketError.new)
46
+ waiter.reject_on_event(self, Events::WebSocket::Error, SocketError.new)
52
47
  end
53
48
 
54
- wait_helper.reject_on_event(@parent, 'close', PageClosedError.new)
55
- wait_helper.wait_for_event(self, event, predicate: predicate)
49
+ waiter.reject_on_event(@parent, 'close', -> { @parent.send(:close_error_with_reason) })
50
+ waiter.wait_for_event(self, event, predicate: predicate)
56
51
  block&.call
57
52
 
58
- wait_helper.promise.value!
53
+ waiter.result.value!
59
54
  end
60
55
  alias_method :wait_for_event, :expect_event
61
56
 
@@ -23,6 +23,7 @@ module Playwright
23
23
  @root_object = RootChannelOwner.new(self)
24
24
  @remote = false
25
25
  @tracing_count = 0
26
+ @closed_error = nil
26
27
  end
27
28
 
28
29
  attr_reader :local_utils
@@ -41,6 +42,15 @@ module Playwright
41
42
 
42
43
  def stop
43
44
  @transport.stop
45
+ cleanup
46
+ end
47
+
48
+ def cleanup(cause: nil)
49
+ @closed_error = cause || TargetClosedError.new
50
+ @callbacks.each_value do |callback|
51
+ callback.reject(@closed_error)
52
+ end
53
+ @callbacks.clear
44
54
  end
45
55
 
46
56
  def initialize_playwright
@@ -60,6 +70,8 @@ module Playwright
60
70
  end
61
71
 
62
72
  def async_send_message_to_server(guid, method, params, metadata: nil)
73
+ return if @closed_error
74
+
63
75
  callback = Concurrent::Promises.resolvable_future
64
76
 
65
77
  with_generated_id do |id|
@@ -130,6 +142,8 @@ module Playwright
130
142
  end
131
143
 
132
144
  def dispatch(msg)
145
+ return if @closed_error
146
+
133
147
  id = msg['id']
134
148
  if id
135
149
  callback = @callbacks.delete(id)
@@ -139,8 +153,10 @@ module Playwright
139
153
  end
140
154
 
141
155
  error = msg['error']
142
- if error
143
- callback.reject(::Playwright::Error.parse(error['error']))
156
+ if error && !msg['result']
157
+ parsed_error = ::Playwright::Error.parse(error['error'])
158
+ parsed_error.log = msg['log']
159
+ callback.reject(parsed_error)
144
160
  else
145
161
  result = replace_guids_with_channels(msg['result'])
146
162
  callback.fulfill(result)
@@ -7,6 +7,11 @@ module Playwright
7
7
  message: error_payload['message'],
8
8
  stack: error_payload['stack'],
9
9
  )
10
+ elsif error_payload['name'] == 'TargetClosedError'
11
+ TargetClosedError.new(
12
+ message: error_payload['message'],
13
+ stack: error_payload['stack'],
14
+ )
10
15
  else
11
16
  new(
12
17
  name: error_payload['name'],
@@ -19,14 +24,20 @@ module Playwright
19
24
  # @param name [String]
20
25
  # @param message [String]
21
26
  # @param stack [Array<String>]
22
- def initialize(name:, message:, stack:)
23
- super("#{name}: #{message}")
27
+ def initialize(message:, name: nil, stack: nil)
28
+ super(message)
24
29
  @name = name
25
30
  @message = message
26
31
  @stack = stack
27
32
  end
28
33
 
29
34
  attr_reader :name, :message, :stack
35
+
36
+ def log=(log)
37
+ return unless log
38
+ format_call_log = log.join("\n - ")
39
+ @message = "#{@message}\nCall log:\n#{format_call_log}\n"
40
+ end
30
41
  end
31
42
 
32
43
  class DriverCrashedError < StandardError
@@ -41,6 +52,13 @@ module Playwright
41
52
  end
42
53
  end
43
54
 
55
+ class TargetClosedError < Error
56
+ def initialize(message: nil, stack: [])
57
+ _message = message || 'Target page, context or browser has been closed'
58
+ super(name: 'TargetClosedError', message: _message, stack: stack)
59
+ end
60
+ end
61
+
44
62
  class WebError
45
63
  def initialize(error, page)
46
64
  @error = error
@@ -15,7 +15,7 @@ module Playwright
15
15
 
16
16
  def as_method_and_params
17
17
  if has_large_file?
18
- ['setInputFilePaths', params_for_set_input_file_paths]
18
+ ['setInputFiles', params_for_set_input_file_paths]
19
19
  else
20
20
  ['setInputFiles', params_for_set_input_files]
21
21
  end
@@ -40,7 +40,7 @@ module Playwright
40
40
  writable_streams = @files.map do |file|
41
41
  case file
42
42
  when String
43
- writable_stream = @context.send(:create_temp_file, File.basename(file))
43
+ writable_stream = @context.send(:create_temp_file, file)
44
44
 
45
45
  File.open(file, 'rb') do |file|
46
46
  writable_stream.write(file)
@@ -48,7 +48,7 @@ module Playwright
48
48
 
49
49
  writable_stream.channel
50
50
  when File
51
- writable_stream = @context.send(:create_temp_file, File.basename(file.path))
51
+ writable_stream = @context.send(:create_temp_file, file.path)
52
52
  writable_stream.write(file)
53
53
 
54
54
  writable_stream.channel
@@ -78,7 +78,7 @@ module Playwright
78
78
  end
79
79
  end
80
80
 
81
- { files: file_payloads }
81
+ { payloads: file_payloads }
82
82
  end
83
83
 
84
84
  private def raise_argument_error