playwright-ruby-client 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +114 -2
  3. data/docs/api_coverage.md +351 -0
  4. data/lib/playwright.rb +7 -1
  5. data/lib/playwright/android_input_impl.rb +23 -0
  6. data/lib/playwright/api_implementation.rb +18 -0
  7. data/lib/playwright/channel.rb +7 -0
  8. data/lib/playwright/channel_owner.rb +3 -2
  9. data/lib/playwright/channel_owners/android.rb +10 -1
  10. data/lib/playwright/channel_owners/android_device.rb +163 -0
  11. data/lib/playwright/channel_owners/browser.rb +13 -13
  12. data/lib/playwright/channel_owners/browser_context.rb +9 -1
  13. data/lib/playwright/channel_owners/download.rb +27 -0
  14. data/lib/playwright/channel_owners/element_handle.rb +306 -0
  15. data/lib/playwright/channel_owners/frame.rb +371 -19
  16. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  17. data/lib/playwright/channel_owners/page.rb +416 -19
  18. data/lib/playwright/channel_owners/request.rb +98 -0
  19. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  20. data/lib/playwright/connection.rb +9 -6
  21. data/lib/playwright/errors.rb +2 -2
  22. data/lib/playwright/event_emitter.rb +8 -1
  23. data/lib/playwright/event_emitter_proxy.rb +49 -0
  24. data/lib/playwright/file_chooser_impl.rb +23 -0
  25. data/lib/playwright/http_headers.rb +20 -0
  26. data/lib/playwright/input_files.rb +42 -0
  27. data/lib/playwright/javascript/expression.rb +37 -0
  28. data/lib/playwright/javascript/function.rb +37 -0
  29. data/lib/playwright/javascript/value_parser.rb +1 -1
  30. data/lib/playwright/javascript/value_serializer.rb +11 -11
  31. data/lib/playwright/keyboard_impl.rb +36 -0
  32. data/lib/playwright/mouse_impl.rb +7 -0
  33. data/lib/playwright/playwright_api.rb +84 -29
  34. data/lib/playwright/select_option_values.rb +32 -0
  35. data/lib/playwright/timeout_settings.rb +2 -2
  36. data/lib/playwright/touchscreen_impl.rb +7 -0
  37. data/lib/playwright/url_matcher.rb +19 -0
  38. data/lib/playwright/utils.rb +18 -0
  39. data/lib/playwright/version.rb +1 -1
  40. data/lib/playwright/wait_helper.rb +1 -1
  41. data/lib/playwright_api/accessibility.rb +46 -6
  42. data/lib/playwright_api/android.rb +37 -0
  43. data/lib/playwright_api/android_device.rb +82 -0
  44. data/lib/playwright_api/android_input.rb +25 -0
  45. data/lib/playwright_api/binding_call.rb +10 -6
  46. data/lib/playwright_api/browser.rb +85 -18
  47. data/lib/playwright_api/browser_context.rb +269 -37
  48. data/lib/playwright_api/browser_type.rb +60 -11
  49. data/lib/playwright_api/cdp_session.rb +23 -1
  50. data/lib/playwright_api/chromium_browser_context.rb +18 -6
  51. data/lib/playwright_api/console_message.rb +14 -15
  52. data/lib/playwright_api/dialog.rb +48 -2
  53. data/lib/playwright_api/download.rb +47 -10
  54. data/lib/playwright_api/element_handle.rb +269 -110
  55. data/lib/playwright_api/file_chooser.rb +23 -7
  56. data/lib/playwright_api/frame.rb +439 -154
  57. data/lib/playwright_api/js_handle.rb +69 -24
  58. data/lib/playwright_api/keyboard.rb +99 -9
  59. data/lib/playwright_api/mouse.rb +22 -0
  60. data/lib/playwright_api/page.rb +856 -229
  61. data/lib/playwright_api/playwright.rb +108 -20
  62. data/lib/playwright_api/request.rb +77 -29
  63. data/lib/playwright_api/response.rb +10 -13
  64. data/lib/playwright_api/route.rb +49 -0
  65. data/lib/playwright_api/selectors.rb +20 -8
  66. data/lib/playwright_api/video.rb +8 -0
  67. data/lib/playwright_api/web_socket.rb +0 -8
  68. data/lib/playwright_api/worker.rb +25 -13
  69. data/playwright.gemspec +1 -0
  70. metadata +33 -2
