playwright-ruby-client 0.2.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -5
  3. data/docs/api_coverage.md +116 -74
  4. data/lib/playwright.rb +48 -9
  5. data/lib/playwright/channel.rb +12 -2
  6. data/lib/playwright/channel_owners/artifact.rb +30 -0
  7. data/lib/playwright/channel_owners/binding_call.rb +3 -0
  8. data/lib/playwright/channel_owners/browser.rb +21 -0
  9. data/lib/playwright/channel_owners/browser_context.rb +154 -3
  10. data/lib/playwright/channel_owners/browser_type.rb +28 -0
  11. data/lib/playwright/channel_owners/dialog.rb +28 -0
  12. data/lib/playwright/channel_owners/element_handle.rb +1 -1
  13. data/lib/playwright/channel_owners/frame.rb +25 -2
  14. data/lib/playwright/channel_owners/js_handle.rb +2 -2
  15. data/lib/playwright/channel_owners/page.rb +141 -25
  16. data/lib/playwright/channel_owners/playwright.rb +24 -27
  17. data/lib/playwright/channel_owners/request.rb +26 -2
  18. data/lib/playwright/channel_owners/response.rb +60 -0
  19. data/lib/playwright/channel_owners/route.rb +78 -0
  20. data/lib/playwright/channel_owners/selectors.rb +19 -1
  21. data/lib/playwright/channel_owners/stream.rb +15 -0
  22. data/lib/playwright/connection.rb +11 -32
  23. data/lib/playwright/download.rb +27 -0
  24. data/lib/playwright/errors.rb +6 -0
  25. data/lib/playwright/events.rb +2 -5
  26. data/lib/playwright/keyboard_impl.rb +1 -1
  27. data/lib/playwright/mouse_impl.rb +41 -0
  28. data/lib/playwright/route_handler_entry.rb +28 -0
  29. data/lib/playwright/transport.rb +27 -6
  30. data/lib/playwright/url_matcher.rb +1 -1
  31. data/lib/playwright/utils.rb +9 -0
  32. data/lib/playwright/version.rb +1 -1
  33. data/lib/playwright/video.rb +51 -0
  34. data/lib/playwright/wait_helper.rb +2 -2
  35. data/lib/playwright_api/accessibility.rb +39 -1
  36. data/lib/playwright_api/android.rb +74 -2
  37. data/lib/playwright_api/android_device.rb +141 -23
  38. data/lib/playwright_api/android_input.rb +17 -13
  39. data/lib/playwright_api/android_socket.rb +16 -0
  40. data/lib/playwright_api/android_web_view.rb +21 -0
  41. data/lib/playwright_api/browser.rb +77 -2
  42. data/lib/playwright_api/browser_context.rb +182 -29
  43. data/lib/playwright_api/browser_type.rb +40 -9
  44. data/lib/playwright_api/dialog.rb +54 -7
  45. data/lib/playwright_api/element_handle.rb +105 -31
  46. data/lib/playwright_api/file_chooser.rb +6 -1
  47. data/lib/playwright_api/frame.rb +229 -36
  48. data/lib/playwright_api/js_handle.rb +23 -0
  49. data/lib/playwright_api/keyboard.rb +48 -1
  50. data/lib/playwright_api/mouse.rb +26 -5
  51. data/lib/playwright_api/page.rb +491 -81
  52. data/lib/playwright_api/playwright.rb +21 -4
  53. data/lib/playwright_api/request.rb +30 -2
  54. data/lib/playwright_api/response.rb +21 -11
  55. data/lib/playwright_api/route.rb +51 -5
  56. data/lib/playwright_api/selectors.rb +27 -1
  57. data/lib/playwright_api/touchscreen.rb +1 -1
  58. data/lib/playwright_api/worker.rb +25 -1
  59. data/playwright.gemspec +4 -2
  60. metadata +42 -14
  61. data/lib/playwright/channel_owners/chromium_browser.rb +0 -8
  62. data/lib/playwright/channel_owners/chromium_browser_context.rb +0 -8
  63. data/lib/playwright/channel_owners/download.rb +0 -27
  64. data/lib/playwright/channel_owners/firefox_browser.rb +0 -8
  65. data/lib/playwright/channel_owners/webkit_browser.rb +0 -8
  66. data/lib/playwright_api/binding_call.rb +0 -27
  67. data/lib/playwright_api/chromium_browser_context.rb +0 -59
  68. data/lib/playwright_api/download.rb +0 -95
  69. data/lib/playwright_api/video.rb +0 -24
