playwright-ruby-client 0.0.8 → 1.58.1.alpha1

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 (209) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +4 -0
  3. data/CLAUDE/api_generation.md +28 -0
  4. data/CLAUDE/ci_expectations.md +23 -0
  5. data/CLAUDE/gem_release_flow.md +39 -0
  6. data/CLAUDE/past_upgrade_pr_patterns.md +42 -0
  7. data/CLAUDE/playwright_upgrade_workflow.md +35 -0
  8. data/CLAUDE/rspec_debugging.md +30 -0
  9. data/CLAUDE/unimplemented_examples.md +18 -0
  10. data/CLAUDE.md +32 -0
  11. data/CONTRIBUTING.md +5 -0
  12. data/README.md +60 -16
  13. data/documentation/README.md +33 -0
  14. data/documentation/babel.config.js +3 -0
  15. data/documentation/docs/api/api_request.md +7 -0
  16. data/documentation/docs/api/api_request_context.md +298 -0
  17. data/documentation/docs/api/api_response.md +114 -0
  18. data/documentation/docs/api/browser.md +237 -0
  19. data/documentation/docs/api/browser_context.md +503 -0
  20. data/documentation/docs/api/browser_type.md +184 -0
  21. data/documentation/docs/api/cdp_session.md +44 -0
  22. data/documentation/docs/api/clock.md +154 -0
  23. data/documentation/docs/api/console_message.md +85 -0
  24. data/documentation/docs/api/dialog.md +84 -0
  25. data/documentation/docs/api/download.md +111 -0
  26. data/documentation/docs/api/element_handle.md +694 -0
  27. data/documentation/docs/api/experimental/_category_.yml +3 -0
  28. data/documentation/docs/api/experimental/android.md +42 -0
  29. data/documentation/docs/api/experimental/android_device.md +109 -0
  30. data/documentation/docs/api/experimental/android_input.md +43 -0
  31. data/documentation/docs/api/experimental/android_socket.md +7 -0
  32. data/documentation/docs/api/experimental/android_web_view.md +7 -0
  33. data/documentation/docs/api/file_chooser.md +53 -0
  34. data/documentation/docs/api/frame.md +1218 -0
  35. data/documentation/docs/api/frame_locator.md +348 -0
  36. data/documentation/docs/api/js_handle.md +121 -0
  37. data/documentation/docs/api/keyboard.md +170 -0
  38. data/documentation/docs/api/locator.md +1495 -0
  39. data/documentation/docs/api/locator_assertions.md +827 -0
  40. data/documentation/docs/api/mouse.md +86 -0
  41. data/documentation/docs/api/page.md +1946 -0
  42. data/documentation/docs/api/page_assertions.md +65 -0
  43. data/documentation/docs/api/playwright.md +66 -0
  44. data/documentation/docs/api/request.md +255 -0
  45. data/documentation/docs/api/response.md +176 -0
  46. data/documentation/docs/api/route.md +205 -0
  47. data/documentation/docs/api/selectors.md +63 -0
  48. data/documentation/docs/api/touchscreen.md +22 -0
  49. data/documentation/docs/api/tracing.md +129 -0
  50. data/documentation/docs/api/web_socket.md +51 -0
  51. data/documentation/docs/api/worker.md +83 -0
  52. data/documentation/docs/article/api_coverage.mdx +11 -0
  53. data/documentation/docs/article/getting_started.md +161 -0
  54. data/documentation/docs/article/guides/_category_.yml +3 -0
  55. data/documentation/docs/article/guides/download_playwright_driver.md +55 -0
  56. data/documentation/docs/article/guides/inspector.md +31 -0
  57. data/documentation/docs/article/guides/launch_browser.md +121 -0
  58. data/documentation/docs/article/guides/playwright_on_alpine_linux.md +112 -0
  59. data/documentation/docs/article/guides/rails_integration.md +278 -0
  60. data/documentation/docs/article/guides/rails_integration_with_null_driver.md +145 -0
  61. data/documentation/docs/article/guides/recording_video.md +79 -0
  62. data/documentation/docs/article/guides/rspec_integration.md +59 -0
  63. data/documentation/docs/article/guides/semi_automation.md +71 -0
  64. data/documentation/docs/article/guides/use_storage_state.md +78 -0
  65. data/documentation/docs/include/api_coverage.md +671 -0
  66. data/documentation/docusaurus.config.js +114 -0
  67. data/documentation/package.json +39 -0
  68. data/documentation/sidebars.js +15 -0
  69. data/documentation/src/components/HomepageFeatures.js +61 -0
  70. data/documentation/src/components/HomepageFeatures.module.css +13 -0
  71. data/documentation/src/css/custom.css +44 -0
  72. data/documentation/src/pages/index.js +49 -0
  73. data/documentation/src/pages/index.module.css +41 -0
  74. data/documentation/src/pages/markdown-page.md +7 -0
  75. data/documentation/static/.nojekyll +0 -0
  76. data/documentation/static/img/playwright-logo.svg +9 -0
  77. data/documentation/static/img/playwright-ruby-client.png +0 -0
  78. data/documentation/static/img/undraw_dropdown_menu.svg +1 -0
  79. data/documentation/static/img/undraw_web_development.svg +1 -0
  80. data/documentation/static/img/undraw_windows.svg +1 -0
  81. data/documentation/yarn.lock +9005 -0
  82. data/lib/playwright/{input_types/android_input.rb → android_input_impl.rb} +5 -1
  83. data/lib/playwright/api_implementation.rb +18 -0
  84. data/lib/playwright/api_response_impl.rb +77 -0
  85. data/lib/playwright/channel.rb +62 -1
  86. data/lib/playwright/channel_owner.rb +70 -7
  87. data/lib/playwright/channel_owners/android.rb +16 -3
  88. data/lib/playwright/channel_owners/android_device.rb +22 -66
  89. data/lib/playwright/channel_owners/api_request_context.rb +247 -0
  90. data/lib/playwright/channel_owners/artifact.rb +40 -0
  91. data/lib/playwright/channel_owners/binding_call.rb +70 -0
  92. data/lib/playwright/channel_owners/browser.rb +114 -22
  93. data/lib/playwright/channel_owners/browser_context.rb +589 -15
  94. data/lib/playwright/channel_owners/browser_type.rb +90 -1
  95. data/lib/playwright/channel_owners/cdp_session.rb +19 -0
  96. data/lib/playwright/channel_owners/dialog.rb +32 -0
  97. data/lib/playwright/channel_owners/element_handle.rb +107 -43
  98. data/lib/playwright/channel_owners/fetch_request.rb +8 -0
  99. data/lib/playwright/channel_owners/frame.rb +334 -104
  100. data/lib/playwright/channel_owners/js_handle.rb +9 -13
  101. data/lib/playwright/channel_owners/local_utils.rb +82 -0
  102. data/lib/playwright/channel_owners/page.rb +778 -95
  103. data/lib/playwright/channel_owners/playwright.rb +25 -30
  104. data/lib/playwright/channel_owners/request.rb +120 -18
  105. data/lib/playwright/channel_owners/response.rb +113 -0
  106. data/lib/playwright/channel_owners/route.rb +181 -0
  107. data/lib/playwright/channel_owners/stream.rb +30 -0
  108. data/lib/playwright/channel_owners/tracing.rb +117 -0
  109. data/lib/playwright/channel_owners/web_socket.rb +96 -0
  110. data/lib/playwright/channel_owners/worker.rb +46 -0
  111. data/lib/playwright/channel_owners/writable_stream.rb +14 -0
  112. data/lib/playwright/clock_impl.rb +67 -0
  113. data/lib/playwright/connection.rb +111 -63
  114. data/lib/playwright/console_message_impl.rb +29 -0
  115. data/lib/playwright/download_impl.rb +32 -0
  116. data/lib/playwright/errors.rb +42 -5
  117. data/lib/playwright/event_emitter.rb +17 -3
  118. data/lib/playwright/event_emitter_proxy.rb +49 -0
  119. data/lib/playwright/events.rb +10 -5
  120. data/lib/playwright/file_chooser_impl.rb +24 -0
  121. data/lib/playwright/frame_locator_impl.rb +66 -0
  122. data/lib/playwright/har_router.rb +89 -0
  123. data/lib/playwright/http_headers.rb +14 -0
  124. data/lib/playwright/input_files.rb +102 -15
  125. data/lib/playwright/javascript/expression.rb +7 -11
  126. data/lib/playwright/javascript/regex.rb +23 -0
  127. data/lib/playwright/javascript/source_url.rb +16 -0
  128. data/lib/playwright/javascript/value_parser.rb +108 -19
  129. data/lib/playwright/javascript/value_serializer.rb +47 -8
  130. data/lib/playwright/javascript/visitor_info.rb +26 -0
  131. data/lib/playwright/javascript.rb +2 -10
  132. data/lib/playwright/{input_types/keyboard.rb → keyboard_impl.rb} +6 -2
  133. data/lib/playwright/locator_assertions_impl.rb +571 -0
  134. data/lib/playwright/locator_impl.rb +544 -0
  135. data/lib/playwright/locator_utils.rb +136 -0
  136. data/lib/playwright/mouse_impl.rb +57 -0
  137. data/lib/playwright/page_assertions_impl.rb +154 -0
  138. data/lib/playwright/playwright_api.rb +102 -30
  139. data/lib/playwright/raw_headers.rb +61 -0
  140. data/lib/playwright/route_handler.rb +78 -0
  141. data/lib/playwright/select_option_values.rb +34 -13
  142. data/lib/playwright/selectors_impl.rb +45 -0
  143. data/lib/playwright/test.rb +102 -0
  144. data/lib/playwright/timeout_settings.rb +9 -4
  145. data/lib/playwright/touchscreen_impl.rb +14 -0
  146. data/lib/playwright/transport.rb +61 -10
  147. data/lib/playwright/url_matcher.rb +24 -2
  148. data/lib/playwright/utils.rb +48 -13
  149. data/lib/playwright/version.rb +2 -1
  150. data/lib/playwright/video.rb +54 -0
  151. data/lib/playwright/waiter.rb +166 -0
  152. data/lib/playwright/web_socket_client.rb +167 -0
  153. data/lib/playwright/web_socket_transport.rb +116 -0
  154. data/lib/playwright.rb +188 -11
  155. data/lib/playwright_api/android.rb +46 -11
  156. data/lib/playwright_api/android_device.rb +182 -31
  157. data/lib/playwright_api/android_input.rb +22 -13
  158. data/lib/playwright_api/android_socket.rb +18 -0
  159. data/lib/playwright_api/android_web_view.rb +24 -0
  160. data/lib/playwright_api/api_request.rb +26 -0
  161. data/lib/playwright_api/api_request_context.rb +311 -0
  162. data/lib/playwright_api/api_response.rb +92 -0
  163. data/lib/playwright_api/browser.rb +116 -103
  164. data/lib/playwright_api/browser_context.rb +290 -389
  165. data/lib/playwright_api/browser_type.rb +96 -118
  166. data/lib/playwright_api/cdp_session.rb +36 -39
  167. data/lib/playwright_api/clock.rb +121 -0
  168. data/lib/playwright_api/console_message.rb +35 -19
  169. data/lib/playwright_api/dialog.rb +53 -50
  170. data/lib/playwright_api/download.rb +49 -43
  171. data/lib/playwright_api/element_handle.rb +354 -402
  172. data/lib/playwright_api/file_chooser.rb +15 -18
  173. data/lib/playwright_api/frame.rb +703 -603
  174. data/lib/playwright_api/frame_locator.rb +285 -0
  175. data/lib/playwright_api/js_handle.rb +50 -76
  176. data/lib/playwright_api/keyboard.rb +67 -146
  177. data/lib/playwright_api/locator.rb +1304 -0
  178. data/lib/playwright_api/locator_assertions.rb +704 -0
  179. data/lib/playwright_api/mouse.rb +23 -29
  180. data/lib/playwright_api/page.rb +1196 -1176
  181. data/lib/playwright_api/page_assertions.rb +60 -0
  182. data/lib/playwright_api/playwright.rb +54 -122
  183. data/lib/playwright_api/request.rb +112 -74
  184. data/lib/playwright_api/response.rb +92 -20
  185. data/lib/playwright_api/route.rb +152 -62
  186. data/lib/playwright_api/selectors.rb +47 -61
  187. data/lib/playwright_api/touchscreen.rb +8 -2
  188. data/lib/playwright_api/tracing.rb +128 -0
  189. data/lib/playwright_api/web_socket.rb +43 -5
  190. data/lib/playwright_api/worker.rb +74 -34
  191. data/playwright.gemspec +14 -9
  192. data/sig/playwright.rbs +658 -0
  193. metadata +216 -50
  194. data/docs/api_coverage.md +0 -354
  195. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  196. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  197. data/lib/playwright/channel_owners/console_message.rb +0 -21
  198. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  199. data/lib/playwright/channel_owners/selectors.rb +0 -4
  200. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  201. data/lib/playwright/input_type.rb +0 -19
  202. data/lib/playwright/input_types/mouse.rb +0 -4
  203. data/lib/playwright/input_types/touchscreen.rb +0 -4
  204. data/lib/playwright/javascript/function.rb +0 -67
  205. data/lib/playwright/wait_helper.rb +0 -73
  206. data/lib/playwright_api/accessibility.rb +0 -93
  207. data/lib/playwright_api/binding_call.rb +0 -23
  208. data/lib/playwright_api/chromium_browser_context.rb +0 -57
  209. data/lib/playwright_api/video.rb +0 -24