data/lib/playwright.rb CHANGED
@@ -10,14 +10,20 @@ require 'concurrent'
10
10
  require 'playwright/errors'
11
11
  require 'playwright/events'
12
12
  require 'playwright/event_emitter'
13
+ require 'playwright/event_emitter_proxy'
13
14
  require 'playwright/javascript'
14
15
  require 'playwright/utils'
15
16
 
17
+ require 'playwright/api_implementation'
16
18
  require 'playwright/channel'
17
19
  require 'playwright/channel_owner'
20
+ require 'playwright/http_headers'
21
+ require 'playwright/input_files'
18
22
  require 'playwright/connection'
23
+ require 'playwright/select_option_values'
19
24
  require 'playwright/timeout_settings'
20
25
  require 'playwright/transport'
26
+ require 'playwright/url_matcher'
21
27
  require 'playwright/version'
22
28
  require 'playwright/wait_helper'
23
29
 
@@ -33,7 +39,7 @@ module Playwright
33
39
 
34
40
  playwright_promise = connection.async_wait_for_object_with_known_name('Playwright')
35
41
  Thread.new { connection.run }
36
- playwright = PlaywrightApi.from_channel_owner(playwright_promise.value!)
42
+ playwright = PlaywrightApi.wrap(playwright_promise.value!)
37
43
  begin
38
44
  block.call(playwright)
39
45
  ensure
@@ -0,0 +1,23 @@
1
+ module Playwright
2
+ define_api_implementation :AndroidInputImpl do
3
+ def initialize(channel)
4
+ @channel = channel
5
+ end
6
+
7
+ def type(text)
8
+ @channel.send_message_to_server('inputType', text: text)
9
+ end
10
+
11
+ def press(key)
12
+ @channel.send_message_to_server('inputPress', key: key)
13
+ end
14
+
15
+ def tap_point(point)
16
+ @channel.send_message_to_server('inputTap', point: point)
17
+ end
18
+
19
+ def drag(from, to, steps)
20
+ @channel.send_message_to_server('inputDrag', from: from, to: to, steps: steps)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Playwright
2
+ # Each Impl class include this module.
3
+ # Used for detecting whether the object is a XXXXImpl or not.
4
+ module ApiImplementation ; end
5
+
6
+ def self.define_api_implementation(class_name, &block)
7
+ klass = Class.new
8
+ klass.include(ApiImplementation)
9
+ klass.class_eval(&block) if block
10
+ if ::Playwright.const_defined?(class_name)
11
+ raise ArgumentError.new("Playwright::#{class_name} already exist. Choose another class name.")
12
+ end
13
+ ::Playwright.const_set(class_name, klass)
14
+ end
15
+ end
16
+
17
+ # load subclasses
18
+ Dir[File.join(__dir__, '*_impl.rb')].each { |f| require f }
@@ -24,5 +24,12 @@ module Playwright
24
24
  nil
25
25
  end
26
26
  end
27
+
28
+ # @param method [String]
29
+ # @param params [Hash]
30
+ def send_no_reply(method, params)
31
+ @connection.async_send_message_to_server(@guid, method, params)
32
+ nil
33
+ end
27
34
  end
28
35
  end
@@ -39,13 +39,14 @@ module Playwright
39
39
 
40
40
  attr_reader :channel
41
41
 
42
- def dispose
42
+ # used only from Connection. Not intended for public use. So keep private.
43
+ private def dispose!
43
44
  # Clean up from parent and connection.
