playwright-ruby-client 1.39.1 → 1.41.beta1

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