@@ -1,46 +1,83 @@
1
1
  require 'base64'
2
+ require_relative '../locator_utils'
2
3
 
3
4
  module Playwright
4
5
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_page.py
5
6
  define_channel_owner :Page do
6
- include Utils::Errors::SafeCloseError
7
+ include Utils::Errors::TargetClosedErrorMethods
8
+ include LocatorUtils
7
9
  attr_writer :owned_context
8
10
 
9
- def after_initialize
11
+ private def after_initialize
10
12
  @browser_context = @parent
11
13
  @timeout_settings = TimeoutSettings.new(@browser_context.send(:_timeout_settings))
12
- @accessibility = Accessibility.new(@channel)
13
- @keyboard = InputTypes::Keyboard.new(@channel)
14
- @mouse = InputTypes::Mouse.new(@channel)
15
- @touchscreen = InputTypes::Touchscreen.new(@channel)
14
+ @keyboard = KeyboardImpl.new(@channel)
15
+ @mouse = MouseImpl.new(@channel)
16
+ @touchscreen = TouchscreenImpl.new(@channel)
16
17
 
17
- @viewport_size = @initializer['viewportSize']
18
+ if @initializer['viewportSize']
19
+ @viewport_size = {
20
+ width: @initializer['viewportSize']['width'],
21
+ height: @initializer['viewportSize']['height'],
22
+ }
23
+ end
18
24
  @closed = false