44
45
  @parent&.send(:delete_object_from_child, @guid)
45
46
  @connection.send(:delete_object_from_channel_owner, @guid)
46
47
 
47
48
  # Dispose all children.
48
- @objects.each_value(&:dispose)
49
+ @objects.each_value { |object| object.send(:dispose!) }
49
50
  @objects.clear
50
51
  end
51
52
 
@@ -1,3 +1,12 @@
1
1
  module Playwright
2
- define_channel_owner :Android
2
+ define_channel_owner :Android do
3
+ def after_initialize
4
+ @timeout_settings = TimeoutSettings.new
5
+ end
6
+
7
+ def devices
8
+ resp = @channel.send_message_to_server('devices')
9
+ resp.map { |device| ChannelOwners::AndroidDevice.from(device) }
10
+ end
11
+ end
3
12
  end
@@ -0,0 +1,163 @@
1
+ module Playwright
2
+ define_channel_owner :AndroidDevice do
3
+ include Utils::PrepareBrowserContextOptions
4
+
5
+ def after_initialize
6
+ @input = AndroidInputImpl.new(@channel)
7
+ end
8
+
9
+ attr_reader :input
10
+
11
+ def serial
12
+ @initializer['serial']
13
+ end
14
+
15
+ def model
16
+ @initializer['model']
17
+ end
18
+
19
+ private def to_regex(value)
20
+ case value
21
+ when nil
22
+ nil
23
+ when Regexp
24
+ value
25
+ else
26
+ Regexp.new("^#{value}$")
27
+ end
28
+ end
29
+
30
+ private def to_selector_channel(selector)
31
+ {
32
+ checkable: selector[:checkable],
33
+ checked: selector[:checked],
34
+ clazz: to_regex(selector[:clazz]),
35
+ pkg: to_regex(selector[:pkg]),
36
+ desc: to_regex(selector[:desc]),
37
+ res: to_regex(selector[:res]),
38
+ text: to_regex(selector[:text]),
39
+ clickable: selector[:clickable],
40
+ depth: selector[:depth],
41
+ enabled: selector[:enabled],
42
+ focusable: selector[:focusable],
43
+ focused: selector[:focused],
44
+ hasChild: selector[:hasChild] ? { selector: to_selector_channel(selector[:hasChild][:selector]) } : nil,
45
+ hasDescendant: selector[:hasDescendant] ? {
46
+ selector: to_selector_channel(selector[:hasDescendant][:selector]),
47
+ maxDepth: selector[:hasDescendant][:maxDepth],
48
+ } : nil,
49
+ longClickable: selector[:longClickable],
50
+ scrollable: selector[:scrollable],
51
+ selected: selector[:selected],
52
+ }.compact
53
+ end
54
+
55
+ def tap_on(selector, duration: nil, timeout: nil)
56
+ params = {
57
+ selector: to_selector_channel(selector),
58
+ duration: duration,
59
+ timeout: timeout,
60
+ }.compact
61
+ @channel.send_message_to_server('tap', params)
62
+ end
63
+
64
+ def info(selector)
65
+ @channel.send_message_to_server('info', selector: to_selector_channel(selector))
66
+ end
67
+
68
+ def tree
69
+ @channel.send_message_to_server('tree')
70
+ end
71
+
72
+ def screenshot(path: nil)
73
+ encoded_binary = @channel.send_message_to_server('screenshot')
74
+ decoded_binary = Base64.strict_decode64(encoded_binary)
75
+ if path
76
+ File.open(path, 'wb') do |f|
77
+ f.write(decoded_binary)
78
+ end
79
+ end
80
+ decoded_binary
81
+ end
82
+
83
+ def close
84
+ @channel.send_message_to_server('close')
85
+ emit(Events::AndroidDevice::Close)
86
+ end
87
+
88
+ def shell(command)
89
+ resp = @channel.send_message_to_server('shell', command: command)
90
+ Base64.strict_decode64(resp)
91
+ end
92
+
93
+ def launch_browser(
94
+ pkg: nil,
95
+ acceptDownloads: nil,
96
+ bypassCSP: nil,
97
+ colorScheme: nil,
98
+ deviceScaleFactor: nil,
99
+ extraHTTPHeaders: nil,
100
+ geolocation: nil,
101
+ hasTouch: nil,
102
+ httpCredentials: nil,
103
+ ignoreHTTPSErrors: nil,
104
+ isMobile: nil,
105
+ javaScriptEnabled: nil,
106
+ locale: nil,
107
+ logger: nil,
108
+ offline: nil,
109
+ permissions: nil,
110
+ proxy: nil,
111
+ recordHar: nil,
112
+ recordVideo: nil,
113
+ storageState: nil,
114
+ timezoneId: nil,
115
+ userAgent: nil,
116
+ videoSize: nil,
117
+ videosPath: nil,
118
+ viewport: nil,
119
+ &block)
120
+ params = {
121
+ pkg: pkg,
122
+ acceptDownloads: acceptDownloads,
123
+ bypassCSP: bypassCSP,
124
+ colorScheme: colorScheme,
125
+ deviceScaleFactor: deviceScaleFactor,
126
+ extraHTTPHeaders: extraHTTPHeaders,
127
+ geolocation: geolocation,
128
+ hasTouch: hasTouch,
129
+ httpCredentials: httpCredentials,
130
+ ignoreHTTPSErrors: ignoreHTTPSErrors,
131
+ isMobile: isMobile,
132
+ javaScriptEnabled: javaScriptEnabled,
133
+ locale: locale,
134
+ logger: logger,
135
+ offline: offline,
136
+ permissions: permissions,
137
+ proxy: proxy,
138
+ recordHar: recordHar,
139
+ recordVideo: recordVideo,
140
+ storageState: storageState,
141
+ timezoneId: timezoneId,
142
+ userAgent: userAgent,
143
+ videoSize: videoSize,
144
+ videosPath: videosPath,
145
+ viewport: viewport,
146
+ }.compact
147
+ prepare_browser_context_options(params)
148
+
149
+ resp = @channel.send_message_to_server('launchBrowser', params)
150
+ context = ChannelOwners::BrowserContext.from(resp)
151
+
152
+ if block
153
+ begin
154
+ block.call(context)
155
+ ensure
156
+ context.close
157
+ end
158
+ else
159
+ context
160
+ end
161
+ end
162
+ end
163
+ end
@@ -2,6 +2,7 @@ module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser.py
3
3
  define_channel_owner :Browser do
