playwright-ruby-client 0.0.3 → 0.0.8

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 +119 -12
  3. data/docs/api_coverage.md +354 -0
  4. data/lib/playwright.rb +8 -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 +22 -29
  9. data/lib/playwright/channel_owners/browser_context.rb +43 -0
  10. data/lib/playwright/channel_owners/console_message.rb +21 -0
  11. data/lib/playwright/channel_owners/element_handle.rb +314 -0
  12. data/lib/playwright/channel_owners/frame.rb +466 -7
  13. data/lib/playwright/channel_owners/js_handle.rb +55 -0
  14. data/lib/playwright/channel_owners/page.rb +353 -5
  15. data/lib/playwright/channel_owners/request.rb +90 -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 +13 -0
  20. data/lib/playwright/input_files.rb +42 -0
  21. data/lib/playwright/input_type.rb +19 -0
  22. data/lib/playwright/input_types/android_input.rb +19 -0
  23. data/lib/playwright/input_types/keyboard.rb +32 -0
  24. data/lib/playwright/input_types/mouse.rb +4 -0
  25. data/lib/playwright/input_types/touchscreen.rb +4 -0
  26. data/lib/playwright/javascript.rb +13 -0
  27. data/lib/playwright/javascript/expression.rb +67 -0
  28. data/lib/playwright/javascript/function.rb +67 -0
  29. data/lib/playwright/javascript/value_parser.rb +75 -0
  30. data/lib/playwright/javascript/value_serializer.rb +54 -0
  31. data/lib/playwright/playwright_api.rb +45 -25
  32. data/lib/playwright/select_option_values.rb +32 -0
  33. data/lib/playwright/timeout_settings.rb +19 -0
  34. data/lib/playwright/url_matcher.rb +19 -0
  35. data/lib/playwright/utils.rb +37 -0
  36. data/lib/playwright/version.rb +1 -1
  37. data/lib/playwright/wait_helper.rb +73 -0
  38. data/lib/playwright_api/accessibility.rb +60 -6
  39. data/lib/playwright_api/android.rb +33 -0
  40. data/lib/playwright_api/android_device.rb +78 -0
  41. data/lib/playwright_api/android_input.rb +25 -0
  42. data/lib/playwright_api/binding_call.rb +18 -0
  43. data/lib/playwright_api/browser.rb +136 -44
  44. data/lib/playwright_api/browser_context.rb +378 -51
  45. data/lib/playwright_api/browser_type.rb +137 -55
  46. data/lib/playwright_api/cdp_session.rb +32 -7
  47. data/lib/playwright_api/chromium_browser_context.rb +31 -0
  48. data/lib/playwright_api/console_message.rb +27 -7
  49. data/lib/playwright_api/dialog.rb +47 -3
  50. data/lib/playwright_api/download.rb +29 -5
  51. data/lib/playwright_api/element_handle.rb +429 -143
  52. data/lib/playwright_api/file_chooser.rb +13 -2
  53. data/lib/playwright_api/frame.rb +633 -179
  54. data/lib/playwright_api/js_handle.rb +97 -17
  55. data/lib/playwright_api/keyboard.rb +152 -24
  56. data/lib/playwright_api/mouse.rb +28 -3
  57. data/lib/playwright_api/page.rb +1183 -317
  58. data/lib/playwright_api/playwright.rb +174 -13
  59. data/lib/playwright_api/request.rb +115 -30
  60. data/lib/playwright_api/response.rb +22 -3
  61. data/lib/playwright_api/route.rb +63 -4
  62. data/lib/playwright_api/selectors.rb +29 -7
  63. data/lib/playwright_api/touchscreen.rb +2 -1
  64. data/lib/playwright_api/video.rb +11 -1
  65. data/lib/playwright_api/web_socket.rb +5 -5
  66. data/lib/playwright_api/worker.rb +29 -5
  67. data/playwright.gemspec +3 -0
  68. metadata +68 -2
data/lib/playwright.rb CHANGED
@@ -10,12 +10,20 @@ 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/input_files'
19
+ require 'playwright/input_type'
16
20
  require 'playwright/connection'
21
+ require 'playwright/select_option_values'
22
+ require 'playwright/timeout_settings'
17
23
  require 'playwright/transport'
24
+ require 'playwright/url_matcher'
18
25
  require 'playwright/version'
26
+ require 'playwright/wait_helper'
19
27
 
20
28
  require 'playwright/playwright_api'
21
29
  # 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,30 +17,29 @@ 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
- def new_page(*args)
40
- context = new_context(*args)
41
+ def new_page(**options)
42
+ context = new_context(**options)
41
43
  page = context.new_page
42
44
  page.owned_context = context
43
45
  context.owner_page = page
@@ -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,56 @@
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', ->(hash) { on_page(ChannelOwners::Page.from(hash['page']) )})
12
+ end
13
+
14
+ private def on_page(page)
15
+ page.send(:update_browser_context, self)
16
+ @pages << page
17
+ emit(Events::BrowserContext::Page, page)
18
+ end
19
+
20
+ def pages
21
+ @pages.to_a
22
+ end
23
+
6
24
  # @returns [Playwright::Page]
7
25
  def new_page
8
26
  raise 'Please use browser.new_context' if @owner_page
9
27
  resp = @channel.send_message_to_server('newPage')
10
28
  ChannelOwners::Page.from(resp)
11
29
  end
30
+
31
+ private def on_close
32
+ @closed_or_closing = true
33
+ @browser&.send(:remove_context, self)
34
+ emit(Events::BrowserContext::Close)
35
+ end
36
+
37
+ def close
38
+ return if @closed_or_closing
39
+ @closed_or_closing = true
40
+ @channel.send_message_to_server('close')
41
+ nil
42
+ rescue => err
43
+ raise unless safe_close_error?(err)
44
+ end
45
+
46
+ # called from Page#on_close with send(:remove_page, page), so keep private
47
+ private def remove_page(page)
48
+ @pages.delete(page)
49
+ end
50
+
51
+ # called from Page with send(:_timeout_settings), so keep private.
52
+ private def _timeout_settings
53
+ @timeout_settings
54
+ end
12
55
  end
13
56
  end
@@ -0,0 +1,21 @@
1
+ module Playwright
2
+ define_channel_owner :ConsoleMessage do
3
+ def type
4
+ @initializer['type']
5
+ end
6
+
7
+ def text
8
+ @initializer['text']
9
+ end
10
+
11
+ def args
12
+ @initializer['args']&.map do |arg|
13
+ ChannelOwner.from(arg)
14
+ end
15
+ end
16
+
17
+ def location
18
+ @initialize['location']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,314 @@
1
+ require_relative './js_handle'
2
+
3
+ module Playwright
4
+ module ChannelOwners
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
312
+ end
313
+ end
314
+ end