@@ -0,0 +1,30 @@
1
+ module Playwright
2
+ define_channel_owner :Artifact do
3
+ private def after_initialize
4
+ @is_remote = false
5
+ @absolute_path = @initializer['absolutePath']
6
+ end
7
+
8
+ attr_reader :absolute_path
9
+
10
+ def path_after_finished
11
+ if @is_remote
12
+ raise "Path is not available when using browser_type.connect(). Use save_as() to save a local copy."
13
+ end
14
+ @channel.send_message_to_server('pathAfterFinished')
15
+ end
16
+
17
+ def save_as(path)
18
+ stream = ChannelOwners::Stream.from(@channel.send_message_to_server('saveAsStream'))
19
+ stream.save_as(path)
20
+ end
21
+
22
+ def failure
23
+ @channel.send_message_to_server('failure')
24
+ end
25
+
26
+ def delete
27
+ @channel.send_message_to_server('delete')
28
+ end
29
+ end
30
+ end
@@ -1,4 +1,7 @@
1
1
  module Playwright
2
2
  define_channel_owner :BindingCall do
3
+ def name
4
+ @initializer['name']
5
+ end
3
6
  end
4
7
  end
@@ -59,12 +59,33 @@ module Playwright
59
59
  @initializer['version']
60
60
  end
61
61
 
62
+ def start_tracing(page: nil, categories: nil, path: nil, screenshots: nil)
63
+ params = {
64
+ page: page&.channel,
65
+ categories: categories,
66
+ path: path,
67
+ screenshots: screenshots,
68
+ }.compact
69
+
70
+ @channel.send_message_to_server('startTracing', params)
71
+ end
72
+
73
+ def stop_tracing
74
+ encoded_binary = @channel.send_message_to_server("stopTracing")
75
+ return Base64.strict_decode64(encoded_binary)
76
+ end
77
+
62
78
  private def on_close(_ = {})
63
79
  @connected = false
64
80
  emit(Events::Browser::Disconnected)
65
81
  @closed_or_closing = false
66
82
  end
67
83
 
84
+ # called from BrowserType#connectOverCDP
85
+ private def add_context(context)
86
+ @contexts << context
87
+ end
88
+
68
89
  # called from BrowserContext#on_close with send(:remove_context), so keep private.
69
90
  private def remove_context(context)
70
91
  @contexts.delete(context)
@@ -2,11 +2,16 @@ 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
4
  include Utils::Errors::SafeCloseError
5
- attr_writer :browser, :owner_page, :options
5
+ attr_accessor :browser
6
+ attr_writer :owner_page, :options
6
7
 
7
8
  private def after_initialize
8
9
  @pages = Set.new
10
+ @routes = Set.new
11
+ @bindings = {}
12
+ @timeout_settings = TimeoutSettings.new
9
13
 
14
+ @channel.on('bindingCall', ->(params) { on_binding(ChannelOwners::BindingCall.from(params['binding'])) })
10
15
  @channel.once('close', ->(_) { on_close })
11
16
  @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