25
+ @workers = Set.new
26
+ @bindings = {}
27
+ @routes = []
28
+
19
29
  @main_frame = ChannelOwners::Frame.from(@initializer['mainFrame'])
20
30
  @main_frame.send(:update_page_from_page, self)
21
31
  @frames = Set.new
22
32
  @frames << @main_frame
33
+ @opener = ChannelOwners::Page.from_nullable(@initializer['opener'])
34
+ @close_reason = nil
23
35
 
36
+ @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
37
+ @closed_or_crashed_promise = Concurrent::Promises.resolvable_future
24
38
  @channel.once('close', ->(_) { on_close })
25
- @channel.on('console', ->(params) {
26
- console_message = ChannelOwners::ConsoleMessage.from(params['message'])
27
- emit(Events::Page::Console, console_message)
39
+ @channel.on('crash', ->(_) { on_crash })
40
+ @channel.on('download', method(:on_download))
41
+ @channel.on('fileChooser', ->(params) {
42
+ chooser = FileChooserImpl.new(
43
+ page: self,
44
+ timeout_settings: @timeout_settings,
45
+ element_handle: ChannelOwners::ElementHandle.from(params['element']),
46
+ is_multiple: params['isMultiple'])
47
+ emit(Events::Page::FileChooser, chooser)
28
48
  })
29
- @channel.on('domcontentloaded', ->(_) { emit(Events::Page::DOMContentLoaded) })
30
49
  @channel.on('frameAttached', ->(params) {
31
50
  on_frame_attached(ChannelOwners::Frame.from(params['frame']))
32
51
  })
33
52
  @channel.on('frameDetached', ->(params) {
34
53
  on_frame_detached(ChannelOwners::Frame.from(params['frame']))
35
54
  })
36
- @channel.on('load', ->(_) { emit(Events::Page::Load) })
37
- @channel.on('popup', ->(params) {
38
- emit(Events::Page::Popup, ChannelOwners::Page.from(params['page']))
55
+ @channel.on('pageError', ->(params) {
56
+ emit(Events::Page::PageError, Error.parse(params['error']['error']))
57
+ })
58
+ @channel.on('route', ->(params) { on_route(ChannelOwners::Route.from(params['route'])) })
59
+ @channel.on('video', method(:on_video))
60
+ @channel.on('viewportSizeChanged', method(:on_viewport_size_changed))
61
+ @channel.on('webSocket', ->(params) {
62
+ emit(Events::Page::WebSocket, ChannelOwners::WebSocket.from(params['webSocket']))
63
+ })
64
+ @channel.on('worker', ->(params) {
65
+ worker = ChannelOwners::Worker.from(params['worker'])
66
+ on_worker(worker)
67
+ })
68
+
69
+ set_event_to_subscription_mapping({
70
+ Events::Page::Console => "console",
71
+ Events::Page::Dialog => "dialog",
72
+ Events::Page::Request => "request",
73
+ Events::Page::Response => "response",
74
+ Events::Page::RequestFinished => "requestFinished",
75
+ Events::Page::RequestFailed => "requestFailed",
76
+ Events::Page::FileChooser => "fileChooser",
39
77
  })
40
78
  end
41
79
 
42
80
  attr_reader \
43
- :accessibility,
44
81
  :keyboard,
45
82
  :mouse,
46
83
  :touchscreen,
@@ -59,41 +96,135 @@ module Playwright
59
96
  emit(Events::Page::FrameDetached, frame)
60
97
  end
61
98
 
99
+ private def on_route(route)
100
+ route.send(:update_context, self)
101
+ return if @close_was_called
102
+ # It is not desired to use PlaywrightApi.wrap directly.
103
+ # However it is a little difficult to define wrapper for `handler` parameter in generate_api.
104
+ # Just a workaround...
105
+ Concurrent::Promises.future(PlaywrightApi.wrap(route)) do |wrapped_route|
106
+ handled = @routes.any? do |handler_entry|
107
+ next false unless handler_entry.match?(route.request.url)
108
+
109
+ promise = Concurrent::Promises.resolvable_future
110
+ route.send(:set_handling_future, promise)
111
+
112
+ promise_handled = Concurrent::Promises.zip(
113
+ promise,
114
+ handler_entry.async_handle(wrapped_route)
115
+ ).value!.first
116
+
117
+ promise_handled
118
+ end
119
+
120
+ @routes.reject!(&:expired?)
121
+ if @routes.count == 0
122
+ async_update_interception_patterns
123
+ end
124
+
125
+ unless handled
126
+ @browser_context.send(:on_route, route)
127
+ end
128
+ end.rescue do |err|
129
+ puts err, err.backtrace
130
+ end
131
+ end
132
+
133
+ private def on_binding(binding_call)
134
+ func = @bindings[binding_call.name]
135
+ if func
136
+ binding_call.call_async(func)
137
+ end
138
+ @browser_context.send(:on_binding, binding_call)
139
+ end
140
+
141
+ private def on_worker(worker)
142
+ worker.page = self
143
+ @workers << worker
144
+ emit(Events::Page::Worker, worker)
145
+ end
146
+
62
147
  private def on_close
63
148
  @closed = true
64
149
  @browser_context.send(:remove_page, self)
150
+ if @closed_or_crashed_promise.pending?
151
+ @closed_or_crashed_promise.fulfill(close_error_with_reason)
152
+ end
65
153
  emit(Events::Page::Close)
66
154
  end
67
155
 
156
+ private def on_crash
157
+ if @closed_or_crashed_promise.pending?
158
+ @closed_or_crashed_promise.fulfill(TargetClosedError.new)
159
+ end
160
+ emit(Events::Page::Crash)
161
+ end
162
+
163
+ private def on_download(params)
164
+ artifact = ChannelOwners::Artifact.from(params['artifact'])
165
+ download = DownloadImpl.new(
166
+ page: self,
167
+ url: params['url'],
168
+ suggested_filename: params['suggestedFilename'],
169
+ artifact: artifact,
170
+ )
171
+ emit(Events::Page::Download, download)
172
+ end
173
+
174
+ private def on_video(params)
175
+ artifact = ChannelOwners::Artifact.from(params['artifact'])
176
+ video.send(:set_artifact, artifact)
177
+ end
178
+
179
+ private def on_viewport_size_changed(params)
180
+ @viewport_size = {
181
+ width: params['viewportSize']['width'],
182
+ height: params['viewportSize']['height'],
183
+ }
184
+ end
185
+
186
+ # @override
187
+ private def perform_event_emitter_callback(event, callback, args)
188
+ should_callback_async = [
189
+ Events::Page::Dialog,
190
+ Events::Page::Response,
191
+ ].freeze
192
+
193
+ if should_callback_async.include?(event)
194
+ Concurrent::Promises.future { super }
195
+ else
196
+ super
197
+ end
198
+ end
199
+
68
200
  def context
69
201
  @browser_context
70
202
  end
71
203
 
204
+ def clock
205
+ @browser_context.clock
206
+ end
207
+
72
208
  def opener
73
- resp = @channel.send_message_to_server('opener')
74
- ChannelOwners::Page.from(resp)
209
+ if @opener&.closed?
210
+ nil
211
+ else
212
+ @opener
213
+ end
75
214
  end
76
215
 
77
- def frame(frameSelector)
78
- name, url =
79
- if frameSelector.is_a?(Hash)
80
- [frameSelector[:name], frameSelector[:url]]
81
- else
82
- [frameSelector, nil]
83
- end
216
+ private def emit_popup_event_from_browser_context
217
+ if @opener && !@opener.closed?
218
+ @opener.emit(Events::Page::Popup, self)
219
+ end
220
+ end
84
221
 
222
+ def frame(name: nil, url: nil)
85
223
  if name
86
224
  @frames.find { |f| f.name == name }
87
225
  elsif url
88
- # ref: https://github.com/microsoft/playwright-python/blob/c4320c27cb080b385a5e45be46baa3cb7a9409ff/playwright/_impl/_helper.py#L104
89
- case url
90
- when String
91
- @frames.find { |f| f.url == url }
92
- when Regexp
93
- @frames.find { |f| url.match?(f.url) }
94
- else
95
- raise NotImplementedError.new('Page#frame with url is not completely implemented yet')
96
- end
226
+ matcher = UrlMatcher.new(url, base_url: @browser_context.send(:base_url))
227
+ @frames.find { |f| matcher.match?(f.url) }
97
228
  else
98
229
  raise ArgumentError.new('Either name or url matcher should be specified')
99
230
  end
@@ -105,22 +236,52 @@ module Playwright
105
236
 
106
237
  def set_default_navigation_timeout(timeout)
107
238
  @timeout_settings.default_navigation_timeout = timeout
108
- @channel.send_message_to_server('setDefaultNavigationTimeoutNoReply', timeout: timeout)
109
239
  end
110
240
 
111
241
  def set_default_timeout(timeout)
112
242
  @timeout_settings.default_timeout = timeout
113
- @channel.send_message_to_server('setDefaultTimeoutNoReply', timeout: timeout)
114
243
  end
115
244
 
116
- def query_selector(selector)
117
- @main_frame.query_selector(selector)
245
+ def query_selector(selector, strict: nil)
246
+ @main_frame.query_selector(selector, strict: strict)
118
247
  end
119
248
 
120
249
  def query_selector_all(selector)
121
250
  @main_frame.query_selector_all(selector)
122
251
  end
123
252
 
253
+ def wait_for_selector(selector, state: nil, strict: nil, timeout: nil)
254
+ @main_frame.wait_for_selector(selector, state: state, strict: strict, timeout: timeout)
255
+ end
256
+
257
+ def checked?(selector, strict: nil, timeout: nil)
258
+ @main_frame.checked?(selector, strict: strict, timeout: timeout)
259
+ end
260
+
261
+ def disabled?(selector, strict: nil, timeout: nil)
262
+ @main_frame.disabled?(selector, strict: strict, timeout: timeout)
263
+ end
264
+
265
+ def editable?(selector, strict: nil, timeout: nil)
266
+ @main_frame.editable?(selector, strict: strict, timeout: timeout)
267
+ end
268
+
269
+ def enabled?(selector, strict: nil, timeout: nil)
270
+ @main_frame.enabled?(selector, strict: strict, timeout: timeout)
271
+ end
272
+
273
+ def hidden?(selector, strict: nil, timeout: nil)
274
+ @main_frame.hidden?(selector, strict: strict, timeout: timeout)
275
+ end
276
+
277
+ def visible?(selector, strict: nil, timeout: nil)
278
+ @main_frame.visible?(selector, strict: strict, timeout: timeout)
279
+ end
280
+
281
+ def dispatch_event(selector, type, eventInit: nil, strict: nil, timeout: nil)
282
+ @main_frame.dispatch_event(selector, type, eventInit: eventInit, strict: strict, timeout: timeout)
283
+ end
284
+
124
285
  def evaluate(pageFunction, arg: nil)
125
286
  @main_frame.evaluate(pageFunction, arg: arg)
126
287
  end
@@ -129,8 +290,8 @@ module Playwright
129
290
  @main_frame.evaluate_handle(pageFunction, arg: arg)
130
291
  end
131
292
 
132
- def eval_on_selector(selector, pageFunction, arg: nil)
133
- @main_frame.eval_on_selector(selector, pageFunction, arg: arg)
293
+ def eval_on_selector(selector, pageFunction, arg: nil, strict: nil)
294
+ @main_frame.eval_on_selector(selector, pageFunction, arg: arg, strict: strict)
134
295
  end
135
296
 
136
297
  def eval_on_selector_all(selector, pageFunction, arg: nil)
@@ -145,6 +306,25 @@ module Playwright
145
306
  @main_frame.add_style_tag(content: content, path: path, url: url)
146
307
  end
147
308
 
309
+ def expose_function(name, callback)
310
+ @channel.send_message_to_server('exposeBinding', name: name)
311
+ @bindings[name] = ->(_source, *args) { callback.call(*args) }
312
+ end
313
+
314
+ def expose_binding(name, callback, handle: nil)
315
+ params = {
316
+ name: name,
317
+ needsHandle: handle,
318
+ }.compact
319
+ @channel.send_message_to_server('exposeBinding', params)
320
+ @bindings[name] = callback
321
+ end
322
+
323
+ def set_extra_http_headers(headers)
324
+ serialized_headers = HttpHeaders.new(headers).as_serialized
325
+ @channel.send_message_to_server('setExtraHTTPHeaders', headers: serialized_headers)
326
+ end
327
+
148
328
  def url
149
329
  @main_frame.url
150
330
  end
@@ -163,10 +343,10 @@ module Playwright
163
343
 
164
344
  def reload(timeout: nil, waitUntil: nil)
165
345
  params = {
166
- timeout: timeout,
346
+ timeout: @timeout_settings.timeout(timeout),
167
347
  waitUntil: waitUntil,
168
348
  }.compact
169
- resp = @channel.send_message_to_server('reoad', params)
349
+ resp = @channel.send_message_to_server('reload', params)
170
350
  ChannelOwners::Response.from_nullable(resp)
171
351
  end
172
352
 
@@ -174,29 +354,144 @@ module Playwright
174
354
  @main_frame.wait_for_load_state(state: state, timeout: timeout)
175
355
  end
176
356
 
357
+ def wait_for_url(url, timeout: nil, waitUntil: nil)
358
+ @main_frame.wait_for_url(url, timeout: timeout, waitUntil: waitUntil)
359
+ end
360
+
361
+ def go_back(timeout: nil, waitUntil: nil)
362
+ params = { timeout: @timeout_settings.timeout(timeout), waitUntil: waitUntil }.compact
363
+ resp = @channel.send_message_to_server('goBack', params)
364
+ ChannelOwners::Response.from_nullable(resp)
365
+ end
366
+
367
+ def go_forward(timeout: nil, waitUntil: nil)
368
+ params = { timeout: @timeout_settings.timeout(timeout), waitUntil: waitUntil }.compact
369
+ resp = @channel.send_message_to_server('goForward', params)
370
+ ChannelOwners::Response.from_nullable(resp)
371
+ end
372
+
373
+ def emulate_media(colorScheme: nil, contrast: nil, forcedColors: nil, media: nil, reducedMotion: nil)
374
+ params = {
375
+ colorScheme: no_override_if_null(colorScheme),
376
+ contrast: no_override_if_null(contrast),
377
+ forcedColors: no_override_if_null(forcedColors),
378
+ media: no_override_if_null(media),
379
+ reducedMotion: no_override_if_null(reducedMotion),
380
+ }.compact
381
+ @channel.send_message_to_server('emulateMedia', params)
382
+
383
+ nil
384
+ end
385
+
386
+ private def no_override_if_null(target)
387
+ if target == 'null'
388
+ 'no-override'
389
+ else
390
+ target
391
+ end
392
+ end
393
+
177
394
  def set_viewport_size(viewportSize)
178
395
  @viewport_size = viewportSize
179
396
  @channel.send_message_to_server('setViewportSize', { viewportSize: viewportSize })
180
397
  nil
181
398
  end
182
399
 
400
+ def bring_to_front
401
+ @channel.send_message_to_server('bringToFront')
402
+ nil
403
+ end
404
+
405
+ def add_init_script(path: nil, script: nil)
406
+ source =
407
+ if path
408
+ JavaScript::SourceUrl.new(File.read(path), path).to_s
409
+ elsif script
410
+ script
411
+ else
412
+ raise ArgumentError.new('Either path or script parameter must be specified')
413
+ end
414
+
415
+ @channel.send_message_to_server('addInitScript', source: source)
416
+ nil
417
+ end
418
+
419
+ def route(url, handler, times: nil)
420
+ entry = RouteHandler.new(url, @browser_context.send(:base_url), handler, times)
421
+ @routes.unshift(entry)
422
+ update_interception_patterns
423
+ end
424
+
425
+ def unroute_all(behavior: nil)
426
+ @routes.clear
427
+ update_interception_patterns
428
+ end
429
+
430
+ def unroute(url, handler: nil)
431
+ @routes.reject! do |handler_entry|
432
+ handler_entry.same_value?(url: url, handler: handler)
433
+ end
434
+ update_interception_patterns
435
+ end
436
+
437
+ def route_from_har(har, notFound: nil, update: nil, url: nil, updateContent: nil, updateMode: nil)
438
+ if update
439
+ @browser_context.send(:record_into_har, har, self, url: url, update_content: updateContent, update_mode: updateMode)
440
+ return
441
+ end
442
+
443
+ router = HarRouter.create(
444
+ @connection.local_utils,
445
+ har.to_s,
446
+ notFound || "abort",
447
+ url_match: url,
448
+ )
449
+ router.add_page_route(self)
450
+ end
451
+
452
+ private def async_update_interception_patterns
453
+ patterns = RouteHandler.prepare_interception_patterns(@routes)
454
+ @channel.async_send_message_to_server('setNetworkInterceptionPatterns', patterns: patterns)
455
+ end
456
+
457
+ private def update_interception_patterns
458
+ patterns = RouteHandler.prepare_interception_patterns(@routes)
459
+ @channel.send_message_to_server('setNetworkInterceptionPatterns', patterns: patterns)
460
+ end
461
+
183
462
  def screenshot(
184
- path: nil,
185
- type: nil,
186
- quality: nil,
187
- fullPage: nil,
463
+ animations: nil,
464
+ caret: nil,
188
465
  clip: nil,
466
+ fullPage: nil,
467
+ mask: nil,
468
+ maskColor: nil,
189
469
  omitBackground: nil,
190
- timeout: nil)
470
+ path: nil,
471
+ quality: nil,
472
+ scale: nil,
473
+ style: nil,
474
+ timeout: nil,
475
+ type: nil)
191
476
 
192
477
  params = {
193
478
  type: type,
194
479
  quality: quality,
195
480
  fullPage: fullPage,
196
481
  clip: clip,
482
+ maskColor: maskColor,
197
483
  omitBackground: omitBackground,
198
- timeout: timeout,
484
+ animations: animations,
485
+ caret: caret,
486
+ scale: scale,
487
+ style: style,
488
+ timeout: @timeout_settings.timeout(timeout),
199
489
  }.compact
490
+ if mask.is_a?(Enumerable)
491
+ params[:mask] = mask.map do |locator|
492
+ locator.send(:to_protocol)
493
+ end
494
+ end
200
495
  encoded_binary = @channel.send_message_to_server('screenshot', params)
201
496
  decoded_binary = Base64.strict_decode64(encoded_binary)
202
497
  if path
@@ -211,13 +506,20 @@ module Playwright
211
506
  @main_frame.title
212
507
  end
213
508
 
214
- def close(runBeforeUnload: nil)
215
- options = { runBeforeUnload: runBeforeUnload }.compact
216
- @channel.send_message_to_server('close', options)
217
- @owned_context&.close
509
+ def close(runBeforeUnload: nil, reason: nil)
510
+ @close_reason = reason
511
+ unless runBeforeUnload
512
+ @close_was_called = true
513
+ end
514
+ if @owned_context
515
+ @owned_context.close
516
+ else
517
+ options = { runBeforeUnload: runBeforeUnload }.compact
518
+ @channel.send_message_to_server('close', options)
519
+ end
218
520
  nil
219
521
  rescue => err
220
- raise unless safe_close_error?(err)
522
+ raise if !target_closed_error?(err) || !runBeforeUnload
221
523
  end
222
524
 
223
525
  def closed?
@@ -233,7 +535,10 @@ module Playwright
233
535
  modifiers: nil,
234
536
  noWaitAfter: nil,
235
537
  position: nil,
236
- timeout: nil)
538
+ strict: nil,
539
+ timeout: nil,
540
+ trial: nil,
541
+ steps: nil)
237
542
 
238
543
  @main_frame.click(
239
544
  selector,
@@ -244,7 +549,36 @@ module Playwright
244
549
  modifiers: modifiers,
245
550
  noWaitAfter: noWaitAfter,
246
551
  position: position,
552
+ strict: strict,
247
553
  timeout: timeout,
554
+ trial: trial,
555
+ steps: steps,
556
+ )
557
+ end
558
+
559
+ def drag_and_drop(
560
+ source,
561
+ target,
562
+ force: nil,
563
+ noWaitAfter: nil,
564
+ sourcePosition: nil,
565
+ strict: nil,
566
+ targetPosition: nil,
567
+ timeout: nil,
568
+ trial: nil,
569
+ steps: nil)
570
+
571
+ @main_frame.drag_and_drop(
572
+ source,
573
+ target,
574
+ force: force,
575
+ noWaitAfter: noWaitAfter,
576
+ sourcePosition: sourcePosition,
577
+ strict: strict,
578
+ targetPosition: targetPosition,
579
+ timeout: timeout,
580
+ trial: trial,
581
+ steps: steps,
248
582
  )
249
583
  end
250
584
 
@@ -256,7 +590,10 @@ module Playwright
256
590
  modifiers: nil,
257
591
  noWaitAfter: nil,
258
592
  position: nil,
259
- timeout: nil)
593
+ strict: nil,
594
+ timeout: nil,
595
+ trial: nil,
596
+ steps: nil)
260
597
  @main_frame.dblclick(
261
598
  selector,
262
599
  button: button,
@@ -265,16 +602,157 @@ module Playwright
265
602
  modifiers: modifiers,
266
603
  noWaitAfter: noWaitAfter,
267
604
  position: position,
605
+ strict: strict,
606
+ timeout: timeout,
607
+ trial: trial,
608
+ steps: steps,
609
+ )
610
+ end
611
+
612
+ def tap_point(
613
+ selector,
614
+ force: nil,
615
+ modifiers: nil,
616
+ noWaitAfter: nil,
617
+ position: nil,
618
+ strict: nil,
619
+ timeout: nil,
620
+ trial: nil)
621
+ @main_frame.tap_point(
622
+ selector,
623
+ force: force,
624
+ modifiers: modifiers,
625
+ noWaitAfter: noWaitAfter,
626
+ position: position,
627
+ strict: strict,
628
+ timeout: timeout,
629
+ trial: trial,
630
+ )
631
+ end
632
+
633
+ def fill(
634
+ selector,
635
+ value,
636
+ force: nil,
637
+ noWaitAfter: nil,
638
+ strict: nil,
639
+ timeout: nil)
640
+ @main_frame.fill(
641
+ selector,
642
+ value,
643
+ force: force,
644
+ noWaitAfter: noWaitAfter,
645
+ strict: strict,
646
+ timeout: timeout)
647
+ end
648
+
649
+ def console_messages
650
+ messages = @channel.send_message_to_server('consoleMessages')
651
+ messages.map do |message|
652
+ ConsoleMessageImpl.new(message, self, nil)
653
+ end
654
+ end
655
+
656
+ def page_errors
657
+ errors = @channel.send_message_to_server('pageErrors')
658
+ errors.map do |error|
659
+ Error.parse(error['error'])
660
+ end
661
+ end
662
+
663
+ def locator(
664
+ selector,
665
+ has: nil,
666
+ hasNot: nil,
667
+ hasNotText: nil,
668
+ hasText: nil)
669
+ @main_frame.locator(
670
+ selector,
671
+ has: has,
672
+ hasNot: hasNot,
673
+ hasNotText: hasNotText,
674
+ hasText: hasText)
675
+ end
676
+
677
+ def frame_locator(selector)
678
+ @main_frame.frame_locator(selector)
679
+ end
680
+
681
+ def focus(selector, strict: nil, timeout: nil)
682
+ @main_frame.focus(selector, strict: strict, timeout: timeout)
683
+ end
684
+
685
+ def text_content(selector, strict: nil, timeout: nil)
686
+ @main_frame.text_content(selector, strict: strict, timeout: timeout)
687
+ end
688
+
689
+ def inner_text(selector, strict: nil, timeout: nil)
690
+ @main_frame.inner_text(selector, strict: strict, timeout: timeout)
691
+ end
692
+
693
+ def inner_html(selector, strict: nil, timeout: nil)
694
+ @main_frame.inner_html(selector, strict: strict, timeout: timeout)
695
+ end
696
+
697
+ def get_attribute(selector, name, strict: nil, timeout: nil)
698
+ @main_frame.get_attribute(selector, name, strict: strict, timeout: timeout)
699
+ end
700
+
701
+ def hover(
702
+ selector,
703
+ force: nil,
704
+ modifiers: nil,
705
+ noWaitAfter: nil,
706
+ position: nil,
707
+ strict: nil,
708
+ timeout: nil,
709
+ trial: nil)
710
+ @main_frame.hover(
711
+ selector,
712
+ force: force,
713
+ modifiers: modifiers,
714
+ noWaitAfter: noWaitAfter,
715
+ position: position,
716
+ strict: strict,
717
+ timeout: timeout,
718
+ trial: trial,
719
+ )
720
+ end
721
+
722
+ def select_option(
723
+ selector,
724
+ element: nil,
725
+ index: nil,
726
+ value: nil,
727
+ label: nil,
728
+ force: nil,
729
+ noWaitAfter: nil,
730
+ strict: nil,
731
+ timeout: nil)
732
+ @main_frame.select_option(
733
+ selector,
734
+ element: element,
735
+ index: index,
736
+ value: value,
737
+ label: label,
738
+ force: force,
739
+ noWaitAfter: noWaitAfter,
740
+ strict: strict,
268
741
  timeout: timeout,
269
742
  )
