playwright-ruby-client 1.39.0 → 1.40.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.
Files changed (54) 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 +10 -14
  4. data/documentation/docs/api/browser_context.md +32 -51
  5. data/documentation/docs/api/browser_type.md +8 -12
  6. data/documentation/docs/api/dialog.md +15 -18
  7. data/documentation/docs/api/download.md +10 -12
  8. data/documentation/docs/api/element_handle.md +9 -7
  9. data/documentation/docs/api/frame.md +28 -55
  10. data/documentation/docs/api/locator.md +24 -23
  11. data/documentation/docs/api/locator_assertions.md +652 -0
  12. data/documentation/docs/api/page.md +53 -103
  13. data/documentation/docs/api/playwright.md +20 -23
  14. data/documentation/docs/api/selectors.md +29 -34
  15. data/documentation/docs/article/guides/rails_integration.md +80 -51
  16. data/documentation/docs/article/guides/rspec_integration.md +59 -0
  17. data/documentation/docs/include/api_coverage.md +43 -0
  18. data/documentation/docusaurus.config.js +1 -1
  19. data/documentation/package.json +7 -7
  20. data/documentation/yarn.lock +4641 -5023
  21. data/lib/playwright/api_response_impl.rb +2 -2
  22. data/lib/playwright/channel.rb +1 -1
  23. data/lib/playwright/channel_owners/api_request_context.rb +12 -3
  24. data/lib/playwright/channel_owners/browser.rb +11 -7
  25. data/lib/playwright/channel_owners/browser_context.rb +35 -15
  26. data/lib/playwright/channel_owners/frame.rb +38 -14
  27. data/lib/playwright/channel_owners/page.rb +29 -16
  28. data/lib/playwright/channel_owners/web_socket.rb +8 -13
  29. data/lib/playwright/connection.rb +18 -2
  30. data/lib/playwright/errors.rb +22 -2
  31. data/lib/playwright/input_files.rb +4 -4
  32. data/lib/playwright/javascript/value_serializer.rb +1 -1
  33. data/lib/playwright/locator_assertions_impl.rb +417 -0
  34. data/lib/playwright/locator_impl.rb +24 -5
  35. data/lib/playwright/test.rb +68 -0
  36. data/lib/playwright/utils.rb +3 -10
  37. data/lib/playwright/version.rb +2 -2
  38. data/lib/playwright/waiter.rb +146 -0
  39. data/lib/playwright.rb +1 -1
  40. data/lib/playwright_api/api_request_context.rb +1 -2
  41. data/lib/playwright_api/browser.rb +2 -2
  42. data/lib/playwright_api/browser_context.rb +3 -3
  43. data/lib/playwright_api/browser_type.rb +2 -1
  44. data/lib/playwright_api/download.rb +1 -2
  45. data/lib/playwright_api/element_handle.rb +4 -1
  46. data/lib/playwright_api/frame.rb +4 -1
  47. data/lib/playwright_api/locator.rb +9 -1
  48. data/lib/playwright_api/locator_assertions.rb +561 -0
  49. data/lib/playwright_api/page.rb +16 -13
  50. data/lib/playwright_api/request.rb +4 -4
  51. data/lib/playwright_api/worker.rb +4 -4
  52. data/sig/playwright.rbs +48 -5
  53. metadata +9 -4
  54. 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
@@ -48,7 +48,7 @@ module Playwright
48
48
 
49
49
  private def with_logging(&block)
50
50
  locations = caller_locations
51
- first_api_call_location_idx = locations.index { |loc| loc.absolute_path.include?('playwright_api') }
51
+ first_api_call_location_idx = locations.index { |loc| loc.absolute_path&.include?('playwright_api') }
52
52
  unless first_api_call_location_idx
53
53
  return block.call(nil)
54
54
  end
@@ -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:)
@@ -382,13 +384,17 @@ module Playwright
382
384
  end
383
385
 
384
386
  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)
387
+ waiter = Waiter.new(self, wait_name: "browser_context.expect_event(#{event})")
388
+ timeout_value = timeout || @timeout_settings.timeout
389
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout}ms exceeded while waiting for event \"#{event}\"")
390
+ unless event == Events::BrowserContext::Close
391
+ waiter.reject_on_event(self, Events::BrowserContext::Close, TargetClosedError.new)
392
+ end
393
+ waiter.wait_for_event(self, event, predicate: predicate)
388
394
 
389
395
  block&.call
390
396
 
391
- wait_helper.promise.value!
397
+ waiter.result.value!
392
398
  end
393
399
 
394
400
  private def on_close
@@ -397,7 +403,18 @@ module Playwright
397
403
  @closed_promise.fulfill(true)
398
404
  end
399
405
 