12
17
  @channel.on('route', ->(params) {
@@ -18,11 +23,36 @@ module Playwright
18
23
  page.send(:update_browser_context, self)
19
24
  @pages << page
20
25
  emit(Events::BrowserContext::Page, page)
26
+ page.send(:emit_popup_event_from_browser_context)
21
27
  end
22
28
 
23
29
  private def on_route(route, request)
24
- # @routes.each ...
25
- route.continue_
30
+ # It is not desired to use PlaywrightApi.wrap directly.
31
+ # However it is a little difficult to define wrapper for `handler` parameter in generate_api.
32
+ # Just a workaround...
33
+ wrapped_route = PlaywrightApi.wrap(route)
34
+ wrapped_request = PlaywrightApi.wrap(request)
35
+
36
+ if @routes.none? { |handler_entry| handler_entry.handle(wrapped_route, wrapped_request) }
37
+ route.continue
38
+ end
39
+ end
40
+
41
+ private def on_binding(binding_call)
42
+ func = @binding[binding_call.name]
43
+ if func
44
+ binding_call.call(func)
45
+ end
46
+ end
47
+
48
+ def set_default_navigation_timeout(timeout)
49
+ @timeout_settings.default_navigation_timeout = timeout
50
+ @channel.send_message_to_server('setDefaultNavigationTimeoutNoReply', timeout: timeout)
51
+ end
52
+
53
+ def set_default_timeout(timeout)
54
+ @timeout_settings.default_timeout = timeout
55
+ @channel.send_message_to_server('setDefaultTimeoutNoReply', timeout: timeout)
26
56
  end
27
57
 
28
58
  def pages
@@ -36,6 +66,111 @@ module Playwright
36
66
  ChannelOwners::Page.from(resp)
37
67
  end
38
68
 
69
+ def cookies(urls: nil)
70
+ target_urls =
71
+ if urls.nil?
72
+ []
73
+ elsif urls.is_a?(Enumerable)
74
+ urls
75
+ else
76
+ [urls]
77
+ end
78
+ @channel.send_message_to_server('cookies', urls: urls)
79
+ end
80
+
81
+ def add_cookies(cookies)
82
+ @channel.send_message_to_server('addCookies', cookies: cookies)
83
+ end
84
+
85
+ def clear_cookies
86
+ @channel.send_message_to_server('clearCookies')
87
+ end
88
+
89
+ def grant_permissions(permissions, origin: nil)
90
+ params = {
91
+ permissions: permissions,
92
+ origin: origin,
93
+ }.compact
94
+ @channel.send_message_to_server('grantPermissions', params)
95
+ end
96
+
97
+ def clear_permissions
98
+ @channel.send_message_to_server('clearPermissions')
99
+ end
100
+
101
+ def set_geolocation(geolocation)
102
+ @channel.send_message_to_server('setGeolocation', geolocation: geolocation)
103
+ end
104
+
105
+ def set_extra_http_headers(headers)
106
+ @channel.send_message_to_server('setExtraHTTPHeaders',
107
+ headers: HttpHeaders.new(headers).as_serialized)
108
+ end
109
+
110
+ def set_offline(offline)
111
+ @channel.send_message_to_server('setOffline', offline: offline)
112
+ end
113
+
114
+ def add_init_script(path: nil, script: nil)
115
+ source =
116
+ if path
117
+ File.read(path)
118
+ elsif script
119
+ script
120
+ else
121
+ raise ArgumentError.new('Either path or script parameter must be specified')
122
+ end
123
+
124
+ @channel.send_message_to_server('addInitScript', source: script)
125
+ nil
126
+ end
127
+
128
+ def expose_binding(name, callback, handle: nil)
129
+ if @pages.any? { |page| page.send(:has_bindings?, name) }
130
+ raise ArgumentError.new("Function \"#{name}\" has been already registered in one of the pages")
131
+ end
132
+ if @bindings.key?(name)
133
+ raise ArgumentError.new("Function \"#{name}\" has been already registered")
134
+ end
135
+ params = {
136
+ name: name,
137
+ needsHandle: handle,
138
+ }.compact
139
+ @bindings[name] = callback
140
+ @channel.send_message_to_server('exposeBinding', params)
141
+ end
142
+
143
+ def expose_function(name, callback)
144
+ expose_binding(name, ->(_source, *args) { callback.call(*args) }, )
145
+ end
146
+
147
+ def route(url, handler)
148
+ entry = RouteHandlerEntry.new(url, handler)
149
+ @routes << entry
150
+ if @routes.count >= 1
151
+ @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: true)
152
+ end
153
+ end
154
+
155
+ def unroute(url, handler: nil)
156
+ @routes.reject! do |handler_entry|
157
+ handler_entry.same_value?(url: url, handler: handler)
158
+ end
159
+ if @routes.count == 0
160
+ @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
161
+ end
162
+ end
163
+
164
+ def expect_event(event, predicate: nil, timeout: nil, &block)
165
+ wait_helper = WaitHelper.new
166
+ wait_helper.reject_on_timeout(timeout || @timeout_settings.timeout, "Timeout while waiting for event \"#{event}\"")
167
+ wait_helper.wait_for_event(self, event, predicate: predicate)
168
+
169
+ block&.call
170
+
171
+ wait_helper.promise.value!
172
+ end
173
+
39
174
  private def on_close
40
175
  @closed_or_closing = true
41
176
  @browser&.send(:remove_context, self)
@@ -51,6 +186,18 @@ module Playwright
51
186
  raise unless safe_close_error?(err)
52
187
  end
53
188
 
189
+ def pause
190
+ @channel.send_message_to_server('pause')
191
+ end
192
+
193
+ def expect_page(predicate: nil, timeout: nil)
194
+ params = {
195
+ predicate: predicate,
196
+ timeout: timeout,
197
+ }.compact
198
+ expect_event(Events::BrowserContext::Page, params)
199
+ end
200
+
54
201
  # called from Page#on_close with send(:remove_page, page), so keep private
55
202
  private def remove_page(page)
56
203
  @pages.delete(page)
@@ -60,5 +207,9 @@ module Playwright
60
207
  private def _timeout_settings
61
208
  @timeout_settings
62
209
  end
210
+
211
+ private def has_record_video_option?
212
+ @options.key?(:recordVideo)
213
+ end
63
214
  end
64
215
  end
@@ -22,5 +22,33 @@ module Playwright
22
22
  browser
23
23
  end
24
24
  end
25
+
26
+ def connect_over_cdp(endpointURL, slowMo: nil, timeout: nil, &block)
27
+ raise 'Connecting over CDP is only supported in Chromium.' unless name == 'chromium'
28
+
29
+ params = {
30
+ sdkLanguage: 'ruby',
31
+ endpointURL: endpointURL,
32
+ slowMo: slowMo,
33
+ timeout: timeout,
34
+ }.compact
35
+ result = @channel.send_message_to_server_result('connectOverCDP', params)
36
+ browser = ChannelOwners::Browser.from(result['browser'])
37
+
38
+ if result['defaultContext']
39
+ context = ChannelOwners::BrowserContext.from(result['defaultContext'])
40
+ browser.send(:add_context, context)
41
+ end
42
+
43
+ if block
44
+ begin
45
+ block.call(browser)
46
+ ensure
47
+ browser.close
48
+ end
49
+ else
50
+ browser
51
+ end
52
+ end
25
53
  end
26
54
  end
@@ -0,0 +1,28 @@
1
+ module Playwright
2
+ define_channel_owner :Dialog do
3
+ def type
4
+ @initializer['type']
5
+ end
6
+
7
+ def message
8
+ @initializer['message']
9
+ end
10
+
11
+ def default_value
12
+ @initializer['defaultValue']
13
+ end
14
+
15
+ def accept(promptText: nil)
16
+ accept_async(prompt_text: promptText).value!
17
+ end
18
+
19
+ def accept_async(promptText: nil)
20
+ params = { promptText: promptText }.compact
21
+ @channel.async_send_message_to_server('accept', params)
22
+ end
23
+
24
+ def dismiss
25
+ @channel.async_send_message_to_server('dismiss')
26
+ end
27
+ end
28
+ end
@@ -179,7 +179,7 @@ module Playwright
179
179
  value: value,
180
180
  noWaitAfter: noWaitAfter,
181
181
  timeout: timeout,
182
- }
182
+ }.compact
183
183
  @channel.send_message_to_server('fill', params)
