playwright-ruby-client 1.39.1 → 1.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) 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 +1 -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 +4 -1
  8. data/documentation/docs/api/frame.md +4 -1
  9. data/documentation/docs/api/locator.md +4 -1
  10. data/documentation/docs/api/locator_assertions.md +11 -1
  11. data/documentation/docs/api/page.md +6 -3
  12. data/documentation/docs/article/guides/rails_integration.md +80 -51
  13. data/documentation/docs/article/guides/rspec_integration.md +59 -0
  14. data/documentation/docusaurus.config.js +1 -1
  15. data/documentation/package.json +7 -7
  16. data/documentation/yarn.lock +4641 -5023
  17. data/lib/playwright/api_response_impl.rb +2 -2
  18. data/lib/playwright/channel_owners/api_request_context.rb +12 -3
  19. data/lib/playwright/channel_owners/browser.rb +11 -7
  20. data/lib/playwright/channel_owners/browser_context.rb +35 -15
  21. data/lib/playwright/channel_owners/frame.rb +38 -14
  22. data/lib/playwright/channel_owners/page.rb +29 -16
  23. data/lib/playwright/channel_owners/web_socket.rb +8 -13
  24. data/lib/playwright/connection.rb +18 -2
  25. data/lib/playwright/errors.rb +20 -2
  26. data/lib/playwright/input_files.rb +4 -4
  27. data/lib/playwright/locator_assertions_impl.rb +15 -15
  28. data/lib/playwright/test.rb +2 -2
  29. data/lib/playwright/utils.rb +3 -10
  30. data/lib/playwright/version.rb +2 -2
  31. data/lib/playwright/waiter.rb +146 -0
  32. data/lib/playwright.rb +1 -1
  33. data/lib/playwright_api/api_request_context.rb +1 -2
  34. data/lib/playwright_api/browser.rb +2 -2
  35. data/lib/playwright_api/browser_context.rb +3 -3
  36. data/lib/playwright_api/browser_type.rb +2 -1
  37. data/lib/playwright_api/download.rb +1 -2
  38. data/lib/playwright_api/element_handle.rb +4 -1
  39. data/lib/playwright_api/frame.rb +4 -1
  40. data/lib/playwright_api/locator.rb +4 -1
  41. data/lib/playwright_api/locator_assertions.rb +12 -2
  42. data/lib/playwright_api/page.rb +16 -13
  43. data/lib/playwright_api/request.rb +4 -4
  44. data/lib/playwright_api/worker.rb +4 -4
  45. data/sig/playwright.rbs +6 -6
  46. metadata +5 -4
  47. 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:)
@@ -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
@@ -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
@@ -79,7 +79,7 @@ module Playwright
79
79
  expected
80
80
  end
81
81
 
82
- private def to_expected_text_values(items, match_substring = false, normalize_white_space = false, ignore_case = false)
82
+ private def to_expected_text_values(items, match_substring: false, normalize_white_space: false, ignore_case: false)
83
83
  return [] unless items.respond_to?(:each)
84
84
 
85
85
  items.each.with_object([]) do |item, out|
@@ -109,9 +109,9 @@ module Playwright
109
109
  if expected.respond_to?(:each)
110
110
  expected_text = to_expected_text_values(
111
111
  expected,
112
- true,
113
- true,
114
- ignoreCase,
112
+ match_substring: true,
113
+ normalize_white_space: true,
114
+ ignore_case: ignoreCase,
115
115
  )
116
116
 
117
117
  expect_impl(
@@ -127,9 +127,9 @@ module Playwright
127
127
  else
128
128
  expected_text = to_expected_text_values(
129
129
  [expected],
130
- true,
131
- true,
132
- ignoreCase
130
+ match_substring: true,
131
+ normalize_white_space: true,
132
+ ignore_case: ignoreCase,
133
133
  )
134
134
 
135
135
  expect_impl(
@@ -146,8 +146,8 @@ module Playwright
146
146
  end
147
147
  _define_negation :to_contain_text
148
148
 
149
- def to_have_attribute(name, value, timeout: nil)
150
- expected_text = to_expected_text_values([value])
149
+ def to_have_attribute(name, value, ignoreCase: nil, timeout: nil)
150
+ expected_text = to_expected_text_values([value], ignore_case: ignoreCase)
151
151
  expect_impl(
152
152
  "to.have.attribute.value",
153
153
  {
@@ -278,9 +278,9 @@ module Playwright
278
278
  if expected.respond_to?(:each)
279
279
  expected_text = to_expected_text_values(
280
280
  expected,
281
- true,
282
- true,
283
- ignoreCase,
281
+ match_substring: true,
282
+ normalize_white_space: true,
283
+ ignore_case: ignoreCase,
284
284
  )
285
285
  expect_impl(
286
286
  "to.have.text.array",
@@ -295,9 +295,9 @@ module Playwright
295
295
  else
296
296
  expected_text = to_expected_text_values(
297
297
  [expected],
298
- true,
299
- true,
300
- ignoreCase,
298
+ match_substring: true,
299
+ normalize_white_space: true,
300
+ ignore_case: ignoreCase,
301
301
  )
302
302
  expect_impl(
303
303
  "to.have.text",
@@ -60,9 +60,9 @@ module Playwright
60
60
  # to_be_visible => be_visible
61
61
  # not_to_be_visible => not_be_visible
62
62
  root_method_name = method_name.gsub("to_", "")
63
- Matchers.define_method(root_method_name) do |*args, **kwargs|
63
+ Matchers.send(:define_method, root_method_name) do |*args, **kwargs|
64
64
  Matchers::PlaywrightMatcher.new(method_name, *args, **kwargs)
65
65
  end
66
66
  end
67
67
  end
68
- end
68
+ end