400
- def close
406
+ def close(reason: nil)
407
+ return if @close_was_called
408
+ @close_was_called = true
409
+ @close_reason = reason
410
+
411
+ inner_close
412
+ @channel.send_message_to_server('close', { reason: reason }.compact)
413
+ @closed_promise.value!
414
+ nil
415
+ end
416
+
417
+ private def inner_close
401
418
  @har_recorders.each do |har_id, params|
402
419
  har = ChannelOwners::Artifact.from(@channel.send_message_to_server('harExport', harId: har_id))
403
420
  # Server side will compress artifact if content is attach or if file is .zip.
@@ -413,11 +430,6 @@ module Playwright
413
430
 
414
431
  har.delete
415
432
  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
433
  end
422
434
 
423
435
  # REMARK: enable_debug_console is playwright-ruby-client specific method.
@@ -459,6 +471,10 @@ module Playwright
459
471
  end
460
472
  end
461
473
 
474
+ private def effective_close_reason
475
+ @close_reason || @browser&.send(:close_reason)
476
+ end
477
+
462
478
  def expect_console_message(predicate: nil, timeout: nil, &block)
463
479
  params = {
464
480
  predicate: predicate,
@@ -502,10 +518,14 @@ module Playwright
502
518
  end
503
519
 
504
520
  # called from InputFiles
505
- # @param name [string]
521
+ # @param filepath [string]
506
522
  # @return [WritableStream]
507
- private def create_temp_file(name)
508
- result = @channel.send_message_to_server('createTempFile', name: name)
523
+ private def create_temp_file(filepath)
524
+ result = @channel.send_message_to_server(
525
+ 'createTempFile',
526
+ name: File.basename(filepath),
527
+ lastModifiedMs: File.mtime(filepath).to_i * 1000,
528
+ )
509
529
  ChannelOwners::WritableStream.from(result)
510
530
  end
511
531
  end
@@ -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(
@@ -479,7 +491,8 @@ module Playwright
479
491
  @main_frame.title
480
492
  end
481
493
 
482
- def close(runBeforeUnload: nil)
494
+ def close(runBeforeUnload: nil, reason: nil)
495
+ @close_reason = reason
483
496
  if @owned_context
484
497
  @owned_context.close
485
498
  else
@@ -488,7 +501,7 @@ module Playwright
488
501
  end
489
502
  nil
490
503
  rescue => err
491
- raise if !safe_close_error?(err) || !runBeforeUnload
504
+ raise if !target_closed_error?(err) || !runBeforeUnload
492
505
  end
493
506
 
494
507
  def closed?
@@ -878,34 +891,34 @@ module Playwright
878
891
  end
879
892
  end
880
893
 
881
- class AlreadyClosedError < StandardError
882
- def initialize
883
- super('Page closed')
884
- end
885
- end
886
-
887
894
  class FrameAlreadyDetachedError < StandardError
888
895
  def initialize
889
896
  super('Navigating frame was detached!')
890
897
  end
891
898
  end
892
899
 
900
+ private def close_error_with_reason
901
+ reason = @close_reason || @browser_context.send(:effective_close_reason)
902
+ TargetClosedError.new(message: reason)
903
+ end
904
+
893
905
  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}\"")
906
+ waiter = Waiter.new(self, wait_name: "Page.expect_event(#{event})")
907
+ timeout_value = timeout || @timeout_settings.timeout
908
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout_value}ms exceeded while waiting for event \"#{event}\"")
896
909
 
897
910
  unless event == Events::Page::Crash
898
- wait_helper.reject_on_event(self, Events::Page::Crash, CrashedError.new)
911
+ waiter.reject_on_event(self, Events::Page::Crash, CrashedError.new)
899
912
  end
900
913
 
901
914
  unless event == Events::Page::Close
902
- wait_helper.reject_on_event(self, Events::Page::Close, AlreadyClosedError.new)
915
+ waiter.reject_on_event(self, Events::Page::Close, -> { close_error_with_reason })
903
916
  end
904
917
 
905
- wait_helper.wait_for_event(self, event, predicate: predicate)
918
+ waiter.wait_for_event(self, event, predicate: predicate)
906
919
  block&.call
907
920
 
908
- wait_helper.promise.value!
921
+ waiter.result.value!
909
922
  end
910
923
 
911
924
  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
@@ -49,4 +67,6 @@ module Playwright
49
67
 
50
68
  attr_reader :error, :page
51
69
  end
70
+
71
+ class AssertionError < StandardError; end
52
72
  end
@@ -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
@@ -24,7 +24,7 @@ module Playwright
24
24
  @handles << value.channel
25
25
  { h: index }
26
26
  when nil
27
- { v: 'undefined' }
27
+ { v: 'null' }
28
28
  when Float::NAN
29
29
  { v: 'NaN'}
30
30
  when Float::INFINITY