184
184
 
185
185
  nil
@@ -98,6 +98,15 @@ module Playwright
98
98
  request&.response
99
99
  end
100
100
 
101
+ def wait_for_url(url, timeout: nil, waitUntil: nil)
102
+ matcher = UrlMatcher.new(url)
103
+ if matcher.match?(@url)
104
+ wait_for_load_state(state: waitUntil, timeout: timeout)
105
+ else
106
+ expect_navigation(timeout: timeout, url: url, waitUntil: waitUntil)
107
+ end
108
+ end
109
+
101
110
  def wait_for_load_state(state: nil, timeout: nil)
102
111
  option_state = state || 'load'
103
112
  option_timeout = timeout || @page.send(:timeout_settings).navigation_timeout
@@ -464,11 +473,18 @@ module Playwright
464
473
  nil
465
474
  end
466
475
 
467
- def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
476
+ def check(
477
+ selector,
478
+ force: nil,
479
+ noWaitAfter: nil,
480
+ position: nil,
481
+ timeout: nil)
482
+
468
483
  params = {
469
484
  selector: selector,
470
485
  force: force,
471
486
  noWaitAfter: noWaitAfter,
487
+ position: position,
472
488
  timeout: timeout,
473
489
  }.compact
474
490
  @channel.send_message_to_server('check', params)