270
743
  end
271
744
 
272
- def fill(selector, value, noWaitAfter: nil, timeout: nil)
273
- @main_frame.fill(selector, value, noWaitAfter: noWaitAfter, timeout: timeout)
745
+ def input_value(selector, strict: nil, timeout: nil)
746
+ @main_frame.input_value(selector, strict: strict, timeout: timeout)
274
747
  end
275
748
 
276
- def focus(selector, timeout: nil)
277
- @main_frame.focus(selector, timeout: timeout)
749
+ def set_input_files(selector, files, noWaitAfter: nil, strict: nil,timeout: nil)
750
+ @main_frame.set_input_files(
751
+ selector,
752
+ files,
753
+ noWaitAfter: noWaitAfter,
754
+ strict: strict,
755
+ timeout: timeout)
278
756
  end
279
757
 
280
758
  def type(
@@ -282,9 +760,16 @@ module Playwright
282
760
  text,
283
761
  delay: nil,
284
762
  noWaitAfter: nil,
763
+ strict: nil,
285
764
  timeout: nil)
286
765
 
287
- @main_frame.type(selector, text, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
766
+ @main_frame.type(
767
+ selector,
768
+ text,
769
+ delay: delay,
770
+ noWaitAfter: noWaitAfter,
771
+ strict: strict,
772
+ timeout: timeout)
288
773
  end
289
774
 
290
775
  def press(
@@ -292,24 +777,185 @@ module Playwright
292
777
  key,
293
778
  delay: nil,
294
779
  noWaitAfter: nil,
780
+ strict: nil,
295
781
  timeout: nil)
296
782
 
297
- @main_frame.press(selector, key, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
783
+ @main_frame.press(
784
+ selector,
785
+ key,
786
+ delay: delay,
787
+ noWaitAfter: noWaitAfter,
788
+ strict: strict,
789
+ timeout: timeout)
790
+ end
791
+
792
+ def check(
793
+ selector,
794
+ force: nil,
795
+ noWaitAfter: nil,
796
+ position: nil,
797
+ strict: nil,
798
+ timeout: nil,
799
+ trial: nil)
800
+
801
+ @main_frame.check(
802
+ selector,
803
+ force: force,
804
+ noWaitAfter: noWaitAfter,
805
+ position: position,
806
+ strict: strict,
807
+ timeout: timeout,
808
+ trial: trial)
809
+ end
810
+
811
+ def uncheck(
812
+ selector,
813
+ force: nil,
814
+ noWaitAfter: nil,
815
+ position: nil,
816
+ strict: nil,
817
+ timeout: nil,
818
+ trial: nil)
819
+
820
+ @main_frame.uncheck(
821
+ selector,
822
+ force: force,
823
+ noWaitAfter: noWaitAfter,
824
+ position: position,
825
+ strict: strict,
826
+ timeout: timeout,
827
+ trial: trial)
828
+ end
829
+
830
+ def set_checked(selector, checked, **options)
831
+ if checked
832
+ check(selector, **options)
833
+ else
834
+ uncheck(selector, **options)
835
+ end
836
+ end
837
+
838
+ def wait_for_timeout(timeout)
839
+ @main_frame.wait_for_timeout(timeout)
298
840
  end
299
841
 
300
842
  def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
301
843
  @main_frame.wait_for_function(pageFunction, arg: arg, polling: polling, timeout: timeout)
302
844
  end
303
845
 
304
- class CrashedError < StandardError
305
- def initialize
306
- super('Page crashed')
846
+ def workers
847
+ @workers.to_a
848
+ end
849
+
850
+ def requests
851
+ @channel.send_message_to_server('requests').map do |req|
852
+ ChannelOwners::Request.from(req)
307
853
  end
308
854
  end
309
855
 
310
- class AlreadyClosedError < StandardError
856
+ def request
857
+ @browser_context.request
858
+ end
859
+
860
+ def pause
861
+ @browser_context.send(:pause)
862
+ end
863
+
864
+ def pdf(
865
+ displayHeaderFooter: nil,
866
+ footerTemplate: nil,
867
+ format: nil,
868
+ headerTemplate: nil,
869
+ height: nil,
870
+ landscape: nil,
871
+ margin: nil,
872
+ pageRanges: nil,
873
+ path: nil,
874
+ preferCSSPageSize: nil,
875
+ printBackground: nil,
876
+ scale: nil,
877
+ width: nil,
878
+ tagged: nil,
879
+ outline: nil)
880
+
881
+ params = {
882
+ displayHeaderFooter: displayHeaderFooter,
883
+ footerTemplate: footerTemplate,
884
+ format: format,
885
+ headerTemplate: headerTemplate,
886
+ height: height,
887
+ landscape: landscape,
888
+ margin: margin,
889
+ pageRanges: pageRanges,
890
+ preferCSSPageSize: preferCSSPageSize,
891
+ printBackground: printBackground,
892
+ scale: scale,
893
+ width: width,
894
+ tagged: tagged,
895
+ outline: outline,
896
+ }.compact
897
+ encoded_binary = @channel.send_message_to_server('pdf', params)
898
+ decoded_binary = Base64.strict_decode64(encoded_binary)
899
+ if path
900
+ File.open(path, 'wb') do |f|
901
+ f.write(decoded_binary)
902
+ end
903
+ end
904
+ decoded_binary
905
+ end
906
+
907
+ def video
908
+ return nil unless @browser_context.send(:has_record_video_option?)
909
+ @video ||= Video.new(self)
910
+ end
911
+
912
+ def snapshot_for_ai(timeout: nil, mode: nil, track: nil)
913
+ option_mode = mode || 'full'
914
+ unless ['full', 'incremental'].include?(option_mode)
915
+ raise ArgumentError.new("mode must be either 'full' or 'incremental'")
916
+ end
917
+
918
+ options = {
919
+ timeout: @timeout_settings.timeout(timeout),
920
+ mode: option_mode,
921
+ }
922
+ options[:track] = track if track
923
+ result = @channel.send_message_to_server_result('snapshotForAI', options)
924
+ if option_mode == 'full'
925
+ result['full']
926
+ elsif option_mode == 'incremental'
927
+ result['incremental']
928
+ end
929
+ end
930
+
931
+ def start_js_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
932
+ params = {
933
+ resetOnNavigation: resetOnNavigation,
934
+ reportAnonymousScripts: reportAnonymousScripts,
935
+ }.compact
936
+
937
+ @channel.send_message_to_server('startJSCoverage', params)
938
+ end
939
+
940
+ def stop_js_coverage
941
+ @channel.send_message_to_server('stopJSCoverage')
942
+ end
943
+
944
+ def start_css_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
945
+ params = {
946
+ resetOnNavigation: resetOnNavigation,
947
+ }.compact
948
+
949
+ @channel.send_message_to_server('startCSSCoverage', params)
950
+ end
951
+
952
+ def stop_css_coverage
953
+ @channel.send_message_to_server('stopCSSCoverage')
954
+ end
955
+
956
+ class CrashedError < StandardError
311
957
  def initialize
312
- super('Page closed')
958
+ super('Page crashed')
313
959
  end
314
960
  end
315
961
 
@@ -319,34 +965,40 @@ module Playwright
319
965
  end
320
966
  end
321
967
 
322
- def expect_event(event, optionsOrPredicate: nil, &block)
323
- predicate, timeout =
324
- case optionsOrPredicate
325
- when Proc
326
- [optionsOrPredicate, nil]
327
- when Hash
328
- [optionsOrPredicate[:predicate], optionsOrPredicate[:timeout]]
329
- else
330
- [nil, nil]
331
- end
332
- timeout ||= @timeout_settings.timeout
968
+ private def close_error_with_reason
969
+ reason = @close_reason || @browser_context.send(:effective_close_reason)
970
+ TargetClosedError.new(message: reason)
971
+ end
333
972
 
334
- wait_helper = WaitHelper.new
335
- wait_helper.reject_on_timeout(timeout, "Timeout while waiting for event \"#{event}\"")
973
+ def expect_event(event, predicate: nil, timeout: nil, &block)
974
+ waiter = Waiter.new(self, wait_name: "Page.expect_event(#{event})")
975
+ timeout_value = timeout || @timeout_settings.timeout
976
+ waiter.reject_on_timeout(timeout_value, "Timeout #{timeout_value}ms exceeded while waiting for event \"#{event}\"")
336
977
 
337
978
  unless event == Events::Page::Crash
338
- wait_helper.reject_on_event(self, Events::Page::Crash, CrashedError.new)
979
+ waiter.reject_on_event(self, Events::Page::Crash, CrashedError.new)
339
980
  end
340
981
 
341
982
  unless event == Events::Page::Close
342
- wait_helper.reject_on_event(self, Events::Page::Close, AlreadyClosedError.new)
983
+ waiter.reject_on_event(self, Events::Page::Close, -> { close_error_with_reason })
343
984
  end
344
985
 
345
- wait_helper.wait_for_event(self, event, predicate: predicate)
346
-
986
+ waiter.wait_for_event(self, event, predicate: predicate)
347
987
  block&.call
348
988
 
349
- wait_helper.promise.value!
989
+ waiter.result.value!
990
+ end
991
+
992
+ def expect_console_message(predicate: nil, timeout: nil, &block)
993
+ expect_event(Events::Page::Console, predicate: predicate, timeout: timeout, &block)
994
+ end
995
+
996
+ def expect_download(predicate: nil, timeout: nil, &block)
997
+ expect_event(Events::Page::Download, predicate: predicate, timeout: timeout, &block)
998
+ end
999
+
1000
+ def expect_file_chooser(predicate: nil, timeout: nil, &block)
1001
+ expect_event(Events::Page::FileChooser, predicate: predicate, timeout: timeout, &block)
350
1002
  end
351
1003
 
352
1004
  def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
@@ -357,11 +1009,15 @@ module Playwright
357
1009
  &block)
358
1010
  end
359
1011
 
360
- def expect_request(urlOrPredicate, timeout: nil)
1012
+ def expect_popup(predicate: nil, timeout: nil, &block)
1013
+ expect_event(Events::Page::Popup, predicate: predicate, timeout: timeout, &block)
1014
+ end
1015
+
1016
+ def expect_request(urlOrPredicate, timeout: nil, &block)
361
1017
  predicate =
362
1018
  case urlOrPredicate
363
1019
  when String, Regexp
364
- url_matcher = UrlMatcher.new(urlOrPredicate)
1020
+ url_matcher = UrlMatcher.new(urlOrPredicate, base_url: @browser_context.send(:base_url))
365
1021
  -> (req){ url_matcher.match?(req.url) }
366
1022
  when Proc
367
1023
  urlOrPredicate
@@ -369,14 +1025,18 @@ module Playwright
369
1025
  -> (_) { true }
370
1026
  end
371
1027
 
372
- expect_event(Events::Page::Request, optionsOrPredicate: { predicate: predicate, timeout: timeout})
1028
+ expect_event(Events::Page::Request, predicate: predicate, timeout: timeout, &block)
373
1029
  end
374
1030
 
375
- def expect_response(urlOrPredicate, timeout: nil)
1031
+ def expect_request_finished(predicate: nil, timeout: nil, &block)
1032
+ expect_event(Events::Page::RequestFinished, predicate: predicate, timeout: timeout, &block)
1033
+ end
1034
+
1035
+ def expect_response(urlOrPredicate, timeout: nil, &block)
376
1036
  predicate =
377
1037
  case urlOrPredicate
378
1038
  when String, Regexp
379
- url_matcher = UrlMatcher.new(urlOrPredicate)
1039
+ url_matcher = UrlMatcher.new(urlOrPredicate, base_url: @browser_context.send(:base_url))
380
1040
  -> (req){ url_matcher.match?(req.url) }
381
1041
  when Proc
382
1042
  urlOrPredicate
@@ -384,18 +1044,41 @@ module Playwright
384
1044
  -> (_) { true }
385
1045
  end
386
1046
 
387
- expect_event(Events::Page::Response, optionsOrPredicate: { predicate: predicate, timeout: timeout})
1047
+ expect_event(Events::Page::Response, predicate: predicate, timeout: timeout, &block)
388
1048
  end
389
1049
 
390
- # called from BrowserContext#on_page with send(:update_browser_context, page), so keep private.
391
- private def update_browser_context(context)
392
- @browser_context = context
393
- @timeout_settings = TimeoutSettings.new(context.send(:_timeout_settings))
1050
+ def expect_websocket(predicate: nil, timeout: nil, &block)
1051
+ expect_event(Events::Page::WebSocket, predicate: predicate, timeout: timeout, &block)
394
1052
  end
395
1053
 
396
- # called from Frame with send(:timeout_settings)
397
- private def timeout_settings
1054
+ def expect_worker(predicate: nil, timeout: nil, &block)
1055
+ expect_event(Events::Page::Worker, predicate: predicate, timeout: timeout, &block)
1056
+ end
1057
+
1058
+ # called from Frame with send(:_timeout_settings)
1059
+ private def _timeout_settings
398
1060
  @timeout_settings
399
1061
  end
1062
+
1063
+ # called from BrowserContext#expose_binding
1064
+ private def has_bindings?(name)
1065
+ @bindings.key?(name)
1066
+ end
1067
+
1068
+ # called from Worker#on_close
1069
+ private def remove_worker(worker)
1070
+ @workers.delete(worker)
1071
+ end
1072
+
1073
+ # called from Video
1074
+ private def remote_connection?
1075
+ @connection.remote?
1076
+ end
1077
+
1078
+ # Expose guid for library developers.
1079
+ # Not intended to be used by users.
1080
+ def guid
1081
+ @guid
1082
+ end
400
1083
  end
401
1084
  end