4
4
  include Utils::Errors::SafeCloseError
5
+ include Utils::PrepareBrowserContextOptions
5
6
 
6
7
  def after_initialize
7
8
  @contexts = Set.new
@@ -16,26 +17,25 @@ module Playwright
16
17
  @connected
17
18
  end
18
19
 
19
- def new_context(**options)
20
+ def new_context(**options, &block)
20
21
  params = options.dup
21
- # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
22
- if params[:viewport] == 0
23
- params.delete(:viewport)
24
- params[:noDefaultViewport] = true
25
- end
26
- if params[:extraHTTPHeaders]
27
- # TODO
28
- end
29
- if params[:storageState].is_a?(String)
30
- params[:storageState] = JSON.parse(File.read(params[:storageState]))
31
- end
22
+ prepare_browser_context_options(params)
32
23
 
33
24
  resp = @channel.send_message_to_server('newContext', params.compact)
34
25
  context = ChannelOwners::BrowserContext.from(resp)
35
26
  @contexts << context
36
27
  context.browser = self
37
28
  context.options = params
38
- context
29
+
30
+ if block
31
+ begin
32
+ block.call(context)
33
+ ensure
34
+ context.close
35
+ end
36
+ else
37
+ context
38
+ end
39
39
  end
40
40
 
41
41
  def new_page(**options)