@@ -476,11 +492,18 @@ module Playwright
476
492
  nil
477
493
  end
478
494
 
479
- def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
495
+ def uncheck(
496
+ selector,
497
+ force: nil,
498
+ noWaitAfter: nil,
499
+ position: nil,
500
+ timeout: nil)
501
+
480
502
  params = {
481
503
  selector: selector,
482
504
  force: force,
483
505
  noWaitAfter: noWaitAfter,
506
+ position: position,
484
507
  timeout: timeout,
485
508
  }.compact
486
509
  @channel.send_message_to_server('uncheck', params)
@@ -9,8 +9,8 @@ module Playwright
9
9
  @preview
10
10
  end
11
11
 
12
- private def on_preview_updated(preview)
13
- @preview = preview
12
+ private def on_preview_updated(params)
13
+ @preview = params['preview']
14
14
  end
15
15
 
16
16
  def evaluate(pageFunction, arg: nil)
@@ -14,12 +14,21 @@ module Playwright
14
14
  @mouse = MouseImpl.new(@channel)
15
15
  @touchscreen = TouchscreenImpl.new(@channel)
16
16
 
17
- @viewport_size = @initializer['viewportSize']
17
+ if @initializer['viewportSize']
18
+ @viewport_size = {
19
+ width: @initializer['viewportSize']['width'],
20
+ height: @initializer['viewportSize']['height'],
21
+ }
22
+ end
18
23
  @closed = false
24
+ @bindings = {}
25
+ @routes = Set.new
26
+
19
27
  @main_frame = ChannelOwners::Frame.from(@initializer['mainFrame'])
20
28
  @main_frame.send(:update_page_from_page, self)
21
29
  @frames = Set.new
22
30
  @frames << @main_frame
31
+ @opener = ChannelOwners::Page.from_nullable(@initializer['opener'])
23
32
 
24
33
  @channel.once('close', ->(_) { on_close })
