playwright-ruby-client 0.0.4 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +120 -6
  3. data/docs/api_coverage.md +351 -0
  4. data/lib/playwright.rb +9 -0
  5. data/lib/playwright/channel_owner.rb +16 -2
  6. data/lib/playwright/channel_owners/android.rb +10 -1
  7. data/lib/playwright/channel_owners/android_device.rb +163 -0
  8. data/lib/playwright/channel_owners/browser.rb +20 -27
  9. data/lib/playwright/channel_owners/browser_context.rb +51 -0
  10. data/lib/playwright/channel_owners/console_message.rb +0 -4
  11. data/lib/playwright/channel_owners/element_handle.rb +306 -0
  12. data/lib/playwright/channel_owners/frame.rb +473 -7
  13. data/lib/playwright/channel_owners/js_handle.rb +51 -0
  14. data/lib/playwright/channel_owners/page.rb +589 -4
  15. data/lib/playwright/channel_owners/request.rb +98 -0
  16. data/lib/playwright/channel_owners/webkit_browser.rb +1 -1
  17. data/lib/playwright/connection.rb +15 -14
  18. data/lib/playwright/errors.rb +1 -1
  19. data/lib/playwright/event_emitter.rb +17 -1
  20. data/lib/playwright/http_headers.rb +20 -0
  21. data/lib/playwright/input_files.rb +42 -0
  22. data/lib/playwright/input_type.rb +19 -0
  23. data/lib/playwright/input_types/android_input.rb +19 -0
  24. data/lib/playwright/input_types/keyboard.rb +32 -0
  25. data/lib/playwright/input_types/mouse.rb +4 -0
  26. data/lib/playwright/input_types/touchscreen.rb +4 -0
  27. data/lib/playwright/javascript.rb +13 -0
  28. data/lib/playwright/javascript/expression.rb +67 -0
  29. data/lib/playwright/javascript/function.rb +67 -0
  30. data/lib/playwright/javascript/value_parser.rb +75 -0
  31. data/lib/playwright/javascript/value_serializer.rb +54 -0
  32. data/lib/playwright/playwright_api.rb +45 -25
  33. data/lib/playwright/select_option_values.rb +32 -0
  34. data/lib/playwright/timeout_settings.rb +19 -0
  35. data/lib/playwright/url_matcher.rb +19 -0
  36. data/lib/playwright/utils.rb +37 -0
  37. data/lib/playwright/version.rb +1 -1
  38. data/lib/playwright/wait_helper.rb +73 -0
  39. data/lib/playwright_api/accessibility.rb +46 -6
  40. data/lib/playwright_api/android.rb +33 -0
  41. data/lib/playwright_api/android_device.rb +78 -0
  42. data/lib/playwright_api/android_input.rb +25 -0
  43. data/lib/playwright_api/binding_call.rb +18 -0
  44. data/lib/playwright_api/browser.rb +93 -12
  45. data/lib/playwright_api/browser_context.rb +279 -28
  46. data/lib/playwright_api/browser_type.rb +68 -5
  47. data/lib/playwright_api/cdp_session.rb +23 -1
  48. data/lib/playwright_api/chromium_browser_context.rb +26 -0
  49. data/lib/playwright_api/console_message.rb +20 -7
  50. data/lib/playwright_api/dialog.rb +48 -2
  51. data/lib/playwright_api/download.rb +19 -4
  52. data/lib/playwright_api/element_handle.rb +278 -104
  53. data/lib/playwright_api/file_chooser.rb +20 -3
  54. data/lib/playwright_api/frame.rb +452 -147
  55. data/lib/playwright_api/js_handle.rb +78 -19
  56. data/lib/playwright_api/keyboard.rb +99 -9
  57. data/lib/playwright_api/mouse.rb +22 -0
  58. data/lib/playwright_api/page.rb +864 -222
  59. data/lib/playwright_api/playwright.rb +116 -14
  60. data/lib/playwright_api/request.rb +86 -24
  61. data/lib/playwright_api/response.rb +18 -7
  62. data/lib/playwright_api/route.rb +49 -0
  63. data/lib/playwright_api/selectors.rb +28 -2
  64. data/lib/playwright_api/video.rb +8 -0
  65. data/lib/playwright_api/web_socket.rb +0 -8
  66. data/lib/playwright_api/worker.rb +25 -13
  67. data/playwright.gemspec +3 -0
  68. metadata +66 -2
data/lib/playwright.rb CHANGED
@@ -10,12 +10,21 @@ require 'concurrent'
10
10
  require 'playwright/errors'
11
11
  require 'playwright/events'
12
12
  require 'playwright/event_emitter'
13
+ require 'playwright/javascript'
14
+ require 'playwright/utils'
13
15
 
14
16
  require 'playwright/channel'
15
17
  require 'playwright/channel_owner'
18
+ require 'playwright/http_headers'
19
+ require 'playwright/input_files'
20
+ require 'playwright/input_type'
16
21
  require 'playwright/connection'
22
+ require 'playwright/select_option_values'
23
+ require 'playwright/timeout_settings'
17
24
  require 'playwright/transport'
25
+ require 'playwright/url_matcher'
18
26
  require 'playwright/version'
27
+ require 'playwright/wait_helper'
19
28
 
20
29
  require 'playwright/playwright_api'
21
30
  # load generated files
@@ -6,6 +6,10 @@ module Playwright
6
6
  channel.object
7
7
  end
8
8
 
9
+ def self.from_nullable(channel)
10
+ channel&.object
11
+ end
12
+
9
13
  # @param parent [Playwright::ChannelOwner|Playwright::Connection]
10
14
  # @param type [String]
11
15
  # @param guid [String]