@@ -8,7 +8,10 @@ module Playwright
8
8
  @pages = Set.new
9
9
 
10
10
  @channel.once('close', ->(_) { on_close })
11
- @channel.on('page', ->(hash) { on_page(ChannelOwners::Page.from(hash['page']) )})
11
+ @channel.on('page', ->(params) { on_page(ChannelOwners::Page.from(params['page']) )})
12
+ @channel.on('route', ->(params) {
13
+ on_route(ChannelOwners::Route.from(params['route']), ChannelOwners::Request.from(params['request']))
14
+ })
12
15
  end
13
16
 
14
17
  private def on_page(page)
@@ -17,6 +20,11 @@ module Playwright
17
20
  emit(Events::BrowserContext::Page, page)
18
21
  end
19
22
 
23
+ private def on_route(route, request)
24
+ # @routes.each ...
25
+ route.continue_
26
+ end
27
+
20
28
  def pages
21
29
  @pages.to_a
22
30
  end
@@ -0,0 +1,27 @@
1
+ module Playwright
2
+ define_channel_owner :Download do
3
+ def url
4
+ @initializer['url']
5
+ end
6
+
7
+ def suggested_filename
8
+ @initializer['suggestedFilename']
9
+ end
10
+
11
+ def delete
12
+ @channel.send_message_to_server('delete')
13
+ end
14
+
15
+ def failure
16
+ @channel.send_message_to_server('failure')
17
+ end
18
+
19
+ def path
20
+ @channel.send_message_to_server('path')
21
+ end
22
+
23
+ def save_as(path)
24
+ @channel.send_message_to_server('saveAs', path: path)
25
+ end
26
+ end
27
+ end
@@ -3,6 +3,312 @@ require_relative './js_handle'
3
3
  module Playwright
4
4
  module ChannelOwners
5
5
  class ElementHandle < JSHandle