25
34
  @channel.on('console', ->(params) {
@@ -29,9 +38,7 @@ module Playwright
29
38
  @channel.on('crash', ->(_) { emit(Events::Page::Crash) })
30
39
  @channel.on('dialog', method(:on_dialog))
31
40
  @channel.on('domcontentloaded', ->(_) { emit(Events::Page::DOMContentLoaded) })
32
- @channel.on('download', ->(params) {
33
- emit(Events::Page::Download, ChannelOwners::Download.from(params['download']))
34
- })
41
+ @channel.on('download', method(:on_download))
35
42
  @channel.on('fileChooser', ->(params) {
36
43
  chooser = FileChooserImpl.new(
37
44
  page: self,
@@ -49,9 +56,6 @@ module Playwright
49
56
  @channel.on('pageError', ->(params) {
50
57
  emit(Events::Page::PageError, Error.parse(params['error']['error']))
51
58
  })
52
- @channel.on('popup', ->(params) {
53
- emit(Events::Page::Popup, ChannelOwners::Page.from(params['page']))
54
- })
55
59
  @channel.on('request', ->(params) {
56
60
  emit(Events::Page::Request, ChannelOwners::Request.from(params['request']))
57
61
  })
@@ -74,9 +78,7 @@ module Playwright
74
78
  @channel.on('route', ->(params) {
75
79
  on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
76
80
  })
77
- @channel.on('video', ->(params) {
78
- video.send(:update_relative_path, params['relativePath'])
79
- })
81
+ @channel.on('video', method(:on_video))
80
82
  @channel.on('webSocket', ->(params) {
81
83
  emit(Events::Page::WebSocket, ChannelOwners::WebSocket.from(params['webSocket']))
82
84
  })
@@ -96,12 +98,12 @@ module Playwright
96
98
  private def on_request_failed(request, response_end_timing, failure_text)
97
99
  request.send(:update_failure_text, failure_text)
98
100
  request.send(:update_response_end_timing, response_end_timing)
99
- emit(Events::Page::RequestFailed)
101
+ emit(Events::Page::RequestFailed, request)
100
102
  end
101
103
 
102
104
  private def on_request_finished(request, response_end_timing)
103
105
  request.send(:update_response_end_timing, response_end_timing)
104
- emit(Events::Page::RequestFinished)
106
+ emit(Events::Page::RequestFinished, request)
105
107
  end
106
108
 
107
109
  private def on_frame_attached(frame)
@@ -117,8 +119,15 @@ module Playwright
117
119
  end
118
120
 
119
121
  private def on_route(route, request)
120
- # @routes.each ...
121
- @browser_context.send(:on_route, route, request)
122
+ # It is not desired to use PlaywrightApi.wrap directly.
123
+ # However it is a little difficult to define wrapper for `handler` parameter in generate_api.
124
+ # Just a workaround...
125
+ wrapped_route = PlaywrightApi.wrap(route)
126
+ wrapped_request = PlaywrightApi.wrap(request)
127
+
128
+ if @routes.none? { |handler_entry| handler_entry.handle(wrapped_route, wrapped_request) }
129
+ @browser_context.send(:on_route, route, request)
130
+ end
122
131
  end
123
132
 
124
133
  private def on_close
@@ -130,14 +139,28 @@ module Playwright
130
139
  private def on_dialog(params)
131
140
  dialog = ChannelOwners::Dialog.from(params['dialog'])
132
141
  unless emit(Events::Page::Dialog, dialog)
133
- dialog.dismiss # FIXME: this should be asynchronous
142
+ dialog.dismiss
134
143
  end
135
144
  end
136
145
 
146
+ private def on_download(params)
147
+ download = Download.new(
148
+ url: params['url'],
149
+ suggested_filename: params['suggestedFilename'],
150
+ artifact: ChannelOwners::Artifact.from(params['artifact']),
151
+ )
152
+ emit(Events::Page::Download, download)
153
+ end
154
+
155
+ private def on_video(params)
156
+ artifact = ChannelOwners::Artifact.from(params['artifact'])
157
+ video.send(:set_artifact, artifact)
158
+ end
159
+
137
160
  # @override
138
161
  def on(event, callback)
139
162
  if event == Events::Page::FileChooser && listener_count(event) == 0
140
- @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: true)
163
+ @channel.async_send_message_to_server('setFileChooserInterceptedNoReply', intercepted: true)
141
164
  end
142
165
  super
143
166
  end
@@ -145,7 +168,7 @@ module Playwright
145
168
  # @override
146
169
  def once(event, callback)
147
170
  if event == Events::Page::FileChooser && listener_count(event) == 0
148
- @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: true)
171
+ @channel.async_send_message_to_server('setFileChooserInterceptedNoReply', intercepted: true)
149
172
  end
150
173
  super
151
174
  end
@@ -154,7 +177,7 @@ module Playwright
154
177
  def off(event, callback)
155
178
  super
156
179
  if event == Events::Page::FileChooser && listener_count(event) == 0
157
- @channel.send_no_reply('setFileChooserInterceptedNoReply', intercepted: false)
180
+ @channel.async_send_message_to_server('setFileChooserInterceptedNoReply', intercepted: false)
158
181
  end
159
182
  end
160
183
 
@@ -163,8 +186,17 @@ module Playwright
163
186
  end
164
187
 
165
188
  def opener
166
- resp = @channel.send_message_to_server('opener')
167
- ChannelOwners::Page.from(resp)
189
+ if @opener&.closed?
190
+ nil
191
+ else
192
+ @opener
193
+ end
194
+ end
195
+
196
+ private def emit_popup_event_from_browser_context
197
+ if @opener && !@opener.closed?
198
+ @opener.emit(Events::Page::Popup, self)
199
+ end
168
200
  end
169
201
 
170
202
  def frame(name: nil, url: nil)
@@ -311,6 +343,10 @@ module Playwright
311
343
  @main_frame.wait_for_load_state(state: state, timeout: timeout)
312
344
  end
313
345
 
346
+ def wait_for_url(url, timeout: nil, waitUntil: nil)
347
+ @main_frame.wait_for_url(url, timeout: timeout, waitUntil: waitUntil)
348
+ end
349
+
314
350
  def go_back(timeout: nil, waitUntil: nil)
315
351
  params = { timeout: timeout, waitUntil: waitUntil }.compact
316
352
  resp = @channel.send_message_to_server('goBack', params)
@@ -347,7 +383,7 @@ module Playwright
347
383
  def add_init_script(path: nil, script: nil)
348
384
  source =
349
385
  if path
350
- File.read(path, 'r')
386
+ File.read(path)
351
387
  elsif script
352
388
  script
353
389
  else
@@ -358,6 +394,23 @@ module Playwright
358
394
  nil
359
395
  end
360
396
 
397
+ def route(url, handler)
398
+ entry = RouteHandlerEntry.new(url, handler)
399
+ @routes << entry
400
+ if @routes.count >= 1
401
+ @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: true)
402
+ end
403
+ end
404
+
405
+ def unroute(url, handler: nil)
406
+ @routes.reject! do |handler_entry|
407
+ handler_entry.same_value?(url: url, handler: handler)
408
+ end
409
+ if @routes.count == 0
410
+ @channel.send_message_to_server('setNetworkInterceptionEnabled', enabled: false)
411
+ end
412
+ end
413
+
361
414
  def screenshot(
362
415
  path: nil,
363
416
  type: nil,
@@ -546,12 +599,24 @@ module Playwright
546
599
  @main_frame.press(selector, key, delay: delay, noWaitAfter: noWaitAfter, timeout: timeout)
547
600
  end
548
601
 
549
- def check(selector, force: nil, noWaitAfter: nil, timeout: nil)
550
- @main_frame.check(selector, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
602
+ def check(
603
+ selector,
604
+ force: nil,
605
+ noWaitAfter: nil,
606
+ position: nil,
607
+ timeout: nil)
608
+
609
+ @main_frame.check(selector, force: force, noWaitAfter: noWaitAfter, position: position, timeout: timeout)
551
610
  end
552
611
 
553
- def uncheck(selector, force: nil, noWaitAfter: nil, timeout: nil)
554
- @main_frame.uncheck(selector, force: force, noWaitAfter: noWaitAfter, timeout: timeout)
612
+ def uncheck(
613
+ selector,
614
+ force: nil,
615
+ noWaitAfter: nil,
616
+ position: nil,
617
+ timeout: nil)
618
+
619
+ @main_frame.uncheck(selector, force: force, noWaitAfter: noWaitAfter, position: position, timeout: timeout)
555
620
  end
556
621
 
557
622
  def wait_for_function(pageFunction, arg: nil, polling: nil, timeout: nil)
@@ -597,6 +662,36 @@ module Playwright
597
662
  decoded_binary
598
663
  end
599
664
 
665
+ def video
666
+ return nil unless @browser_context.send(:has_record_video_option?)
667
+ @video ||= Video.new(self)
668
+ end
669
+
670
+ def start_js_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
671
+ params = {
672
+ resetOnNavigation: resetOnNavigation,
673
+ reportAnonymousScripts: reportAnonymousScripts,
674
+ }.compact
675
+
676
+ @channel.send_message_to_server('startJSCoverage', params)
677
+ end
678
+
679
+ def stop_js_coverage
680
+ @channel.send_message_to_server('stopJSCoverage')
681
+ end
682
+
683
+ def start_css_coverage(resetOnNavigation: nil, reportAnonymousScripts: nil)
684
+ params = {
685
+ resetOnNavigation: resetOnNavigation,
686
+ }.compact
687
+
688
+ @channel.send_message_to_server('startCSSCoverage', params)
689
+ end
690
+
691
+ def stop_css_coverage
692
+ @channel.send_message_to_server('stopCSSCoverage')
693
+ end
694
+
600
695
  class CrashedError < StandardError
601
696
  def initialize
602
697
  super('Page crashed')
@@ -634,6 +729,18 @@ module Playwright
634
729
  wait_helper.promise.value!
635
730
  end
636
731
 
732
+ def expect_console_message(predicate: nil, timeout: nil, &block)
733
+ expect_event(Events::Page::Console, predicate: predicate, timeout: timeout, &block)
734
+ end
735
+
736
+ def expect_download(predicate: nil, timeout: nil, &block)
737
+ expect_event(Events::Page::Download, predicate: predicate, timeout: timeout, &block)
738
+ end
739
+
740
+ def expect_file_chooser(predicate: nil, timeout: nil, &block)
741
+ expect_event(Events::Page::FileChooser, predicate: predicate, timeout: timeout, &block)
742
+ end
743
+
637
744
  def expect_navigation(timeout: nil, url: nil, waitUntil: nil, &block)
638
745
  @main_frame.expect_navigation(
639
746
  timeout: timeout,
@@ -642,6 +749,10 @@ module Playwright
642
749
  &block)
643
750
  end
644
751
 
752
+ def expect_popup(predicate: nil, timeout: nil, &block)
753
+ expect_event(Events::Page::Popup, predicate: predicate, timeout: timeout, &block)
754
+ end
755
+
645
756
  def expect_request(urlOrPredicate, timeout: nil)
646
757
  predicate =
647
758
  case urlOrPredicate
@@ -682,5 +793,10 @@ module Playwright
682
793
  private def timeout_settings
683
794
  @timeout_settings
684
795
  end
796
+
797
+ # called from BrowserContext#expose_binding
798
+ private def has_bindings?(name)
799
+ @bindings.key?(name)
800
+ end
685
801
  end
686
802
  end