@@ -35,16 +39,26 @@ module Playwright
35
39
 
36
40
  attr_reader :channel
37
41
 
38
- def dispose
42
+ # used only from Connection. Not intended for public use. So keep private.
43
+ private def dispose!
39
44
  # Clean up from parent and connection.
40
45
  @parent&.send(:delete_object_from_child, @guid)
41
46
  @connection.send(:delete_object_from_channel_owner, @guid)
42
47
 
43
48
  # Dispose all children.
44
- @objects.each_value(&:dispose)
49
+ @objects.each_value { |object| object.send(:dispose!) }
45
50
  @objects.clear
46
51
  end
47
52
 
53
+ # Suppress long long inspect log and avoid RSpec from hanging up...
54
+ def inspect
55
+ to_s
56
+ end
57
+
58
+ def to_s
59
+ "#<#{@guid}>"
60
+ end
61
+
48
62
  private
49
63
 
50
64
  def after_initialize
@@ -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 = InputTypes::AndroidInput.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
@@ -1,9 +1,12 @@
1
1
  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
+ include Utils::Errors::SafeCloseError
5
+ include Utils::PrepareBrowserContextOptions
6
+
4
7
  def after_initialize
5
8
  @contexts = Set.new
6
- @channel.on('close', method(:handle_close))
9
+ @channel.on('close', method(:on_close))
7
10
  end
8
11
 
9
12
  def contexts
@@ -14,26 +17,25 @@ module Playwright
14
17
  @connected
15
18
  end
16
19
 
17
- def new_context(**options)
20
+ def new_context(**options, &block)
18
21
  params = options.dup
19
- # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
20
- if params[:viewport] == 0
21
- params.delete(:viewport)
22
- params[:noDefaultViewport] = true
23
- end
24
- if params[:extraHTTPHeaders]
25
- # TODO
26
- end
27
- if params[:storageState].is_a?(String)
28
- params[:storageState] = JSON.parse(File.read(params[:storageState]))
29
- end
22
+ prepare_browser_context_options(params)
30
23
 
31
24
  resp = @channel.send_message_to_server('newContext', params.compact)
32
25
  context = ChannelOwners::BrowserContext.from(resp)
33
26
  @contexts << context
34
27
  context.browser = self
35
28
  context.options = params
36
- context
29
+
30
+ if block
31
+ begin
32
+ block.call(context)
33
+ ensure
34
+ context.close
35
+ end
36
+ else
37
+ context
38
+ end
37
39
  end
38
40
 
39
41
  def new_page(**options)
@@ -57,24 +59,15 @@ module Playwright
57
59
  @initializer['version']
58
60
  end
59
61
 
60
- private
61
-
62
- def handle_close(_ = {})
62
+ private def on_close(_ = {})
63
63
  @connected = false
64
64
  emit(Events::Browser::Disconnected)
65
65
  @closed_or_closing = false
66
66
  end
67
67
 
68
- # @param err [Exception]
69
- def safe_close_error?(err)
70
- return true if err.is_a?(Transport::AlreadyDisconnectedError)
71
-
72
- [
73
- 'Browser has been closed',
74
- 'Target page, context or browser has been closed',
75
- ].any? do |closed_message|
76
- err.message.end_with?(closed_message)
77
- end
68
+ # called from BrowserContext#on_close with send(:remove_context), so keep private.
69
+ private def remove_context(context)
70
+ @contexts.delete(context)
78
71
  end
79
72
  end
80
73
  end
@@ -1,13 +1,64 @@
1
1
  module Playwright
2
2
  # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser_context.py
3
3
  define_channel_owner :BrowserContext do
4
+ include Utils::Errors::SafeCloseError
4
5
  attr_writer :browser, :owner_page, :options
5
6
 
7
+ def after_initialize
8
+ @pages = Set.new
9
+
10
+ @channel.once('close', ->(_) { on_close })
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
+ })
15
+ end
16
+
17
+ private def on_page(page)
18
+ page.send(:update_browser_context, self)
19
+ @pages << page
20
+ emit(Events::BrowserContext::Page, page)
21
+ end
22
+
23
+ private def on_route(route, request)
24
+ # @routes.each ...
25
+ route.continue_
26
+ end
27
+
28
+ def pages
29
+ @pages.to_a
30
+ end
31
+
6
32
  # @returns [Playwright::Page]
7
33
  def new_page
8
34
  raise 'Please use browser.new_context' if @owner_page
9
35
  resp = @channel.send_message_to_server('newPage')
10
36
  ChannelOwners::Page.from(resp)
11
37
  end
38
+
39
+ private def on_close
40
+ @closed_or_closing = true
41
+ @browser&.send(:remove_context, self)
42
+ emit(Events::BrowserContext::Close)
43
+ end
44
+
45
+ def close
46
+ return if @closed_or_closing
47
+ @closed_or_closing = true
48
+ @channel.send_message_to_server('close')
49
+ nil
50
+ rescue => err
51
+ raise unless safe_close_error?(err)
52
+ end
53
+
54
+ # called from Page#on_close with send(:remove_page, page), so keep private
55
+ private def remove_page(page)
56
+ @pages.delete(page)
57
+ end
58
+
59
+ # called from Page with send(:_timeout_settings), so keep private.
60
+ private def _timeout_settings
61
+ @timeout_settings
62
+ end
12
63
  end
13
64
  end
@@ -1,9 +1,5 @@
1
1
  module Playwright
2
2
  define_channel_owner :ConsoleMessage do
3
- def to_s
4
- @initializer['text']
5
- end
6
-
7
3
  def type
8
4
  @initializer['type']
9
5
  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
+ base_params = InputFiles.new(values).as_params
186
+ params = base_params + { 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