6
+ def as_element
7
+ self
8
+ end
9
+
10
+ def owner_frame
11
+ resp = @channel.send_message_to_server('ownerFrame')
12
+ ChannelOwners::Frame.from_nullable(resp)
13
+ end
14
+
15
+ def content_frame
16
+ resp = @channel.send_message_to_server('contentFrame')
17
+ ChannelOwners::Frame.from_nullable(resp)
18
+ end
19
+
20
+ def get_attribute(name)
21
+ @channel.send_message_to_server('getAttribute', name: name)
22
+ end
23
+
24
+ def text_content
25
+ @channel.send_message_to_server('textContent')
26
+ end
27
+
28
+ def inner_text
29
+ @channel.send_message_to_server('innerText')
30
+ end
31
+
32
+ def inner_html
33
+ @channel.send_message_to_server('innerHTML')
34
+ end
35
+
36
+ def checked?
37
+ @channel.send_message_to_server('isChecked')
38
+ end
39
+
40
+ def disabled?
41
+ @channel.send_message_to_server('isDisabled')
42
+ end
43
+
44
+ def editable?
45
+ @channel.send_message_to_server('isEditable')
46
+ end
47
+
48
+ def enabled?
49
+ @channel.send_message_to_server('isEnabled')
50
+ end
51
+
52
+ def hidden?
53
+ @channel.send_message_to_server('isHidden')
54
+ end
55
+
56
+ def visible?
57
+ @channel.send_message_to_server('isVisible')
58
+ end
59
+
60
+ def dispatch_event(type, eventInit: nil)
61
+ params = {
62
+ type: type,
63
+ eventInit: JavaScript::ValueSerializer.new(eventInit).serialize,
64
+ }.compact
65
+ @channel.send_message_to_server('dispatchEvent', params)
66
+
67
+ nil
68
+ end
69
+
70
+ def scroll_into_view_if_needed(timeout: nil)
71
+ params = {
72
+ timeout: timeout,
73
+ }.compact
74
+ @channel.send_message_to_server('scrollIntoViewIfNeeded', params)
75
+
76
+ nil
77
+ end
78
+
79
+ def hover(force: nil, modifiers: nil, position: nil, timeout: nil)
80
+ params = {
81
+ force: force,
82
+ modifiers: modifiers,
83
+ position: position,
84
+ timeout: timeout,
85
+ }
86
+ @channel.send_message_to_server('hover', params)
87
+
88
+ nil
89
+ end
90
+
91
+ def click(
92
+ button: nil,
93
+ clickCount: nil,
94
+ delay: nil,
95
+ force: nil,
96
+ modifiers: nil,
97
+ noWaitAfter: nil,
98
+ position: nil,
99
+ timeout: nil)
100
+
101
+ params = {
102
+ button: button,
103
+ clickCount: clickCount,
104
+ delay: delay,
105
+ force: force,
106
+ modifiers: modifiers,
107
+ noWaitAfter: noWaitAfter,
108
+ position: position,
109
+ timeout: timeout,
110
+ }.compact
111
+ @channel.send_message_to_server('click', params)
112
+
113
+ nil
114
+ end
115
+
116
+ def dblclick(
117
+ button: nil,
118
+ delay: nil,
119
+ force: nil,
120
+ modifiers: nil,
121
+ noWaitAfter: nil,
122
+ position: nil,
123
+ timeout: nil)
124
+
125
+ params = {
126
+ button: button,
127
+ delay: delay,
128
+ force: force,
129
+ modifiers: modifiers,
130
+ noWaitAfter: noWaitAfter,
131
+ position: position,
132
+ timeout: timeout,
133
+ }.compact
134
+ @channel.send_message_to_server('dblclick', params)
135
+
136
+ nil
137
+ end
138
+
139
+ def select_option(values, noWaitAfter: nil, timeout: nil)
140
+ base_params = SelectOptionValues.new(values).as_params
141
+ params = base_params + { noWaitAfter: noWaitAfter, timeout: timeout }.compact
142
+ @channel.send_message_to_server('selectOption', params)
143
+
144
+ nil
145
+ end
146
+
147
+ def tap_point(
148
+ force: nil,
149
+ modifiers: nil,
150
+ noWaitAfter: nil,
151
+ position: nil,
152
+ timeout: nil)
153
+
154
+ params = {
155
+ force: force,
156
+ modifiers: modifiers,
157
+ noWaitAfter: noWaitAfter,
158
+ position: position,
159
+ timeout: timeout,
160
+ }.compact
161
+ @channel.send_message_to_server('tap', params)
162
+
163
+ nil
164
+ end
165
+
166
+ def fill(value, noWaitAfter: nil, timeout: nil)
167
+ params = {
168
+ value: value,
169
+ noWaitAfter: noWaitAfter,
170
+ timeout: timeout,
171
+ }
172
+ @channel.send_message_to_server('fill', params)
173
+
174
+ nil
175
+ end
176
+
177
+ def select_text(timeout: nil)
178
+ params = { timeout: timeout }.compact
179
+ @channel.send_message_to_server('selectText', params)
180
+
181
+ nil
182
+ end
183
+
184
+ def set_input_files(files, noWaitAfter: nil, timeout: nil)
185
+ file_payloads = InputFiles.new(files).as_params
186
+ params = { files: file_payloads, noWaitAfter: noWaitAfter, timeout: timeout }.compact
187
+ @channel.send_message_to_server('setInputFiles', params)
188
+
189
+ nil
190
+ end
191
+
192
+ def focus
193
+ @channel.send_message_to_server('focus')
194
+
195
+ nil
196
+ end
197
+
198
+ def type(text, delay: nil, noWaitAfter: nil, timeout: nil)
199
+ params = {
200
+ text: text,
201
+ delay: delay,
202
+ noWaitAfter: noWaitAfter,
203
+ timeout: timeout,
204
+ }.compact
205
+ @channel.send_message_to_server('type', params)
206
+
207
+ nil
208
+ end
209
+
210
+ def press(key, delay: nil, noWaitAfter: nil, timeout: nil)
211
+ params = {
212
+ key: key,
213
+ delay: delay,
214
+ noWaitAfter: noWaitAfter,
215
+ timeout: timeout,
216
+ }.compact
217
+ @channel.send_message_to_server('press', params)
218
+
219
+ nil
220
+ end
221
+
222
+ def check(force: nil, noWaitAfter: nil, timeout: nil)
223
+ params = {
224
+ force: force,
225
+ noWaitAfter: noWaitAfter,
226
+ timeout: timeout,
227
+ }.compact
228
+ @channel.send_message_to_server('check', params)
229
+
230
+ nil
231
+ end
232
+
233
+ def uncheck(force: nil, noWaitAfter: nil, timeout: nil)
234
+ params = {
235
+ force: force,
236
+ noWaitAfter: noWaitAfter,
237
+ timeout: timeout,
238
+ }.compact
239
+ @channel.send_message_to_server('uncheck', params)
240
+
241
+ nil
242
+ end
243
+
244
+ def bounding_box
245
+ @channel.send_message_to_server('boundingBox')
246
+ end
247
+
248
+ def screenshot(
249
+ omitBackground: nil,
250
+ path: nil,
251
+ quality: nil,
252
+ timeout: nil,
253
+ type: nil)
254
+
255
+ params = {
256
+ omitBackground: omitBackground,
257
+ path: path,
258
+ quality: quality,
259
+ timeout: timeout,
260
+ type: type,
261
+ }.compact
262
+ encoded_binary = @channel.send_message_to_server('screenshot', params)
263
+ decoded_binary = Base64.strict_decode64(encoded_binary)
264
+ if path
265
+ File.open(path, 'wb') do |f|
266
+ f.write(decoded_binary)
267
+ end
268
+ end
269
+ decoded_binary
270
+ end
271
+
272
+ def query_selector(selector)
273
+ resp = @channel.send_message_to_server('querySelector', selector: selector)
274
+ ChannelOwners::ElementHandle.from_nullable(resp)
275
+ end
276
+
277
+ def query_selector_all(selector)
278
+ @channel.send_message_to_server('querySelectorAll', selector: selector).map do |el|
279
+ ChannelOwners::ElementHandle.from(el)
280
+ end
281
+ end
282
+
283
+ def eval_on_selector(selector, pageFunction, arg: nil)
284
+ if JavaScript.function?(pageFunction)
285
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector(@channel, selector)
286
+ else
287
+ JavaScript::Expression.new(pageFunction).eval_on_selector(@channel, selector)
288
+ end
289
+ end
290
+
291
+ def eval_on_selector_all(selector, pageFunction, arg: nil)
292
+ if JavaScript.function?(pageFunction)
293
+ JavaScript::Function.new(pageFunction, arg).eval_on_selector_all(@channel, selector)
294
+ else
295
+ JavaScript::Expression.new(pageFunction).eval_on_selector_all(@channel, selector)
296
+ end
297
+ end
298
+
299
+ def wait_for_element_state(state, timeout: nil)
300
+ params = { state: state, timeout: timeout }.compact
301
+ @channel.send_message_to_server('waitForElementState', params)
302
+
303
+ nil
304
+ end
305
+
306
+ def wait_for_selector(selector, state: nil, timeout: nil)
307
+ params = { selector: selector, state: state, timeout: timeout }.compact
308
+ resp = @channel.send_message_to_server('waitForSelector', params)
309
+
310
+ ChannelOwners::ElementHandle.from_nullable(resp)
311
+ end
6
312
  end
7
313
  end
8
314
  end